I find solution Setter.TargetName does not work against an element from the BasedOn setting.
But I do not understand how prement him to me.
I have a class:
public class MyCheckBox: CheckBox
{
public bool DefaultValue
{
get { return (bool)GetValue(DefaultValueProperty); }
set { SetValue(DefaultValueProperty, value); }
}
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.Register("DefaultValue", typeof(bool), typeof(MyCheckBox), new UIPropertyMetadata(false));
protected override void OnToggle()
{
this.IsChecked = this.IsChecked == null ? !DefaultValue : this.IsChecked.Value ? false : true;
}
}
and XAML
<Style TargetType="{x:Type CheckBox}">
<Style.Resources>
<Imaging:BitmapImage x:Key="CheckBoxStatusSource" UriSource="FalseIfNull.png"/> <!-- icon if [IsChecked] is [null] -->
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Image x:Name="CheckBoxStatus" Source="{DynamicResource CheckBoxStatusSource}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="b.png"/><!-- icon if [IsChecked] is [true] -->
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="c.png"/><!-- icon if [IsChecked] is [false] -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type My:MyCheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
<Style.Triggers>
<Trigger Property="DefaultValue" Value="true">
<!-- !!!!!!!!!!!!!!This need change [UriSource] in [CheckBoxStatusSource] to "TrueIfNull.png"!!!!!!!!!!!!!! -->
</Trigger>
</Style.Triggers>
</Style>
Perhaps there is another solution
Unless you need to extend CheckBox for other reasons, what you're trying to do here can be done with Attached Properties too, and it would allow you to have one single Style for all your CheckBoxes.
public static class CheckBoxExtensions
{
public static void SetDefaultValue(CheckBox element, bool value)
{
element.SetValue(DefaultValueProperty, value);
}
public static bool GetDefaultValue(CheckBox element)
{
return (bool)element.GetValue(DefaultValueProperty);
}
// Using a DependencyProperty as the backing store for DefaultValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.RegisterAttached("DefaultValue", typeof(bool), typeof(CheckBoxExtensions), new UIPropertyMetadata(false));
}
This property is now available for you to use in any CheckBox in your solution, and in any Style and Template you use for your CheckBoxes. In this case...
<Style TargetType="{x:Type CheckBox}">
<Style.Resources>
<Imaging:BitmapImage x:Key="CheckBoxStatusSourceFalse" UriSource="FalseIfNull.png"/> <!-- icon if [IsChecked] is [null] and [DefaultValue] is [false] -->
<Imaging:BitmapImage x:Key="CheckBoxStatusSourceTrue" UriSource="TrueIfNull.png"/> <!-- icon if [IsChecked] is [null] and [DefaultValue] is [true] -->
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Image x:Name="CheckBoxStatus" Source="{DynamicResource CheckBoxStatusSourceFalse}"/>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="local:CheckBoxExtensions.DefaultValue" Value="True" />
<Condition Property="IsChecked" Value="{x:Null}" />
</MultiTrigger.Conditions>
<Setter TargetName="CheckBoxStatus" Property="Source" Value="{DynamicResource CheckBoxStatusSourceFalse}"/>
</MultiTrigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="b.png"/><!-- icon if [IsChecked] is [true] -->
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="CheckBoxStatus" Property="Source" Value="c.png"/><!-- icon if [IsChecked] is [false] -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT: Even if you still need to extend CheckBox, this Style would work for both regular CheckBox and your MyCheckBox implementation. Just create another Style that's based on this one.
<Style TargetType="{x:Type My:MyCheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
...
</Style>
And if you want to access the DefaultValue property from code-behind, you can do it this way:
CheckBoxExtensions.SetDefaultValue(checkBox, true);
bool defaultValue = CheckBoxExtensions.GetDefaultValue(checkBox);
Related
I know there are many posts dealing with this topic, but none that address my specific need. I want to define an override to the default style of the WPF Textbox ContextMenu. My application is global with user defined multi-language. That means all the literals in my app are really buttons and the user can enable a setting which allows a button click to display a dialog to enter the text for different languages. This works throughout my app including other ContextMenus. However, my solution for the Textbox context menu does everything right but will not close when the button is pressed. How can I get the buttons in the MenuItem style/template to close the Context Menu? Here is the style for the ContextMenu.
<Style x:Key="MyContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Background" Value="{DynamicResource MyPopupBackgroundBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource MyBorderBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border x:Name="Border" Background="{TemplateBinding Background}" BorderThickness="1"
CornerRadius="6" BorderBrush="{TemplateBinding BorderBrush}">
<ScrollViewer x:Name="ScrollViewer" VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Hidden" Margin="4,4,4,4">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MyMenuItemStyle" TargetType="{x:Type ctls:MyMenuItem}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="StaysOpenOnClick" Value="False"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="#FF494949"/>
<Setter Property="ButtonImageSource" Value="{StaticResource EditBlue}" />
<Setter Property="MaxImageSize" Value="20" />
<Setter Property="ButtonText" Value="Text" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ctls:MyMenuItem}">
<Grid Margin="0,2,0,0">
<ctls:MyBasicButton Style="{StaticResource MyImageAndTextButtonStyle}"
HorizontalAlignment="Left" FontSize="16"
ButtonName="{TemplateBinding ButtonName}"
CollectionKey="{TemplateBinding CollectionKey}"
Background="Transparent" BorderThickness="0"
Content="{TemplateBinding ButtonText}"
CanUseAlternateText="{TemplateBinding CanUseAlternateText}"
Image="{TemplateBinding ButtonImageSource}"
MaxImageSize="{TemplateBinding MaxImageSize}"
Command="{Binding Path=Command, RelativeSource={RelativeSource TemplatedParent}}">
</ctls:MyBasicButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is the resource for the ContextMenu template.
<system:String x:Key="CopyString">Copy</system:String>
<system:String x:Key="CutString">Cut</system:String>
<system:String x:Key="PasteString">Paste</system:String>
<ContextMenu x:Key="CutCopyPasteContextMenu" Style="{StaticResource MyContextMenuStyle}">
<ctls:MyMenuItem Style="{StaticResource MyMenuItemStyle}"
Command="ApplicationCommands.Cut" InputGestureText=""
StaysOpenOnClick="False"
ButtonImageSource="{StaticResource CutBlue}"
ButtonText="{Binding Source={StaticResource CutString}, Converter={StaticResource StringToMenuItemText}}">
</ctls:MyMenuItem>
<ctls:MyMenuItem Style="{StaticResource MyMenuItemStyle}"
Command="ApplicationCommands.Copy" InputGestureText=""
StaysOpenOnClick="False"
ButtonImageSource="{StaticResource CopyBlue}"
ButtonText="{Binding Source={StaticResource CopyString}, Converter={StaticResource StringToMenuItemText}}">
</ctls:MyMenuItem>
<ctls:MyMenuItem Style="{StaticResource MyMenuItemStyle}"
Command="ApplicationCommands.Paste" InputGestureText=""
StaysOpenOnClick="False"
ButtonImageSource="{StaticResource PasteBlue}"
ButtonText="{Binding Source={StaticResource PasteString}, Converter={StaticResource StringToMenuItemText}}">
</ctls:MyMenuItem>
</ContextMenu>
I have a custom MenuItem control with dependency properties that support binding between my custom Button control and the MenuItem. Here is that code.
public class MyMenuItem : MenuItem
{
public string ButtonName
{
get { return (string)base.GetValue(ButtonNameProperty); }
set { SetValue(ButtonNameProperty, value); }
}
public static readonly DependencyProperty ButtonNameProperty =
DependencyProperty.Register("ButtonName", typeof(string), typeof(MyMenuItem));
public string ButtonText
{
get { return (string)base.GetValue(ButtonTextProperty); }
set { SetValue(ButtonTextProperty, value); }
}
public static readonly DependencyProperty ButtonTextProperty =
DependencyProperty.Register("ButtonText", typeof(string), typeof(MyMenuItem));
public ImageSource ButtonImageSource
{
get { return GetValue(ButtonImageSourceProperty) as ImageSource; }
set { SetValue(ButtonImageSourceProperty, value); }
}
public static readonly DependencyProperty ButtonImageSourceProperty =
DependencyProperty.Register("ButtonImageSource", typeof(ImageSource), typeof(MyMenuItem));
public double MaxImageSize
{
get { return (double)base.GetValue(MaxImageSizeProperty); }
set { base.SetValue(MaxImageSizeProperty, value); }
}
public static readonly DependencyProperty MaxImageSizeProperty =
DependencyProperty.Register("MaxImageSize", typeof(double), typeof(MyMenuItem));
public string CollectionKey
{
get { return (string)base.GetValue(CollectionKeyProperty); }
set { base.SetValue(CollectionKeyProperty, value); }
}
public static readonly DependencyProperty CollectionKeyProperty =
DependencyProperty.Register("CollectionKey", typeof(string), typeof(MyMenuItem));
public bool CanUseAlternateText
{
get { return (bool)GetValue(CanUseAlternateTextProperty); }
set { SetValue(CanUseAlternateTextProperty, value); }
}
public static readonly DependencyProperty CanUseAlternateTextProperty =
DependencyProperty.Register("CanUseAlternateText", typeof(bool), typeof(MyMenuItem));
}
Finally, Here is the style for my Textbox using my custom ContextMenu named CutCopyPasteContextMenu.
<Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Background" Value="{DynamicResource MyDataLabelBackgroundBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource MyTextBrush}"/>
<Setter Property="CaretBrush" Value="{DynamicResource MyTextBrush}"/>
<Setter Property="SelectionBrush" Value="{DynamicResource MySelectedBrush}"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="{DynamicResource MyMainFont}" />
<Setter Property="Height" Value="28"/>
<Setter Property="Padding" Value="0,0,4,0"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource MyBorderBrush}"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="Continue"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="IsReadOnly" Value="False"/>
<Setter Property="ToolTipService.InitialShowDelay" Value="2000"/>
<Setter Property="ToolTipService.BetweenShowDelay" Value="0"/>
<Setter Property="ToolTipService.ShowDuration" Value="60000"/>
<Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
<Setter Property="ContextMenu" Value="{StaticResource CutCopyPasteContextMenu}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" CornerRadius="4"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false"
Margin="4,-1,0,0"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{DynamicResource MyDisabledBackgroundBrush}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="true"/>
<Condition Property="IsReadOnly" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource MyBorderHighlightBrush}"/>
</MultiTrigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource MyBorderHighlightBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
Here is what the ContextMenu looks like when the app is run. All the menu item functions work correctly and I am allowed to define the dynamic language text for the menu item buttons as I designed.
Can someone please tell me why the ContextMenu will not close? I think it might have something to do with my Button control absorbing the click event of the MenuItem but I am just guessing.
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 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" />