KeyDown Event is triggered twice - c#

I have a style which includes two TextBoxes in order to create a PlaceHolder / Watermark, so far is working fine.The only exception is that the events are triggered twice, the first one comes from the CustomTextBox I have in the Style, and the second one from the CustomTextBox I have in XAML.Is there any way to prevent this behaviour? I have already tried to set IsEnable="False" and ReadOnly="True" but doesn't seems to work.Here the style I use to simulate the Watermark:
<Style x:Key="CustomTextBoxStyle"
TargetType="{x:Type utils:CustomTextBox}">
<Setter Property="FontFamily"
Value="/UserInterface;component/Resources/Fonts/#Avenir LT Std 35 Light" />
<Setter Property="FontSize"
Value="16" />
<Setter Property="Foreground"
Value="#FF414042" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type utils:CustomTextBox}">
<Border Name="Border"
BorderBrush="#FF348781"
BorderThickness="0,0,0,4"
CornerRadius="2">
<ScrollViewer x:Name="PART_ContentHost"
Margin="0" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled" />
<VisualState x:Name="ReadOnly" />
<VisualState x:Name="MouseOver" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type utils:CustomTextBox}"
BasedOn="{StaticResource CustomTextBoxStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type utils:CustomTextBox}">
<Grid>
<utils:CustomTextBox
Text="{TemplateBinding Text}"
x:Name="textSource"
Background="Transparent"
Panel.ZIndex="2"
Style="{StaticResource CustomTextBoxStyle}"
KeyboardViewModel="{TemplateBinding KeyboardViewModel}"/>
<utils:CustomTextBox Text="{TemplateBinding HintText}"
Background="{TemplateBinding Background}"
Panel.ZIndex="1"
IsEnabled="False">
<utils:CustomTextBox.Style>
<Style TargetType="{x:Type utils:CustomTextBox}"
BasedOn="{StaticResource CustomTextBoxStyle}">
<Setter Property="Foreground"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, Source={x:Reference textSource}}"
Value="">
<Setter Property="Foreground"
Value="Gray" />
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
</DataTrigger>
</Style.Triggers>
</Style>
</utils:CustomTextBox.Style>
</utils:CustomTextBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any help would be appreciated. Thanks in advance.
EDIT 1: Code on the CustomTextBox Class for the Event handling
protected override void OnKeyDown(KeyEventArgs e)
{
//Filtering "solution"
if (e.Source is CustomTextBox sourceTextBox && sourceTextBox.Name.Equals("textSource"))
{
return;
}
base.OnKeyDown(e);
if (e.Key == Key.Enter && EnterKeyCommand != null)
{
if (EnterKeyCommand.CanExecute(null))
{
EnterKeyCommand.Execute(null);
}
}
}
EDIT 2:
Use of the CustomTextBox on my UserControl:
<Utils:CustomTextBox Grid.Row="0"
Margin="0,10"
KeyboardViewModel="{Binding Path=MainWindowViewModel.KeyboardViewModel}"
HintText="Patient"
x:Name="Patient"/>

Based on your comment, first it comes from the "test source" and then goes to CustomTextBox means that the KeyDown event is received on first user control and then passed to the second user control.
Can you make sure that you put e.Handled = true in the "test source" to notify that it is handled on the first user control?

"Fixed" filtering the e.Source as sugesstion from #Amol Bavannavar on the comments:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.Source is CustomTextBox sourceTextBox && sourceTextBox.Name.Equals("textSource"))
{
return;
}
base.OnKeyDown(e);
if (e.Key == Key.Enter && EnterKeyCommand != null)
{
if (EnterKeyCommand.CanExecute(null))
{
EnterKeyCommand.Execute(null);
}
}
}

Related

How to disable all toggle buttons except one that clicked?

I'm making an application using C# and WPF
I have 8 toggle buttons.
When I click one of the buttons others should be disabled so I can't click it except one is activated.
When I click this button again others should be enabled
Styles.xaml:
<!--Toggle Button-->
<Style x:Key="ToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="MinHeight" Value="30"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml:
<ToggleButton x:Name="CategoryToggle1"
Grid.Row="1"
Style="{StaticResource ToggleButton}"
Checked="ShowOptions"
Unchecked="HideOptions" />
How Can I achieve this by code and XAML?
I want to do it like in this video:
Video
Thank you for comments guys, I found another solution.
MainWindow.XAML:
<RadioButton x:Name="CategoryToggle1"
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource RadioButton}"
GroupName="ToggleButtonsGroup"
Checked="OpenOptions"
Unchecked="HideOptions"/>
Styles.xaml:
<!--Radio Button-->
<Style TargetType="RadioButton"
x:Key="RadioButton"
BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--Toggle Button-->
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353" />
<Setter Property="MinHeight" Value="30" />
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Opacity" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.cs:
private void OpenOptions(object sender, RoutedEventArgs e){
RadioButton radioButton = sender as RadioButton;
radioButton.IsChecked = true;
//Disable all option buttons except one that active
MyGrid.Children.OfType<RadioButton>().Where(rb => rb != radioButton &&
rb.GroupName == radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = false);
}
private void HideOptions(object sender, RoutedEventArgs e)
{
RadioButton radioButton = sender as RadioButton;
MyGrid.Children.OfType<RadioButton>().Where(rb => rb.GroupName ==
radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = true);
}
Using Click events of each ToggleButton
One way you could do it is by giving a name to all your ToggleButtons, hook-up to their Click event and manually uncheck others in the code-behind:
XAML
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="MaxWidth" Value="15"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" Click="button1_Click"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" Click="button2_Click"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" Click="button3_Click"/>
<TextBlock Text="Line 3"/>
</StackPanel>
</StackPanel>
Code-behind
private void button1_Click(object sender, RoutedEventArgs e) {
if (button1.IsChecked == true) {
button2.IsChecked = false;
button3.IsChecked = false;
}
}
private void button2_Click(object sender, RoutedEventArgs e) {
if (button2.IsChecked == true) {
button1.IsChecked = false;
button3.IsChecked = false;
}
}
private void button3_Click(object sender, RoutedEventArgs e) {
if (button3.IsChecked == true) {
button1.IsChecked = false;
button2.IsChecked = false;
}
}
This method is tedious, error-prone, requires code-behind and is not very scalable.
Binding IsChecked properties to a collection of bool with one true at a time.
Another way you could go (still by using code-behind) is to define a collection of boolean values and bind each ToggleButton.IsChecked on one of the bool in the collection, and ensure that the collection only contains at most one true at a time:
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" IsChecked="{Binding [0]}"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" IsChecked="{Binding [1]}"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" IsChecked="{Binding [2]}"/>
<TextBlock Text="Line 3"/>
</StackPanel>
Code-behind
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
ObservableCollection<bool> states = new ObservableCollection<bool> { false, false, false };
states.CollectionChanged += States_CollectionChanged;
DataContext = states;
}
private void States_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
var collection = sender as ObservableCollection<bool>;
if (e.Action == NotifyCollectionChangedAction.Replace) {
if ((bool)e.NewItems[0]) {
for (int i = 0; i < collection.Count; i++) {
if (e.NewStartingIndex != i) {
collection[i] = false;
}
}
}
}
}
}
Again, this uses code-behind and not the view model but at least it is easier to add rows.
The behavior you need is very specific.
I don't know how, but i got here trying to make a toggle button behave like a radio button. Your answer was enlightning.
For what it's worth, here's how you would do that :
Resource :
<Style x:Key='RadioToggle' TargetType='RadioButton'
BasedOn='{StaticResource {x:Type ToggleButton}}' />
Control :
<RadioButton Content='RadioToggle1' IsChecked='True'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle2'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle3'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />

Doesn't disable the button if the textbox invalid

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;
}

After change wpf button IsMouseOver trigger color not work

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

Change Button Style dynamically

I have defined two different styles in a ResourceDictionary as follows:
<Style TargetType="{x:Type Button}" x:Key="EditButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border" Background="{StaticResource BrushBlueSelector}" Padding="5,2" SnapsToDevicePixels="true" CornerRadius="3">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" Color="Turquoise" BlurRadius="8" />
</Border.Effect>
<Path x:Name="buttonSymbol" Data="M0,44.439791L18.98951,54.569246 0.47998798,62.66881z M17.428029,12.359973L36.955557,23.568769 21.957478,49.686174 20.847757,46.346189 15.11851,45.756407 14.138656,42.166935 8.5292659,41.966761 6.9493899,38.037481 2.4399572,38.477377z M26.812517,0.0009765625C27.350616,-0.012230873,27.875986,0.10826397,28.348372,0.3782568L42.175028,8.3180408C43.85462,9.2780154,44.234529,11.777948,43.02482,13.89789L41.375219,16.767812 21.460039,5.3381228 23.10964,2.4582005C23.979116,0.941679,25.437378,0.034730911,26.812517,0.0009765625z"
Stretch="Uniform" Fill="#FFFFFFFF" Width="24" Height="24" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="{StaticResource BrushOrangeSelector}"/>
<Setter TargetName="Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Orange" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Background" Value="{StaticResource BrushHeaderBackground}"/>
<Setter TargetName="Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Gray" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="Gray"/>
<Setter TargetName="Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Gray" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Tag" Value="EditButton" />
<Setter Property="Margin" Value="3" />
<Setter Property="Focusable" Value="False" />
</Style>
<Style TargetType="{x:Type Button}" x:Key="SaveButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border" Background="{StaticResource BrushBlueSelector}" Padding="5,2" SnapsToDevicePixels="true" CornerRadius="3">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" Color="Turquoise" BlurRadius="2" />
</Border.Effect>
<Path x:Name="buttonSymbol" Data="M8.1099597,36.94997L8.1099597,41.793968 39.213959,41.793968 39.213959,36.94997z M12.42,0.049999889L18.4,0.049999889 18.4,12.252 12.42,12.252z M0,0L7.9001866,0 7.9001866,14.64218 39.210766,14.64218 39.210766,0 47.401001,0 47.401001,47.917 0,47.917z"
Stretch="Uniform" Fill="#FFFFFFFF" Width="24" Height="24" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="Green"/>
<Setter TargetName="Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Green" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Background" Value="{StaticResource BrushHeaderBackground}"/>
<Setter TargetName="Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Gray" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Tag" Value="SaveButton" />
<Setter Property="Margin" Value="3" />
<Setter Property="Focusable" Value="False" />
</Style>
I have an edit button inside dataGrid as follows:
<DataGrid Grid.Column="1" Margin="50" ItemsSource="{Binding Names}"
CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding}" Width="*" Header="Names"/>
<DataGridTemplateColumn Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Style="{StaticResource EditButton}" Click="EditButton_InsideDataGrid_Click" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And here is it's click event:
private void EditButton_InsideDataGrid_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
string tagValue = String.Empty;
if (button != null)
{
tagValue = button.Tag.ToString();
if (tagValue == "EditButton")
{
button.Style = (Style)Application.Current.Resources["SaveButton"];
}
else if (tagValue == "SaveButton")
{
button.Style = (Style)Application.Current.Resources["EditButton"];
}
}
}
Now, it works fine. I can see that:
Initially : Style is EditButton.
Click 1st time : Style is SaveButton.
Click 2nd time : Style is EditButton.
Click 3rd time : Style is SaveButton.
Click 4th time : Style is EditButton.
Click 5th time : Style is SaveButton.
......
......
......
Now, when I add the below mentioned code in Click event of the editButton :
int colIndex = 0;
int rowIndex = 0;
DependencyObject dep = (DependencyObject)e.OriginalSource;
while (dep != null && !(dep is DataGridCell))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
if (dep is DataGridCell)
{
colIndex = ((DataGridCell)dep).Column.DisplayIndex;
while (dep != null && !(dep is DataGridRow))
{
dep = VisualTreeHelper.GetParent(dep);
}
DataGridRow row = (DataGridRow)dep;
rowIndex = FindRowIndex(row);
}
while (dep != null && !(dep is DataGrid))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
DataGrid dg = (DataGrid)dep;
for (int column = 0; column < colIndex; column++)
{
if (!(dg.Columns[column].IsReadOnly))
{
DataGridCell cell = GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column]));
//cell.IsEditing = true;
}
}
dg.BeginEdit();
}
public DataGridCell GetDataGridCell(DataGridCellInfo cellInfo)
{
var cellContent = cellInfo.Column.GetCellContent(cellInfo.Item);
if (cellContent != null)
return (DataGridCell)cellContent.Parent;
return null;
}
private int FindRowIndex(DataGridRow row)
{
DataGrid dataGrid = ItemsControl.ItemsControlFromItemContainer(row) as DataGrid;
int index = dataGrid.ItemContainerGenerator.IndexFromContainer(row);
return index;
}
Now, my results are:
Initially : Style is EditButton.
Click 1st time : Style is EditButton.
Click 2nd time : Style is SaveButton.
Click 3rd time : Style is EditButton.
Click 4th time : Style is SaveButton.
Click 5th time : Style is EditButton.
......
......
......
Here is the sample project in which I reproduced the same problem:
https://drive.google.com/file/d/0B5WyqSALui0beVJXTG5yWTZwZm8/view?usp=sharing
Update:
New Sample as Suggested by #Yoyo : https://drive.google.com/file/d/0B5WyqSALui0bUGxRRklDOUpKRms/view?usp=sharing
I have tried to follow #Yoyo's instructions successfully. But still the problem remains same.
Here is how I offer to solve your issue
I rewrite your style for button
<Style TargetType="{x:Type Button}"
x:Key="EditSaveStyle">
<Style.Resources>
<Brush x:Key="BrushHeaderBackground">#FF2A2A2A</Brush>
<Brush x:Key="BrushBlueSelector">#FF0094FF</Brush>
<Brush x:Key="BrushOrangeSelector">#FFFF6A00</Brush>
</Style.Resources>
<Setter Property="Margin"
Value="3" />
<Setter Property="Focusable"
Value="False" />
<Setter Property="Width"
Value="32" />
<Setter Property="Height"
Value="32" />
<Setter Property="Background"
Value="{StaticResource BrushOrangeSelector}" />
<Setter Property="Content">
<Setter.Value>
<StreamGeometry>
M0,44.439791L18.98951,54.569246 0.47998798,62.66881z M17.428029,12.359973L36.955557,23.568769 21.957478,49.686174 20.847757,46.346189 15.11851,45.756407 14.138656,42.166935 8.5292659,41.966761 6.9493899,38.037481 2.4399572,38.477377z M26.812517,0.0009765625C27.350616,-0.012230873,27.875986,0.10826397,28.348372,0.3782568L42.175028,8.3180408C43.85462,9.2780154,44.234529,11.777948,43.02482,13.89789L41.375219,16.767812 21.460039,5.3381228 23.10964,2.4582005C23.979116,0.941679,25.437378,0.034730911,26.812517,0.0009765625z
</StreamGeometry>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border"
Background="{StaticResource BrushBlueSelector}"
Padding="5,2"
SnapsToDevicePixels="true"
CornerRadius="3">
<Border.Effect>
<DropShadowEffect ShadowDepth="0"
Color="Turquoise"
BlurRadius="8" />
</Border.Effect>
<Path x:Name="buttonSymbol"
Data="{TemplateBinding Content}"
Stretch="Uniform"
Fill="#FFFFFFFF"
RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1"
ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="Border"
Property="Background"
Value="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}" />
<Setter TargetName="Border"
Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0"
Color="{Binding Background.Color,RelativeSource={RelativeSource TemplatedParent}}"
BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource BrushHeaderBackground}" />
<Setter TargetName="Border"
Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0"
Color="Gray"
BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter TargetName="Border"
Property="Background"
Value="Gray" />
<Setter TargetName="Border"
Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0"
Color="Gray"
BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditing,RelativeSource={RelativeSource AncestorType=DataGridRow}}"
Value="true">
<Setter Property="Content">
<Setter.Value>
<StreamGeometry>M8.1099597,36.94997L8.1099597,41.793968 39.213959,41.793968 39.213959,36.94997z M12.42,0.049999889L18.4,0.049999889 18.4,12.252 12.42,12.252z M0,0L7.9001866,0 7.9001866,14.64218 39.210766,14.64218 39.210766,0 47.401001,0 47.401001,47.917 0,47.917z</StreamGeometry>
</Setter.Value>
</Setter>
<Setter Property="Background"
Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
the style is based on standard button instead
I combined both templates and specified the difference in the trigger
The trigger is based on the parent DataGridRow.IsEditing
here is the rewrite of the code in main window.cs
private void EditButton_InsideDataGrid_Click(object sender, RoutedEventArgs e)
{
int colIndex = 0;
int rowIndex = 0;
DependencyObject dep = (DependencyObject)e.OriginalSource;
while (dep != null && !(dep is DataGridCell))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
DataGridRow row = null;
if (dep is DataGridCell)
{
colIndex = ((DataGridCell)dep).Column.DisplayIndex;
while (dep != null && !(dep is DataGridRow))
{
dep = VisualTreeHelper.GetParent(dep);
}
row = (DataGridRow)dep;
rowIndex = FindRowIndex(row);
}
while (dep != null && !(dep is DataGrid))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
DataGrid dg = (DataGrid)dep;
if (row != null)
{
if (row.IsEditing)
dg.CommitEdit(DataGridEditingUnit.Row, true);
else
{
dg.CurrentCell = new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[0]);
dg.BeginEdit();
}
}
}
I did some adjustments to edit and save a row.
finally the data grid
<DataGrid Grid.Column="1" Margin="50" ItemsSource="{Binding Names}"
CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=.}" Width="*" Header="Names"/>
<DataGridTemplateColumn Header="Edit" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="EditButton_InsideDataGrid_Click"
Style="{StaticResource EditSaveStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I have use the style EditSaveStyle on a standard button
using this approach you may not required to inherit and create your own button for switching style etc. you may perhaps introduce attached properties if you look forward to customize even more.
here is a working sample ChangingStylesAtRuntime.zip
note that dg.CommitEdit may fail if the data is not properly binded or IsReadOnly="True" is not set for un-bound/read-only columns
Consider using a DataTemplateSelector Class.
This class provides a way to choose a DataTemplate based on the data object and the data-bound element.
XAML:
<Window.Resources>
...
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
...
</Window.Resources>
...
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"/>
Class:
public class TaskListDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate
SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null && item is Task)
{
Task taskitem = item as Task;
if (taskitem.Priority == 1)
return
element.FindResource("importantTaskTemplate") as DataTemplate;
else
return
element.FindResource("myTaskTemplate") as DataTemplate;
}
return null;
}
}

How can I change an elements style at runtime?

I have an element and multiple styles, how do I switch between the styles at runtime either programatically or through XAML binding.
<Rectangle x:Name="fixtureControl" Style="{DynamicResource FixtureStyle_Fast}">
<!-- In the style resources. -->
<Style x:Key="FixtureStyle_Fast" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Black"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
<Style x:Key="FixtureStyle_Good" TargetType="{x:Type Shape}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Opacity=".9"
Direction="-90"
RenderingBias="Performance"
BlurRadius="50"
ShadowDepth="10" />
</Setter.Value>
</Setter>
</Style>
<Style x:Key="FixtureStyle_Best" TargetType="{x:Type Shape}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Opacity=".9"
Direction="-90"
RenderingBias="Quality"
BlurRadius="50"
ShadowDepth="10" />
</Setter.Value>
</Setter>
</Style>
Then I have some radio buttons that handle changing the style
private void RadioButton_Click(object sender, RoutedEventArgs e) {
if (e.Source == rdoQualityBest) {
fixtureControl.Style = FindResource("FixtureStyle_Best") as Style;
} else if (e.Source == rdoQualityGood) {
fixtureControl.Style = FindResource("FixtureStyle_Good") as Style;
} else {
fixtureControl.Style = FindResource("FixtureStyle_Fast") as Style;
}
}
However this applies the style to the element, not replacing it, so if I apply Fast then Quality, I get both the border and the drop-shadow.
Something like this has worked for me in the past (a pure XAML solution):
<!-- Styles 1-4 defined somewhere else on your page -->
<ComboBox Name="AvailableStyles">
<ComboBoxItem Tag="{x:Null}" IsSelected="True">None</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style1}">1</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style2}">2</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style3}">3</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style4}">4</ComboBoxItem>
</ComboBox>
<Button Content="Button" Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}"/>
<CheckBox Content="Check Box" Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}"/>
<RadioButton Content="Radio Button"Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}"/>
Hope this helps!
It is working fine for me
here is my code:
in .xaml
<Window.Resources>
<Style x:Key="FixtureStyle_Fast" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Black"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
<Style x:Key="FixtureStyle_Good" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Red"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
<Style x:Key="FixtureStyle_Best" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Blue"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="fixtureControl" Style="{DynamicResource FixtureStyle_Fast}"/>
<StackPanel Grid.Column="1">
<RadioButton Name="rdoQualityBest" Content="Best" Click="RadioButton_Click" />
<RadioButton Name="rdoQualityGood" Content="Good" Click="RadioButton_Click" />
<RadioButton Name="rdoQualityFast" Content="Fast" Click="RadioButton_Click" />
</StackPanel>
</Grid>
in .xaml.cs
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
if (e.Source == rdoQualityBest)
{
fixtureControl.Style = FindResource("FixtureStyle_Best") as Style;
}
else if (e.Source == rdoQualityGood)
{
fixtureControl.Style = FindResource("FixtureStyle_Good") as Style;
}
else
{
fixtureControl.Style = FindResource("FixtureStyle_Fast") as Style;
}
}

Categories