How to fire Property Changed with an Enum Property? - c#

I have a standard Enum that will either be Yes or No:
public enum YesOrNo
{
Yes,
No
}
My base Model Class has a YesOrNo property like this:
public class Group : NotifyPropertyChanged
{
private YesOrNo groupOperator;
public YesOrNo GroupOperator
{
get
{
return this.groupOperator;
}
set
{
this.groupOperator = value;
OnPropertyChanged("GroupOperator");
}
}
In my View, I am using a ToggleSwitch, similar to a slider you would see on a Mobile phone. Sliding back and forth should effectively reassign the value of the Enum. So it will default as Yes and sliding the toggle will set the Enum value to No and alternatively.
If I were to have a test method that reassigns the Enum when the Checked command is hit, the PropertyChanged event is fired so I know that is technically working. I am just wondering how I could go about alternating values in the Enum.
This is the ToggleButton in my XAML:
<ToggleButton Style="{StaticResource ToggleViewSwitch}" Command="{Binding SetOperatorCommand, UpdateSourceTrigger=PropertyChanged}" IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
And this is my Main View Model, where I hold the Command and the test method to assign the value manually:
private bool isChecked = false;
public bool IsChecked
{
get
{
return this.isChecked;
}
set
{
this.isChecked = value;
OnPropertyChanged("IsChecked");
}
}
private RelayCommand setOperatorCommand;
public ICommand SetOperatorCommand
{
get
{
if (this.setOperatorCommand == null)
{
this.setOperatorCommand = new RelayCommand(
x => ToggleGroupOperator());
}
return this.setOperatorCommand;
}
}
private void ToggleGroupOperator()
{
if (IsChecked)
{
TopLevelGroup.GroupOperator = YesNo.No;
}
else
{
TopLevelGroup.GroupOperator = YesNo.Yes;
}
}

First make a Converter...
public class YesOrNoToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> (value is YesOrNo yesOrNo && yesOrNo == YesOrNo.Yes) ? true : false;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> (value is bool isYes && isYes) ? YesOrNo.Yes : YesOrNo.No;
}
Then reference the converter during binding...
<Window.Resources>
<Converters:YesOrNoToBooleanConverter x:Key="YesOrNoToBooleanConverter" />
</Window.Resources>
<Grid>
<CheckBox IsChecked="{Binding GroupOperator, Converter={StaticResource YesOrNoToBooleanConverter}}" />
</Grid>
This will allow your ViewModel to remain using the enum without any overhead and the view to bind without any overhead; leave this binding manipulation work to converters.

Related

UpdateSourceTrigger not working with converter

I am changing the progress bar based on how many items are in the list. Below code works fine.
<Controls:MetroProgressBar Minimum="0" Maximum="2" Value="{Binding ProgressList.Count, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
But what I really need is not ProgressList.Count but ProgressList.Where(x => x.Done).Count() so I changed my xaml to:
<Controls:MetroProgressBar Minimum="0" Maximum="2" Value="{Binding ProgressList, Converter={StaticResource DoneProgressConverter}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
and made a converter:
public class DoneProgressConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((ObservableCollection<SetupProgress>)value).Where(x => x.Done).Count();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But it seems that UpdateSourceTrigger=PropertyChanged doesn't trigger anymore because it enters my converter only once (immediately after running the application).
You should create a Property and update it from your ViewModel and bind it to your progress bar. in that way you will notify View by changing the property binded.
private int _Progress;
public int Progress
{
get { return _Progress; }
set
{
_Progress= value;
NotifyPropertyChanged();
}
}
Now Bind it to progress bar and update the property by,
Progress = ProgressList.Where(x => x.Done).Count();
or add to the Setter of your List to Modify when you add new items.
private ObservableCollection<yourClass>_ProgressList;
public ObservableCollection<yourClass> ProgressList
{
get { return _ProgressList; }
set
{
_ProgressList= value;
Progress = ProgressList.Where(x => x.Done).Count();
NotifyPropertyChanged();
}
}
Note : UpdateSourceTrigger works from Target(View) To Source(VM)

Using binding path=. is disconnecting my binding

I am using radio buttons which has the datacontext set to a observabelcollection of enums. When i am binding my radio buttons with path set to dot as shown below, data binding works for the first time the app comes up but then the data binding fails. If anyone knows why???
<RadioButton Content="No Model" FontSize="16" IsChecked= "{Binding Path=SisoModel, Mode=TwoWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:SISOModels.NoModel}}"/>
<RadioButton Content="Prediction Only" FontSize="16" IsChecked="{Binding Path=SisoModel, Mode=TwoWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:SISOModels.PredictionOnly}}"/>
<RadioButton Content="Prediction And Control" FontSize="16" IsChecked="{Binding Path=SisoModel, Mode=TwoWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:SISOModels.PredictionAndControl}}"/>
conversion code is here:
[ValueConversion(typeof(Enum), typeof(bool))]
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || parameter == null) return false;
string enumValue = value.ToString();
string targetValue = parameter.ToString();
bool outputValue = enumValue.Equals(targetValue, StringComparison.InvariantCultureIgnoreCase);
return outputValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || parameter == null) return null;
bool useValue = (bool)value;
string targetValue = parameter.ToString();
if (useValue) return Enum.Parse(targetType, targetValue);
return null;
}
}
model and view mode code is here:
public enum SISOModels
{
NoModel,
PredictionOnly,
PredictionAndControl
};
public class SisoModels1 : BindableBase
{
public SisoModels1(SISOModels _SisoModel)
{
SisoModel = _SisoModel;
}
public SISOModels SisoModel { get; set; }
}
within a for loop based on size of grid i have written below code, which will add these radio buttn user control(additionalSetup) into all cells of grid and set the datacontext:
AdditionalSetup a1 = new AdditionalSetup();
a1.DataContext = vm.sisoModelList[ct];
ct++;
reason for going with observable collection is that we have to populate these radio buttons in all the cells of a grid and the grid size is available runtime. compile time am not sure how many radio buttons i am going to populate.
Without being able to test, its hard to say for sure; but I'm pretty confident that the return null at the end of ConvertBack is killing your bindings.
The correct ValueEquals (Or EnumToBoolean) converter is:
public Convert(...)
{
return value.Equals(parameter);
}
public ConvertBack(...)
{
if ((bool)value)
return parameter;
else
return Binding.DoNothing; //Not null!!!!
}
Enum types are value types, so unless you put a nullable wrapper around them, a null check is not usually necessary (feel free to add though).
More importantly, you should not be returning null out of this converter. Use Binding.DoNothing instead.
If you are binding directly against the ObservableCollection I'm guessing that is causing you a few issues as well, since it is obviously not an enum.

WPF Databinding fails at binding entire object

In the following DataTemplate, the first binding doesn't work while the 2nd one works, and I would like to know why.
<local:IsEnabledConverter x:Key="isEnabled"/>
<local:Boolean2TextConverter x:Key="txtConverter"/>
<DataTemplate x:Key="fileinfoTemplate" DataType="{x:Type local:MyFileInfo}">
<StackPanel>
<Label x:Name="1stLabel" Content="{Binding Path=Filename}" IsEnabled="{Binding Path=., Converter={StaticResource isEnabled}}"/> <--- doesn't work
<Label x:Name="2ndLabel" Content="{Binding Path=IfPrint, Converter={StaticResource txtConverter}}" IsEnabled="{Binding Path=IsChecked, ElementName=ckBox}"/> <--- works
<CheckBox x:Name="ckBox" IsChecked="{Binding Path=IfPrint}" IsEnabled="{Binding Path=IsValid}" Style="{StaticResource printCkBox}"/>
</StackPanel>
</DataTemplate>
IsEnabledConverter:
class IsEnabledConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
MyFileInfo f = value as MyFileInfo;
return f.IsValid && f.IfPrint;
}
//... omit ConvertBack NotImplementedException stuff
}
Boolean2TextConverter:
class IsEnabledConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
Boolean b = (Boolean)value;
return b.ToString();
}
//similarly omit ConvertBack here
}
Code for MyFileInfo:
public class MyFileInfo {
public string IfPrint {
get;
set;
}
public string IsValid {
get;
set;
}
...
}
Problem: When the CheckBox is toggled, the 2nd Label grays out and shows "false", or becomes normal and shows "true", as it should. However, the first Label doesn't change at all; its IsEnabled state is supposed be the conjunction of two Booleans, one of which is changed by the CheckBox. What is wrong? (note that the IsEnabledConverter is called once upon GUI initialization, but not called again when its binding source changes.)
There are 2 issues here. First you have to implement INotifyPropertyChanged for the ViewModel MyFileInfo. Secondly you have to use MultiBinding here. Because I don't think we have some way to trigger updating the target (such as when toggling the CheckBox) if you bind the whole view model to the IsEnabled target. So here is how it should be done:
Your view model:
public class MyFileInfo : INotifyPropertyChanged {
bool _ifPrint;
bool _isValid;
public bool IfPrint {
get { return _ifPrint; }
set {
if(_ifPrint != value) {
_ifPrint = value;
OnPropertyChanged("IfPrint");
}
}
}
public bool IsValid {
get { return _isValid; }
set {
if(_isValid != value) {
_isValid = value;
OnPropertyChanged("IsValid");
}
}
}
//Implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string prop){
var handler = PropertyChanged;
if(handler != null) handler(this, new PropertyChangedEventArgs(prop));
}
//.... should do the same for the remaining properties....
//...
}
Here is the converter used for MultiBinding, which should implement IMultiValueConverter (instead of IValueConverter):
class IsEnabledConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture) {
if(values.Length == 2){
return (bool) values[0] && (bool) values[1];
}
return false;
}
//... omit ConvertBack NotImplementedException stuff
}
Here is the modifed XAML (to use MultiBinding instead):
<Label x:Name="firstLabel" Content="{Binding Path=Filename}">
<Label.IsEnabled>
<MultiBinding Converter="{StaticResource isEnabled}">
<Binding Path="IsValid"/>
<Binding Path="IfPrint"/>
</MultiBinding>
</Label.IsEnabled>
</Label>
Now one of IsValid and IfPrint changing will trigger the MultiBinding's Converter. Here you can also bind to IsChecked of the CheckBox directly instead of indirectly via IfPrint.
PS: Note Name used in XAML (as well as in codebehind) must not start with number.
Since the instance of MyFileInfo does not change while you check/uncheck the checkbox hence IsEnabledConverteris not getting called.
In order to Enable/Disable your 1stLabel depending on two properties, either use MultiValueConverter or use MultiDataTrigger by applying Style to your Label.

Binding radio button to enum property

I think I've followed the examples given in this post but my property is not changing when button are changed. Any suggestions on where I went wrong?
C# code for enum and class
public enum SystemTypes
{
TypeA,
TypeB
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
SystemTypes systemType = SystemTypes.TypeA;
public SystemTypes SystemType
{
get { return systemType; }
set { systemType = value; }
}
}
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
xaml
<Canvas>
<Canvas.Resources>
<local:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
</Canvas.Resources>
<RadioButton x:Name="TypeARadioButton" Content="TypeA" Canvas.Left="10" Canvas.Top="10"
IsChecked="{Binding Path=SystemType, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:SystemTypes.TypeA}}" />
<RadioButton x:Name="TypeBRadioButton" Content="TypeB" Canvas.Left="10" Canvas.Top="31"
IsChecked="{Binding Path=SystemType, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:SystemTypes.TypeB}}" />
</Canvas>
You need to set Binding Mode to TwoWay, then in Converter implement method ConvertBack responsible for converting bool to SystemTypes, in settter of SystemType include
set { systemType = value; OnPropertyChanged(() => "SystemType");}
in order to fill property in that its value was changed.
OnPropertyChanged(() => "SystemType")
can work if you implement interface INotifyPropertyChanged. I cannot you whether you set DataContext, if you did not binding is not working. In order to rectify this after InitializeComponent() add
this.DataContext = this;

How to set a String ViewModel property on ReadioButton Check usin WPF?

I am new to WPF and here I am trying to set a simple string property of my viewModel when a particular radio button is checked on my window.
class ViewModel
{
string LanguageSettings {get;set;}
}
XAML looks like following:
<RadioButton Name="OptionEnglish" GroupName="LanguageOptions" IsChecked="{Binding LanguageSettings, Converter={StaticResource Converter}, ConverterParameter=English}" Content="English" HorizontalAlignment="Right" Width="760" />
<RadioButton Name="OptionChinese" GroupName="LanguageOptions" IsChecked="{Binding LanguageSettings, Converter={StaticResource Converter}, ConverterParameter=Chinese}" Content="Chinese" />
I have implemented the IValueConverted which looks like below:
public class BoolInverterConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
return !(bool)value;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
return !(bool)value;
}
return value;
}
#endregion
}
Probably I am not properly understanding the utility of IValueConverter. I think if I change it appropriately it might work.
All I want here is that when English is selected I want Language Settings to be set as English and same for Chinese. Is there any simple way to do it? is there any straightforward way to set that property?
So i do have two options
1.Change my BoolConverterImplementation
2. Find another easier way to do it.
Any ideas?
An approach that does not require a converter:
<RadioButton Content="Chinese" IsChecked="{Binding IsChinese}"/>
<RadioButton Content="English" IsChecked="{Binding IsEnglish}"/>
ViewModel:
public class LanguageSelectorViewModel
{
private bool _isChinese;
private bool _isEnglish;
public bool IsChinese
{
get { return _isChinese; }
set
{
_isChinese = value;
if (value)
SelectedLanguage = "Chinese";
}
}
public bool IsEnglish
{
get { return _isEnglish; }
set
{
_isEnglish = value;
if (value)
SelectedLanguage = "English";
}
}
public string SelectedLanguage { get; set; }
}
You should look into implementing IValueConverter. Below is a starting example for what I believe you are looking for:
public class StringMatchConverter : IValueConverter
{
public object Convert(object value,Type targetType,object parameter,CultureInfo culture)
{
return value == parameter;
}
public object ConvertBack(object value,Type targetType,object parameter,CultureInfo culture)
{
throw new NotImplementedException();
}
}
Example usage:
<!-- Somewhere in resources -->
<conv:StringMatchConverter x:Key="Conv" />
<!-- Somewhere in application -->
<RadioButton Name="OptionEnglish" GroupName="LanguageOptions" IsChecked="{Binding LanguageSettings,Converter={StaticResource Conv}, ConverterParameter=English}" Content="English" HorizontalAlignment="Right" Width="760" />
<RadioButton Name="OptionChinese" GroupName="LanguageOptions" IsChecked="{Binding LanguageSettings,Converter={StaticResource Conv}, ConverterParameter=Chinese}" Content="Chinese" />
I'm not sure but if I understand you correct you maybe could use a IValueConverter.
One way to do it would be to create an attached property:
public class MyAttachedProperty
{
public static readonly DependencyProperty IsCheckedToStrProperty =
DependencyProperty.RegisterAttached("IsCheckedToStr", typeof (string), typeof (MyAttachedProperty), new PropertyMetadata(default(string,IsCheckedToStr)))
private static void IsCheckedToStr(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RadioButton radio = d as RadioButton;
radio.Checked+=radio_Checked;
}
private static void radio_Checked(object sender, RoutedEventArgs e)
{
RadioButton radio = sender as RadioButton;
if (radio.IsChecked == true)
{
SetIsCheckedToStr(radio, radio.Content.ToString());
}
}
public static void SetIsCheckedToStr(UIElement element, string value)
{
element.SetValue(IsCheckedToStrProperty, value);
}
public static string GetIsCheckedToStr(UIElement element)
{
return (string) element.GetValue(IsCheckedToStrProperty);
}
}
And then you can use it in your xaml like this:
<RadioButton Name="OptionEnglish" GroupName="LanguageOptions" local:MyAttachedProperty.IsCheckedStr="{Binding LanguageSettings}" Content="English" HorizontalAlignment="Right" Width="760" />
<RadioButton Name="OptionChinese" GroupName="LanguageOptions" local:MyAttachedProperty.IsCheckedStr="{Binding LanguageSettings}" Content="Chinese" />

Categories