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" />
Related
I’m trying to learn XAML (without much success) – be gentle.
I have a toggle button, a slider and a button. This is what I am trying to do:
When the slider’s value is changed the toggle button should turn off – works fine.
When the button is pressed, set the slider value and keep the toggle button in whichever state it is – does not work; after I push the button, (1) does not work anymore. It behaves like the IsChecked binding was cleared.
Ideally I would like to write everything in XAML, but I guess it is not possible.
// MainWindow.xaml.cs
public class ValueToFalse : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var saved = toggle.IsChecked;
slider.Value = 2;
toggle.IsChecked = saved;
}
}
<!--App.xaml-->
<local:ValueToFalse x:Key="ValueToFalse"/>
<!--MainWindow.xaml-->
<ToggleButton
Name="toggle"
Content="toggle"
HorizontalAlignment="Left"
Margin="479,64,0,0"
VerticalAlignment="Top"
Height="108"
Width="192"
IsChecked="{Binding Value, Mode=OneWay, ElementName=slider, Converter={StaticResource ValueToFalse} }"
/>
<Slider
Name="slider"
HorizontalAlignment="Left"
Margin="167,105,0,0"
VerticalAlignment="Top"
Width="233"
Height="35"
/>
<Button
Content="Button"
HorizontalAlignment="Left"
Margin="346,340,0,0"
VerticalAlignment="Top"
Click="Button_Click"
/>
I didn't quite understand what you want to implement.
But nevertheless, you have an obvious mistake that #Clemens pointed out - if you need to assign a Dependency Property value and keep the binding from it, then the binding must be in the direction of the TwoWay or OneWaySource source.
Since you do not need to assign a value to the source, but you need to store the direction to the source in the binding, you can solve this by implementing a converter.
I show the full implementation of the converter with markup extension. This may be useful to you in the future.
[ValueConversion(typeof(object), typeof(bool))]
public class ValueToFalseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// This value tells the binding that the result of the conversion should not be assigned.
return Binding.DoNothing;
}
private ValueToFalseConverter() { }
public static ValueToFalseConverter Instance { get; } = new ValueToFalseConverter();
}
[MarkupExtensionReturnType(typeof(ValueToFalseConverter))]
public class ValueToFalseExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ValueToFalseConverter.Instance ;
}
}
<ToggleButton
----------
----------
IsChecked="{Binding Value,
Mode=TwoWay,
ElementName=slider,
Converter={local:ValueToFalse}}"
/>
I have a property on my ViewModel that is an enum:
ViewModel:
public MyViewModel {
// Assume this is a DependancyProperty
public AvailableTabs SelectedTab { get; set; }
// Other bound properties
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public enum AvailableTabs {
Tab1,
Tab2,
Tab3
}
I'd like to be able to bind SelectedIndex (or SelectedItem) of my TabControl to this property and have it correctly set the appropriate tab using a converter. Unfortunately, I'm a bit stuck. I know I can easily just use the SelectedIndex in my model, but I want the flexibility of re-ordering the tabs without breaking anything. I've given each TabItem a Tag property of the applicable enum value.
My XAML:
<TabControl Name="MyTabControl" SelectedIndex="{Binding SelectedTab, Converter={StaticResource SomeConverter}}">
<TabItem Header="Tab 1" Tag="{x:Static local:AvailableTabs.Tab1}">
<TextBlock Text="{Binding Property1}" />
</TabItem>
<TabItem Header="Tab 2" Tag="{x:Static local:AvailableTabs.Tab2}">
<TextBlock Text="{Binding Property2}" />
</TabItem>
<TabItem Header="Tab 3" Tag="{x:Static local:AvailableTabs.Tab3}">
<TextBlock Text="{Binding Property3}" />
</TabItem>
</TabControl>
My problem is that I can't figure out how to get the TabControl into my converter so I can do:
// Set the SelectedIndex via the enum (Convert)
var selectedIndex = MyTabControl.Items.IndexOf(MyTabControl.Items.OfType<TabItem>().Single(t => (AvailableTabs) t.Tag == enumValue));
// Get the enum from the SelectedIndex (ConvertBack)
var enumValue = (AvailableTabs)((TabItem)MyTabControl.Items[selectedIndex]).Tag;
I'm afraid I might be overthinking it. I tried using a MultiValue converter without much luck. Any ideas?
You simply need a converter that casts the value to an index.
public class TabConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
{
return (int)value;
}
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
{
return (AvailableTabs)value;
}
}
Instead of specifying the values in the XAML, I would bind ItemsSource to an array of values from your enum:
Code:
public AvailableTabs[] AvailableTabs => Enum.GetValues(typeof(AvailableTabs Enum)).Cast<AvailableTabs>().ToArray();
XAML:
<TabControl Name="MyTabControl" SelectedIndex="{Binding SelectedTab}" ItemsSource="{Binding AvailableTabs}" />
The important thing is that the solution should work if I change the tab order in the TabControl
if this is not important then the elements in the enum type
and the views in the TabControl have to be in the same order
the conversion in this case is to cast the enum value to int value
I name the views in TabControl according to enum values
AppTab.ValidDates (enum value) corresponds to validDatesView (view name)
enum values
public enum AppTab
{
Parameters, ValidDates, ...
}
views in TabControl
<TabControl Name="myTabControl" SelectedIndex="{Binding SelectedTab, Converter={c:AppTabToIntConverter}}"
IsEnabled="{Binding IsGuiEnabled}">
<TabItem Header="{x:Static r:Resource.Parameters}">
<view:ParametersView x:Name="parametersView"/>
</TabItem>
<TabItem Header="{x:Static r:Resource.Dates}">
<view:ValidDatesView x:Name="validDatesView"/>
</TabItem>
fill up ViewNameIndexDictionary and IndexViewNameDictionary
Window_Loaded event is too late, AppTabToIntConverter runs before that
public static Dictionary<AppTab, int> ViewNameIndexDictionary { get; set; } = new Dictionary<AppTab, int>();
public static Dictionary<int, AppTab> IndexViewNameDictionary { get; set; } = new Dictionary<int, AppTab>()
private void Window_Initialized(object sender, EventArgs e)
{
var i = 0;
foreach (TabItem item in myTabControl.Items)
{
var tabContentName = ((FrameworkElement)item.Content).Name;
// Convert TabItem name "validDatesView" to "ValidDates"
var appTabString = tabContentName.FirstCharToUpper().CutLastNCharacter("View".Length);
var appTab = (AppTab)Enum.Parse(typeof(AppTab), appTabString);
ViewNameIndexDictionary.Add(appTab, i);
IndexViewNameDictionary.Add(i, appTab);
i++;
}
}
the converter
// The root tag must contain: xmlns:c="clr-namespace:LedgerCommander.ValueConverter"
class AppTabToIntConverter : BaseValueConverter<AppTabToIntConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is AppTab appTab))
throw new Exception("The type of value is not AppTab");
return MainWindowView.ViewNameIndexDictionary[appTab];
}
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is int tabIndex))
throw new Exception("The type of value is not int");
return MainWindowView.IndexViewNameDictionary[tabIndex];
}
}
the BaseValueConverter (thanks to AngelSix)
public abstract class BaseValueConverter<T> : MarkupExtension, IValueConverter where T : class, new()
{
private static T Converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Converter ?? (Converter = new T());
}
public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
public abstract object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
}
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.
I want to set Visibility for ToolTip (DependencyProperty) inside of IValueConverter using special logic. I want to show/hide ToolTip only when I have the special condition.
How can I do this?
</UserControl.Resources>
<converters:ToolTipMessageConverter x:Key="ToolTipMessageConverter" />
</UserControl.Resources>
<telerik:RadGridView ItemsSource="{Binding Data}" AutoGenerateColumns="False">
<telerik:GridViewDataColumn DataMemberBinding="{Binding DataField}">
<telerik:GridViewDataColumn.ToolTipTemplate>
<TextBlock Text="{Binding OtherData,Converter={StaticResource ToolTipMessageConverter}}" Visibility=??? />
</telerik:GridViewDataColumn.ToolTipTemplate>
</telerik:GridViewDataColum>
<telerik:RadGridView>
public class ToolTipMessageConverter : FrameworkElement, IValueConverter
{
public Visibility ToolTipVisibility
{
get { return (Visibility)GetValue(ToolTipVisibilityProperty); }
set { SetValue(ToolTipVisibilityProperty, value); }
}
public static readonly DependencyProperty ToolTipVisibilityProperty =
DependencyProperty.Register("ToolTipVisibility", typeof(Visibility), typeof(ToolTipMessageConverter), new PropertyMetadata(Visibility.Visible));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var status = (string)value;
var mess = string.Empty;
if (status == "Available")
{
this.SetValue(ToolTipVisibilityProperty, Visibility.Hidden);
}
else
{
mess = "User message... " + value;
this.SetValue(ToolTipVisibilityProperty, Visibility.Visible);
}
return mess;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
So it should work:
<TextBlock Text="{Binding OtherData,Converter={StaticResource ToolTipMessageConverter}}" Visibility="{Binding ToolTipVisibility, Source={StaticResource ToolTipMessageConverter}}" />
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;