Using DataTriggers in XAML to force events on CheckBox property changing - c#

I have a CheckBox set up something like this:
<CheckBox Unchecked="checkBox_Unchecked">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyVal}" Value="{x:Null}">
<Setter Property="IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Is there any way to get the Unchecked event to fire when my Setter sets IsChecked = False?

This may not be a procedural approach; But for your statement, I guess I should specify that by "force"... You can make this using this work around;
1) In your ViewModel have a boolean property listening to 'INotifyPropertyChanged'
2) Let the default value of property be true;
3) In your constructor re-assign the property's value to 'false'.
4) This will trigger the unchecked event;
XAML:
<CheckBox IsChecked="{Binding CheckboxChecked}" Unchecked="ToggleButton_OnUnchecked"/>
C#:
public partial class MainWindow : INotifyPropertyChanged
{
private bool m_CheckboxChecked = true;
public bool CheckboxChecked
{
get { return m_CheckboxChecked; }
set { m_CheckboxChecked = value; OnPropertyChanged("CheckboxChecked"); }
}
public MainWindow()
{
DataContext = this;
InitializeComponent();
CheckboxChecked = false;
}
private void ToggleButton_OnUnchecked(object sender, RoutedEventArgs e)
{
Console.WriteLine("Un-Checked");
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}

You need a Setter for the Case IsChecked = false too. Also I would recommend to bind to a Command in the ViewModel Try this:
<Style.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Command" Value="{Binding ElementName=this, Path=DataContext.ItemCheckedCommand}"></Setter>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter Property="Command" Value="{Binding ElementName=this, Path=DataContext.ItemCheckedCommand}"></Setter>
</Trigger>
</Style.Triggers>
And define a command where you have your logic in the VM:
public RelayCommand ItemCheckCommand {get; private set;}

Related

WPF, setting background color with trigger

I have a customcontrol which render a textbox. I've also a style that set the color of the background based on some conditions as follows:
<Style x:Key="ArtParamStyle" TargetType="av:DC_Base">
<Setter Property="Background" Value="{StaticResource EditableAreaBrush}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Info.Upd.IsAutoCalc}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Forced}" Value="True">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
Initially as the value of my textbox is autocalculated, the background is correctly red. If I also set the Forced as true (by ticking a chebckbox) I've a weird result, the border of textbox is lightgreen but background not.
It seems to be a strange color, a combination of red and lightgreen. As test, if I set the "IsAutoCalc" color as Transparent, the trigger works correctly. How can I solve this?
your code seems to be correct. But I provide you my sample:
XAML:
<Window.Resources>
<Style x:Key="ArtParamStyle" TargetType="TextBox">
<Setter Property="Background" Value="Yellow" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Bool1}" Value="True"/>
<Condition Binding="{Binding Bool2}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Red" />
<Setter Property="BorderBrush" Value="Red" />
</MultiDataTrigger>
<DataTrigger Binding="{Binding Bool2}" Value="True">
<Setter Property="Background" Value="LightGreen" />
<Setter Property="BorderBrush" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<TextBox Style="{StaticResource ArtParamStyle}" Height="50" Margin="4"/>
<CheckBox IsChecked="{Binding Bool1}"/>
<CheckBox IsChecked="{Binding Bool2}"/>
</StackPanel>
</Grid>
In this case I used Multidatatrigger to set red background when Bool2(your forced) is not checked.
MainWindow.cs:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChaged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private bool bool1;
public bool Bool1
{
get { return bool1; }
set { bool1 = value; RaisePropertyChaged("Bool1"); }
}
private bool bool2;
public bool Bool2
{
get { return bool2; }
set { bool2 = value; RaisePropertyChaged("Bool2"); }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
}
Probably your problem is related to your custom control.

WPF Button Background Change on Click and Reset to original when another button in same StackPanel Clicked.

I have a StackPanel with a few buttons. I want to change the color of a button when clicked and reset it to the original when another button in StackPanel is clicked. Is it possible with a single style applied on StackPanel or I have to create Style for each button? If yes then how.
Here is the code of Style applied to StackPanel but this changes the color of the button but does not reset it on clicking another button.
<Style TargetType="StackPanel" x:Key="GlobalStackPanelStyle" BasedOn="{StaticResource FlatStackPanel}">
<Style.Resources>
<Style TargetType="Button">
<Setter Property="Button.Background" Value="Blue"/>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="Green"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
Sorry for the delayed response. Here are the steps that you can follow to get the required output.
Assuming you are following MVVM design pattern.
Create buttons in .xaml and bind command to each button as shown below,
<Button Height="32" Width="180" Grid.Column="1" Content="Button 1"
Command="{Binding ClickCommand}" CommandParameter="Button 1">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsButton1Active}" Value="True">
<Setter Property="Background" Value="Green" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding IsButton1Active}" Value="False">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Height="32" Width="180" Grid.Column="2" Content="Button 2"
Margin="5,0,0,0"
Command="{Binding ClickCommand}" CommandParameter="Button 2">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsButton2Active}" Value="True">
<Setter Property="Background" Value="Green" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding IsButton2Active}" Value="False">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Note: You can add how many buttons you want to add to above xaml.
Create 2 boolean properties and set the values of those boolean properties from your ClickCommand method.
private bool isButton1Active;
private bool isButton2Active;
public bool IsButton1Active
{
get { return isButton1Active; }
set { isButton1Active = value; OnPropertyChanged(); }
}
public bool IsButton2Active
{
get { return isButton2Active; }
set { isButton2Active = value; OnPropertyChanged(); }
}
Here is code for command - Add it in your ViewModel
private UICommand _clickCommand;
public UICommand ClickCommand
{
get { return _clickCommand; }
}
Write below statement in your view model constructor
public YourViewModelConstructor()
{
_clickCommand = new UICommand(OnClick);
}
Here is the method that is bound to ClickCommand
private void OnClick(object parameter)
{
switch(parameter.ToString())
{
case "Button 1":
IsButton1Active = true;
IsButton2Active = false;
break;
case "Button 2":
IsButton2Active = true;
IsButton1Active = false;
break;
}
}
Here is the code for my UICommand class
public class UICommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<bool> _canExecute;
public UICommand(Action<object> onExecuteMethod, Func<bool> onCanExecuteMethod =
null)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
public bool IsCanExecute { get; set; }
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add { if (_canExecute != null) CommandManager.RequerySuggested += value; }
remove { if (_canExecute != null) CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
public bool CanExecute(object parameter)
{
IsCanExecute = (_canExecute == null || _canExecute());
return IsCanExecute;
}
#endregion
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
}
I assume you know on how to setting datacontext to your window.
This examples gives you an idea on how to achieve functionality by creating some properties created in your ViewModel and bind a command in your View to ViewModel Command property and invoke click command by passing Command Parameter.
Still have any doubts after implementing the solution, kindly let us know.

WPF DataGrid DataTrigger Binding on DataContext property

What's the correct DataTrigger binding for DataContext properties?
I have a DataGrid which is bound like this:
XAML:
<DataGrid x:Name="dataGrid" Grid.Row="4" Grid.ColumnSpan="2"
ItemsSource="{Binding}"
CellStyle="{StaticResource RowStateStyle}"
>
</DataGrid>
In .cs the grid is bound to a DataTable, hence the DataContext for a cell is DataRowView, containing Row as a property:
// DataTable containing lots of rows/columns
dataGrid.DataContext = dataTable;
Edit:
Refering to ASh's solution I edited the style and put in Triggers for Unchanged and Modified:
<Style x:Key="RowStateStyle" TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.Row.RowState,
RelativeSource={RelativeSource Self}}" Value="Unchanged">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=DataContext.Row.RowState,
RelativeSource={RelativeSource Self}}" Value="Modified">
<Setter Property="Foreground" Value="Yellow" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Content.Text,
RelativeSource={RelativeSource Self}}" Value="test">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
Triggering on Content.Text works perfectly fine, so does Unchanged. However when I modify a cell (thus DataRowState = Modified), nothing happens and the color stays green. Any solution?
DataTrigger works for me, if I
use DataContext.Row.RowState path
don't use Mode=TwoWay
and remove enum name DataRowState when set Value
<DataTrigger Binding="{Binding Path=DataContext.Row.RowState,
RelativeSource={RelativeSource Self}}"
Value="Unchanged">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
I have found another post related to this issue
WpfToolkit DataGrid: Highlight modified rows
There is no default mechanism which notifies about RowState changes. But it is possible to create one in a derived dataTable class:
public class DataTableExt: DataTable
{
protected override DataRow NewRowFromBuilder(DataRowBuilder builder)
{
return new DataRowExt(builder);
}
protected override void OnRowChanged(DataRowChangeEventArgs e)
{
base.OnRowChanged(e);
// row has changed, notifying about changes
var r = e.Row as DataRowExt;
if (r!= null)
r.OnRowStateChanged();
}
}
with a derived dataRow class:
public class DataRowExt: DataRow, INotifyPropertyChanged
{
protected internal DataRowExt(DataRowBuilder builder) : base(builder)
{
}
internal void OnRowStateChanged()
{
OnPropertyChanged("RowState");
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}

How can I bind to whether any/none `RadioButton` of a certain group is selected?

How can I bind to whether any/none RadioButton of a certain group is checked?
I have, say, 4 RadioButtons. Initially none of them is checked, so the "next" button is disabled. User must make a selection in order to enable the "next" button and be able to proceed.
First solution is not efficient since every new RadioButton entails to add extra DataTrigger:
<StackPanel>
<RadioButton Name="x1"/>
<RadioButton Name="x2"/>
<RadioButton Name="x3"/>
<RadioButton Name="x4"/>
<Button Content="Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=x1, Path=IsChecked}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=x2, Path=IsChecked}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=x3, Path=IsChecked}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=x4, Path=IsChecked}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
Latter in turn is based on MVVM. You create property IsChecked in your ViewModel and whenever any RadioButton is checked it sets IsChecked value to True and your Button based on this value sets its IsEnabled property.
XAML:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="RadioButton">
<Setter Property="Command" Value="{Binding RadioButtonCheckedCommand}"/>
</Style>
</StackPanel.Resources>
<RadioButton Name="x1"/>
<RadioButton Name="x2"/>
<RadioButton Name="x3"/>
<RadioButton Name="x4"/>
<Button Content="Click" IsEnabled="{Binding IsChecked}"/>
</StackPanel>
ViewModel:
class MainViewModel : INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged();
}
}
private RelayCommand _radioButtonCheckedCommand;
public RelayCommand RadioButtonCheckedCommand
{
get
{
return _radioButtonCheckedCommand ??
(_radioButtonCheckedCommand = new RelayCommand(() => IsChecked = true));
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}

DataTrigger triggered by boolean

I want to change the foreground setter property using a boolean equals true.
I tried:
public bool RED = false;
if (condition)
{
RED = true;
}
and have the DataTrigger triggered by the boolean:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="16" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=RED}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
But no luck. Whats the best/simplest way to write this?
You need to create a property. What you have is a field. Also, you need to implement INotifyPropertyChanged or use a DependencyProperty or other type type of property that is able to notify a WPF binding.
private bool _red;
public bool Red {
get { return _red; }
set
{
_red = value;
OnPropertychanged();
}
}

Categories