I see a lot of answers, about removing the backstack.
But How do I remove the forward stack?
Aka, Navigating A, to B, to C
A -> B -> C
I then navigate back from C, to B (Form saved, C closed NavigationService.GoBack();)
B <- C
I should now, NOT be able to navigate back to C using the forward button. But have no idea how to implement this. It makes most sense to remove it from the stack somehow.
I know this question was posted long ago, but recently I stumbled upon similar issue and this is how I solved it for my case. I hope it'll help someone.
Requirement
User should be able to go back to pages visited earlier but should not be able to go forward once back button is pressed.
My Approach
Create a custom control derived from Frame and add two addition DPs AllowForwardNavigation and AllowBackNavigation so that I can control if I want to allow prev/next navigation.
MyFrame.xaml.cs
public partial class MyFrame : Frame
{
#region Dependency Properties
public bool AllowForwardNavigation
{
get { return (bool)GetValue(AllowForwardNavigationProperty); }
set { SetValue(AllowForwardNavigationProperty, value); }
}
public static readonly DependencyProperty AllowForwardNavigationProperty =
DependencyProperty.Register(nameof(AllowForwardNavigation), typeof(bool), typeof(MyFrame), new PropertyMetadata(true));
public bool AllowBackNavigation
{
get { return (bool)GetValue(AllowBackNavigationProperty); }
set { SetValue(AllowBackNavigationProperty, value); }
}
public static readonly DependencyProperty AllowBackNavigationProperty =
DependencyProperty.Register(nameof(AllowBackNavigation), typeof(bool), typeof(MyFrame), new PropertyMetadata(true));
#endregion
public MyFrame()
{
InitializeComponent();
JournalOwnership = JournalOwnership.OwnsJournal;
// find existing bindings
var existingBindings = CommandBindings.OfType<CommandBinding>()
.Where(x => x.Command == NavigationCommands.BrowseForward
|| x.Command == NavigationCommands.BrowseBack)
.ToArray();
// remove existing bindings
foreach (var binding in existingBindings)
CommandBindings.Remove(binding);
// add new binding
CommandBindings.Add(new CommandBinding(NavigationCommands.BrowseForward, OnGoForward, OnQueryGoForward));
CommandBindings.Add(new CommandBinding(NavigationCommands.BrowseBack, OnGoBack, OnQueryGoBack));
// override default navigation behavior
NavigationService.Navigating += NavigationService_Navigating;
}
private void NavigationService_Navigating(Object sender, NavigatingCancelEventArgs e)
{
switch (e.NavigationMode)
{
case NavigationMode.Forward:
e.Cancel = !AllowForwardNavigation;
break;
case NavigationMode.Back:
e.Cancel = !AllowBackNavigation;
break;
}
}
#region Command methods
private void OnGoForward(Object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
if (AllowForwardNavigation && NavigationService.CanGoForward)
NavigationService.GoForward();
}
private void OnQueryGoForward(Object sender, CanExecuteRoutedEventArgs e)
{
e.Handled = true;
e.CanExecute = AllowForwardNavigation && NavigationService.CanGoForward;
}
private void OnGoBack(Object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
if (AllowBackNavigation && NavigationService.CanGoBack)
NavigationService.GoBack();
}
private void OnQueryGoBack(Object sender, CanExecuteRoutedEventArgs e)
{
e.Handled = true;
e.CanExecute = AllowBackNavigation && NavigationService.CanGoBack;
}
#endregion
}
MyFrame.xaml
<Style x:Key="NavigationWindowBackButtonStyle" TargetType="{x:Type Button}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Command" Value="NavigationCommands.BrowseBack"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="Transparent" Height="24" Width="24">
<Ellipse x:Name="Circle" Fill="{StaticResource NavigationWindowNavigationButtonFillEnabled}" Stroke="{StaticResource NavigationWindowNavigationButtonStrokeEnabled}" StrokeThickness="1"/>
<Path x:Name="Arrow" Data="M0.37,7.69 L5.74,14.20 A1.5,1.5,0,1,0,10.26,12.27 L8.42,10.42 14.90,10.39 A1.5,1.5,0,1,0,14.92,5.87 L8.44,5.90 10.31,4.03 A1.5,1.5,0,1,0,5.79,1.77 z" Fill="{StaticResource NavigationWindowNavigationArrowFill}" HorizontalAlignment="Center" Stroke="{StaticResource NavigationWindowNavigationArrowStrokeEnabled}" StrokeThickness="0.75" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="Circle" Value="{StaticResource NavigationWindowNavigationButtonFillDisabled}"/>
<Setter Property="Stroke" TargetName="Circle" Value="#B5BACE"/>
<Setter Property="Stroke" TargetName="Arrow" Value="#B0B5BACE"/>
<Setter Property="Fill" TargetName="Arrow" Value="#D0FFFFFF"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="Circle" Value="{StaticResource NavigationWindowNavigationButtonFillHover}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="Circle" Value="{StaticResource NavigationWindowNavigationButtonFillPressed}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="NavigationWindowForwardButtonStyle" TargetType="{x:Type Button}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Command" Value="NavigationCommands.BrowseForward"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="Transparent" Height="24" Width="24">
<Ellipse x:Name="Circle" Grid.Column="0" Fill="{StaticResource NavigationWindowNavigationButtonFillEnabled}" Stroke="{StaticResource NavigationWindowNavigationButtonStrokeEnabled}" StrokeThickness="1"/>
<Path x:Name="Arrow" Grid.Column="0" Data="M0.37,7.69 L5.74,14.20 A1.5,1.5,0,1,0,10.26,12.27 L8.42,10.42 14.90,10.39 A1.5,1.5,0,1,0,14.92,5.87 L8.44,5.90 10.31,4.03 A1.5,1.5,0,1,0,5.79,1.77 z" Fill="{StaticResource NavigationWindowNavigationArrowFill}" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0" Stroke="{StaticResource NavigationWindowNavigationArrowStrokeEnabled}" StrokeThickness="0.75" VerticalAlignment="Center">
<Path.RenderTransform>
<ScaleTransform ScaleX="-1"/>
</Path.RenderTransform>
</Path>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="Circle" Value="{StaticResource NavigationWindowNavigationButtonFillDisabled}"/>
<Setter Property="Stroke" TargetName="Circle" Value="#B5BACE"/>
<Setter Property="Stroke" TargetName="Arrow" Value="#B0B5BACE"/>
<Setter Property="Fill" TargetName="Arrow" Value="#D0FFFFFF"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="Circle" Value="{StaticResource NavigationWindowNavigationButtonFillHover}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="Circle" Value="{StaticResource NavigationWindowNavigationButtonFillPressed}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Frame.Resources>
<Frame.Template>
<ControlTemplate TargetType="Frame">
<DockPanel Margin="7">
<StackPanel Visibility="{TemplateBinding NavigationUIVisibility}" Margin="0" Orientation="Horizontal"
DockPanel.Dock="Top">
<Button Style="{StaticResource NavigationWindowBackButtonStyle}"
Command="NavigationCommands.BrowseBack"
Content="M 4 0 L 0 4 L 4 8 Z"
Margin="2.7,0,1.3,0" />
<Button Style="{StaticResource NavigationWindowForwardButtonStyle}"
Command="NavigationCommands.BrowseForward"
Content="M 4 0 L 0 4 L 4 8 Z" Margin="1.3,0,0,0" />
</StackPanel>
<Border>
<ContentPresenter />
</Border>
</DockPanel>
</ControlTemplate>
</Frame.Template>
</Frame>
Usage
<local:MyFrame x:Name="_mainFrame" NavigationUIVisibility="Visible"
AllowForwardNavigation="False" />
Related
I am trying to change the color of a checkmark of a checkbox in a WPF List View. I got the following XAML Code from another post. Unfortuantely I am not familar enough with the whole framework to understand where exacatly I need to add that in my ListView. Is anyone able to give me some hint. Many thanks in adavance.
<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}" TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Grid>
<Path x:Name="Equis" Opacity="0" Stroke="Red" Fill="Red" Stretch="UniformToFill" StrokeThickness="20" Data="M 30,100 L 80,140 L 160,60" Margin="0,0,2,2" />
<ContentPresenter Margin="4" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="Equis" Property="Opacity" Value="1" />
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="Equis" Property="Opacity" Value="0" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here the XAML of the checkbox in the GridViewColumn (works fine)
<GridViewColumn Header="HeaderName" Width="35">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="CheckBoxName" IsChecked="{Binding Object}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
You only need the ControlTemplate part of the Style that you show. You need to give it a key with x:Key="MyCheckBoxTemplate". Then you can apply it to the CheckBox in your own CellTemplate with Template="{StaticResource MyCheckBoxTemplate}".
A full worked example is below. In this I changed the sample ControlTemplate in your question because it draws a rather nasty big red splodge when the CheckBox is selected. I made it draw a much smaller checkmark in red:
XAML:
<Window x:Class="WpfApp11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp11"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ControlTemplate TargetType="CheckBox" x:Key="MyCheckBoxTemplate">
<StackPanel Orientation="Horizontal">
<Path x:Name="Equis"
Stroke="Red"
StrokeThickness="3"
Height="17" Width="19" Stretch="None"
Margin="0,0,2,2"
Opacity="1"
>
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="3,10">
<LineSegment Point="8,14" />
<LineSegment Point="16,6" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<ContentPresenter Margin="4" HorizontalAlignment="Left" VerticalAlignment="Top" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="Equis" Property="Opacity" Value="1" />
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="Equis" Property="Opacity" Value="0" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<ListView Margin="5" ItemsSource="{Binding Products}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Checked" DisplayMemberBinding="{Binding IsChecked}" />
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding ShortName}" />
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" />
<GridViewColumn Header="HeaderName" Width="35">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="CheckBoxName" Template="{StaticResource MyCheckBoxTemplate}"
IsChecked="{Binding IsChecked}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Window>
C#:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace WpfApp11
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
CreateProducts();
DataContext = this;
}
public ObservableCollection<Product> Products { get; set; }
private void CreateProducts()
{
Products = new ObservableCollection<Product>
{
new Product{ShortName="My First Product", ID = 1},
new Product{ShortName="My Second Product", ID = 2, IsChecked= true},
new Product{ShortName="My Third Product", ID = 3},
new Product{ShortName="My Fourth Product", ID = 4, IsChecked = true},
new Product{ShortName="My Fifth Product", ID = 5}
};
}
}
public class Product : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string shortName;
public string ShortName
{
get { return shortName; }
set
{
shortName = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(ShortName)));
}
}
private int id;
public int ID
{
get { return id; }
set
{
id = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(ID)));
}
}
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(IsChecked)));
}
}
}
}
You need to re-template the entire control. Take a look at this example:
<CheckBox x:Name="CheckBoxName" IsChecked="{Binding Object}" HorizontalAlignment="Center">
<CheckBox.Resources>
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="OptionMark.Static.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="OptionMark.Static.Border" Color="#FF707070"/>
<Style x:Key="OptionMarkFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="14,0,0,0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="OptionMark.MouseOver.Background" Color="#FFF3F9FF"/>
<SolidColorBrush x:Key="OptionMark.MouseOver.Border" Color="#FF5593FF"/>
<SolidColorBrush x:Key="OptionMark.MouseOver.Glyph" Color="Red"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Background" Color="#FFE6E6E6"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Border" Color="#FFBCBCBC"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Glyph" Color="#FF707070"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Background" Color="#FFD9ECFF"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Border" Color="#FF3C77DD"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Glyph" Color="Red"/>
<SolidColorBrush x:Key="OptionMark.Static.Glyph" Color="Red"/>
</CheckBox.Resources>
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource OptionMark.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="checkBoxBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid x:Name="markGrid">
<Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None"/>
<Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph}" Margin="2" Opacity="0"/>
</Grid>
</Border>
<ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="true">
<Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/>
<Setter Property="Padding" Value="4,-1,0,0"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Opacity" TargetName="optionMark" Value="1"/>
<Setter Property="Opacity" TargetName="indeterminateMark" Value="0"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter Property="Opacity" TargetName="optionMark" Value="0"/>
<Setter Property="Opacity" TargetName="indeterminateMark" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</CheckBox.Style>
</CheckBox>
The colour of the checkmark is defined by the OptionMark.MouseOver.Glyph, OptionMark.Pressed.Glyph and OptionMark.Static.Glyph resources. You can the Color of these to whatever you want.
I have the email text block with validation
<TextBox x:Name="email" Style="{StaticResource emaliStyle}" Grid.Column="2" Grid.Row="2">
<TextBox.Text>
<Binding Mode="TwoWay" Path="Email" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<DataErrorValidationRule ValidatesOnTargetUpdated="False"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Style TargetType="TextBox" x:Key="emaliStyle">
<Setter Property="Width" Value="220"/>
<Setter Property="Height" Value="30"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Padding" Value="6,1,1,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="10"
SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter x:Name="LoginValidation" Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Bottom" Foreground="Maroon" FontSize="8pt"
Text="{Binding ElementName=email, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
</TextBlock>
<AdornedElementPlaceholder Name="email" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="DarkRed" />
<Setter Property="BorderThickness" Value="2" />
</Trigger>
</Style.Triggers>
</Style>
the validation work and show me the message which I write in viewModel( it doesn't matter)
next step it blocks the button if the field is invalid.
my button and style for it
<Button x:Name="SignInButton"
Style="{StaticResource SignInButton}"
Command="{Binding SignInCommand}"
CommandParameter="{Binding ElementName=This}"
Content="Sign In"/>
<Style TargetType="Button" x:Key="SignInButton">
<Setter Property="Background" Value="MidnightBlue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" CornerRadius="10">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Height="20" Margin="26,10"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers >
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=email}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LimeGreen"/>
</Trigger>
</Style.Triggers>
</Style>
I write the Multitrigger(later add other textbox fields) to disable my button, but it isn't work.
I try write all with element name Email doesn't help. What i miss?
because the error message will show and the field border mark red, bu isn't disable
Your BlockButton() method should return false whenever you want to disable the Button that is bound to the SignInCommand. There is no reason to set the IsEnabled property in a style when you bind to a command.
For the status of the command and the Button to get refeshed, you should raise the CanExecuteChanged event for the command whenever the validation status changes. Most ICommand implementations include a RaiseCanExecuteChanged() method or similar that lets you do this:
private bool _isEmailValid;
private bool BlockButton()
{
return _isEmailValid && IsAppDeveloper == false && IsEndUser == false;
}
public string this[string columnName]
{
get
{
string error = String.Empty;
switch (columnName)
{
case "Email":
string s = ValidateModelProperty(Email, columnName);
_isEmailValid = string.IsNullOrEmpty(s);
SignInCommand.RaiseCanExecuteChanged();
return s;
}
return error;
}
}
private string ValidateModelProperty(object value, string propertyName)
{
ICollection<ValidationResult> validationResults = new List<ValidationResult>();
ValidationContext validationContext = new ValidationContext(this, null, null) { MemberName = propertyName };
if (!Validator.TryValidateProperty(value, validationContext, validationResults))
foreach (ValidationResult validationResult in validationResults)
return validationResult.ErrorMessage;
return null;
}
this is my button code
public class MetroButton : Button
{
public static readonly DependencyProperty MoseOverBrushProperty;
public static readonly DependencyProperty PressedBrushProperty;
public MetroButton():base()
{
var resource = new ResourceDictionary
{
Source = new Uri("/Parking.Component.Ui;component/Styles/ButtonMetro.xaml",
UriKind.RelativeOrAbsolute)
};
Style = resource["ButtonMetro"] as Style;
//SetResourceReference(StyleProperty, Style);
}
static MetroButton()
{
MoseOverBrushProperty = DependencyProperty.Register("MoseOverBrush", typeof(Brush), typeof(MetroButton));
PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(MetroButton));
}
public Brush MoseOverBrush
{
get { return (Brush)base.GetValue(MoseOverBrushProperty); }
set { base.SetValue(MoseOverBrushProperty, value); }
}
public Brush PressedBrush
{
get { return (Brush)base.GetValue(PressedBrushProperty); }
set { base.SetValue(PressedBrushProperty, value); }
}
}
and I use this style for my button
<Style x:Key="ButtonMetro" TargetType="{ x:Type LochalUI:MetroButton}">
<Setter Property="Foreground" Value="White" />
<Setter Property="MoseOverBrush" Value="#FF3F62FD"/>
<Setter Property="PressedBrush" Value="#FF000099"/>
<Setter Property="Background" Value="#FF6B9AFF"/>
<Setter Property="FontSize" Value="15" />
<Setter Property="FontFamily" Value="B Yekan" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type LochalUI:MetroButton}">
<Border x:Name="border" CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding Path=PressedBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
but the problem is there when i put color for my button background like below code:
<UI:MetroButton HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="132" Height="107" Background="#FF09CD00" >
<Grid>
<Label Content="تنظیمات" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5,5,5,12" Foreground="White" Margin="5"/>
</Grid>
</UI:MetroButton>
the IsMouseOver changer color and IsPressed Triggers not work.
(I don't want use static resource in my setters)
Changing other properties has no effect just changing background made this problem.
I found the answer problem was in 2 place:
first one when we use trigger in
<ControlTemplate.Triggers/>
you have sure you set your setter set property on the currect object
and the secend one is in the binding we have change
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}"
to
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource TemplatedParent}}"
because MoseOverBrush is parrent property not the ControlTemplate property
This is my Style in App.xaml:
<Style x:Key="numButton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Image x:Name="img" Style="{DynamicResource imgDefault}"></Image>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Source" TargetName="img" Value="img/1_push.png"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Cursor" Value="Hand"></Setter>
</Style>
<Style x:Key="imgDefault" TargetType="{x:Type Image}">
<Setter Property="Source" Value="img/0.png"></Setter>
</Style>
I have multiple buttons as numbers (0-9) and I want to use one style for all of the to avoid more text typing. Currently I am able to change each button's default background like this:
<Button Name="btn1" Grid.Row="0" Grid.Column="0" Style="{DynamicResource numButton}" Foreground="White">
<Button.Resources>
<Style x:Key="imgDefault" TargetType="{x:Type Image}">
<Setter Property="Source" Value="img/1.png"></Setter>
</Style>
</Button.Resources>
</Button>
So now I want to know is it possible to change Value="img/1_push.png" in App.xaml for each button when it's pressed? For example when 2 is pressed I want it's background to be 2_push.png.
Thanks in advance.
You could create custom Button class with two dependency properties:
ImageButton.cs:
namespace WpfApplication2
{
public class ImageButton : System.Windows.Controls.Button
{
public static readonly DependencyProperty DefaultImageProperty =
DependencyProperty.Register("DefaultImage", typeof(Uri), typeof(ImageButton));
public Uri DefaultImage
{
get { return (Uri)GetValue(DefaultImageProperty); }
set { SetValue(DefaultImageProperty, value); }
}
public static readonly DependencyProperty PressedImageProperty =
DependencyProperty.Register("PressedImage", typeof(Uri), typeof(ImageButton));
public Uri PressedImage
{
get { return (Uri)GetValue(PressedImageProperty); }
set { SetValue(PressedImageProperty, value); }
}
}
}
App.xaml:
<Application x:Class="WpfApplication2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
StartupUri="Window1.xaml">
<Application.Resources>
<Style TargetType="local:ImageButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Image x:Name="img" Source="{Binding DefaultImage,RelativeSource={RelativeSource TemplatedParent}, FallbackValue=img/0.png, TargetNullValue=img/0.png}" />
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Source" TargetName="img" Value="{Binding PressedImage, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=img/1_push.png, TargetNullValue=img/1_push.png}"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Cursor" Value="Hand"></Setter>
</Style>
</Application.Resources>
Usage:
<local:ImageButton x:Name="btn1" Grid.Row="0" Grid.Column="0" Foreground="White"
DefaultImage="img/1.png" PressedImage="2_push.png"/>
This is a much more cleaner and flexible approach.
I am making an Expander style, and I noticed that when the border around the Expander's ToggleButton goes from 0 to a non-zero value - "0,0,1,1", in this case - it is resized slightly, to shrink inside the border:
Not exactly app-breaking... But annoying that I can't fix it. How can I circumvent this? I need the Border in the ToggleButton's style to be 0 when the Expander is collapsed (since Expander has its own border that would make it look like a doubly-thick border) and non-zero when expanded (since I want the button to be separated by a border from the rest of the content).
ToggleButton style:
<Style x:Key="ExpanderRightHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="Bd" Background="{StaticResource MutedColorBrush}" BorderBrush="{StaticResource Expander.Border}" Padding="{TemplateBinding Padding}" CornerRadius="0" Height="22" Width="22">
<Grid Background="Transparent" SnapsToDevicePixels="False" Margin="0">
<ui:ColorableImage x:Name="img" Margin="0" Padding="2" Background="{StaticResource MutedColorBrush}" Color="Black" Height="{Binding Path=Height, RelativeSource={RelativeSource AncestorType={x:Type Border}}}" Width="{Binding Path=Width, RelativeSource={RelativeSource AncestorType={x:Type Border}}}" Source="Assets/Images/filter.png" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="false"/>
<ContentPresenter HorizontalAlignment="Center" Margin="12,0,12,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="BorderThickness" TargetName="Bd" Value="0,0,1,1" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource MainColorBrush}"/>
<Setter Property="Background" TargetName="img" Value="{StaticResource MainColorBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Color" TargetName="img" Value="White"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Expander style:
<Style TargetType="{x:Type Expander}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="#F0F0F0"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="BorderBrush" Value="{StaticResource Expander.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" SnapsToDevicePixels="true">
<DockPanel>
<ToggleButton x:Name="HeaderSite" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" Content="{TemplateBinding Header}" DockPanel.Dock="Top" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}" FontStyle="{TemplateBinding FontStyle}" FontStretch="{TemplateBinding FontStretch}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="0" MinWidth="0" MinHeight="0" Padding="0" Style="{StaticResource ExpanderDownHeaderStyle}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" VerticalAlignment="Top" Width="Auto" Height="Auto"/>
<ContentPresenter x:Name="ExpandSite" DockPanel.Dock="Bottom" Focusable="false" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="5,0,5,10" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
<Setter Property="Height" Value="Auto"/>
<Setter Property="Height" TargetName="ExpandSite" Value="Auto"/>
</Trigger>
<Trigger Property="ExpandDirection" Value="Right">
<Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right"/>
<Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left"/>
<Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderRightHeaderStyle}"/>
</Trigger>
<Trigger Property="ExpandDirection" Value="Up">
<Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top"/>
<Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom"/>
<Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderUpHeaderStyle}"/>
</Trigger>
<Trigger Property="ExpandDirection" Value="Left">
<Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left"/>
<Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right"/>
<Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderLeftHeaderStyle}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Possibly related - the ColorableImage control:
ColorableImage.xaml (x:Name="This"):
<Grid>
<Image x:Name="originalImage" Visibility="Hidden" Stretch="{Binding ElementName=This, Path=Stretch}" Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualWidth}"/>
<Image x:Name="displayedImage" Visibility="Visible" Stretch="{Binding ElementName=This, Path=Stretch}" Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualWidth}" />
</Grid>
ColorableImage.xaml.cs:
public partial class ColorableImage : UserControl
{
// This is so that SourcePropertyChanged will not execute when ColorPropertyChanged does. ColorPropertyChanged changes Source,
// so SourcePropertyChanged is called in turn. This will change originalImage to the newly colored Source, which is incorrect.
// "Wrapping" the statement that changes the binding in ColorPropertyChanged with this boolean and making it a condition in
// SourcePropertyChanged makes SourcePropertyChanged executre only when Source is directly changed, whether through C# or XAML.
private static bool changedByColor = false;
public ColorableImage()
{
InitializeComponent();
}
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorableImage), new FrameworkPropertyMetadata(ColorPropertyChanged));
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(ImageSource), typeof(ColorableImage), new FrameworkPropertyMetadata(SourcePropertyChanged));
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public static readonly DependencyProperty StretchProperty = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(ColorableImage), new FrameworkPropertyMetadata(Stretch.Uniform));
public Stretch Stretch
{
get { return (Stretch)GetValue(StretchProperty); }
set
{
SetValue(StretchProperty, value);
this.originalImage.Stretch = value;
this.displayedImage.Stretch = value;
}
}
private static void ColorPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ColorableImage cimg = (ColorableImage)sender;
Binding binding = new Binding("Source")
{
ElementName = "originalImage",
Converter = new HighlightImageConverter(),
ConverterParameter = cimg.Color,
Mode = BindingMode.OneWay
};
changedByColor = true;
cimg.displayedImage.SetBinding(Image.SourceProperty, binding);
changedByColor = false;
}
private static void SourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (!changedByColor)
{
ColorableImage cimg = (ColorableImage)sender;
cimg.originalImage.Source = cimg.Source;
cimg.displayedImage.Source = cimg.Source;
// Note: #00000000 is the value for the "null color"
if (cimg.Color != null && !cimg.Color.ToString().Equals("#00000000"))
{
// Color the new image (this is necessary if the user sets the source after the color; the color would be lost, otherwise)
ColorPropertyChanged(sender, e);
}
}
}
}
You can probably avoid that problem by setting the BorderBrush to transparent, instead of setting the BorderThickness to 0.
You might possibly have to add an extra container...
I don't feel like testing this, since the question is old and #Sheridan wrote that he had trouble making it work.