WPF Binding with converter does not work - wpf

I am trying to bind some attributes in XAML with enum type.
How it should work: I have some radio buttons in a menu bar, that set my enum value. And this enum value sets isEnabled attribute in Grid. So there is a relation: radiobutton ->(EnumToBooleanConverter)-> enum object ->(EnumToIsActiveCnoverter)-> isEnabled attribute. I have written two converters to perform that binding.
Code:
<Window.Resources>
<local:EnumToBooleanConverter x:Key="actionConverter" />
<local:EnumToIsActiveConverter x:Key="activityConverter" />
</Window.Resources>
...
<MenuItem Header="Settings">
<MenuItem Header="Action">
<MenuItem Header="Draw">
<MenuItem.Icon>
<RadioButton GroupName="MenuActionButton"
IsChecked="{Binding Path=appMode,
Converter={StaticResource actionConverter},
ConverterParameter={x:Static local:ApplicationMode.Draw}}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Edit">
<MenuItem.Icon>
<RadioButton GroupName="MenuActionButton"
IsChecked="{Binding Path=appMode,
Converter={StaticResource actionConverter},
ConverterParameter={x:Static local:ApplicationMode.Edit}}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Constraints">
<MenuItem.Icon>
<RadioButton GroupName="MenuActionButton"
IsChecked="{Binding Path=appMode,
Converter={StaticResource actionConverter},
ConverterParameter={x:Static local:ApplicationMode.Constraints}}"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</MenuItem>
CS file:
public partial class MainWindow : Window
{
public ApplicationMode appMode { get; set; }
public MainWindow()
{
this.appMode = ApplicationMode.Draw;
InitializeComponent();
}
}
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//return value.Equals(parameter);
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
public class EnumToIsActiveConverter : 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;
return Binding.DoNothing;
}
}
public enum ApplicationMode
{
Draw,
Edit,
Constraints
}
EnumToBooleanConverter changes enum value to a given parameter when radiobutton is checked and EnumToIsActiveConverter changes isEnabled attribute of a given element when enum value is set to a given parameter.
It seems to me it should be working just fine. What am I missing in here?

More simple solution, without RadioButton:
a. use the MenuItem Checkable feature for the icon job, and bind the IsChecked to appMode property:
<MenuItem Header="Action">
<MenuItem Header="Draw" IsCheckable="True"
IsChecked="{Binding Path=appMode,
Converter={StaticResource actionConverter},
ConverterParameter={x:Static local:ApplicationMode.Draw}}" />
<MenuItem Header="Edit" IsCheckable="True"
IsChecked="{Binding Path=appMode,
Converter={StaticResource actionConverter},
ConverterParameter={x:Static local:ApplicationMode.Edit}}" />
</MenuItem>
b. in the code, change the property to trigger the PropertyChange event:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ApplicationMode _appMode;
public ApplicationMode appMode
{
get { return _appMode; }
set
{
_appMode = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(appMode)));
}
}
public MainWindow()
{
this.appMode = ApplicationMode.Draw;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
}
c. in the converter fix the ConvertBack metode:
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 parameter;
}
}

You are using a RadioButton control, But only as an indicator.
So clicking on the item does not change the property value because only the MenuItem receive the Click, not the RadioButton.
also it's a bit of a waste, because for V (current active indication) you do not need a RadioButton control, quite a Path with visibility binding.
I will present you with a solution in the existing way:
a. add Click event to each MenuItem
b. put the relevant Value in Tag property (This is for the simplicity of the event as you will see)
c. add to your MainWindow class implement of INotifyPropertyChanged interface to alow XAML know the appMode modofication.
here the code, XAML:
<MenuItem Header="Draw" Click="MenuItem_Click" Tag="{x:Static local:ApplicationMode.Draw}" >
<MenuItem.Icon>
<RadioButton GroupName="MenuActionButton"
IsChecked="{Binding Path=appMode,
Converter={StaticResource actionConverter},
ConverterParameter={x:Static local:ApplicationMode.Draw}}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Edit" Click="MenuItem_Click" Tag="{x:Static local:ApplicationMode.Edit}">
<MenuItem.Icon>
<RadioButton GroupName="MenuActionButton"
IsChecked="{Binding Path=appMode,
Converter={StaticResource actionConverter},
ConverterParameter={x:Static local:ApplicationMode.Edit}}"/>
</MenuItem.Icon>
</MenuItem>
the MenuItem_Click for Click event:
public event PropertyChangedEventHandler PropertyChanged;
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
var item = (MenuItem)sender;
appMode = (ApplicationMode) item.Tag;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(appMode)));
}

Related

Radio button checked show image1, unchecked show image2 wpf xmal style

I am using "BooleanToVisibilityConverter" to show one image when radio button is checked. Is their any way I can use same sort of style show another image when this radio button is unchecked?
<BooleanToVisibilityConverter x:Key="VisibilityConverter" />
<RadioButton Name="rbttest" IsChecked="True" GroupName="rbtMenuGroup"
Style="{StaticResource RadioButtonMenuStyle}" >
<WrapPanel>
<Image Source="/wpf1;component/Images/test1.png"
Visibility="{Binding IsChecked=false ???}" Style="{StaticResource MenuIconStyle}" />
<Image Source="/wpf;component/Images/test2.png"
Visibility="{Binding IsChecked, Converter={StaticResource
VisibilityConverter},ElementName = rbttest}"
Style="{StaticResource MenuIconStyle}" ></Image>
<TextBlock Text="This is testing" />
</WrapPanel>
</RadioButton>
I found a solution for the time being before I study the MVVM, DataBinding, DependencyProperty, ControlTemplates.
I have added the BooleanToVisibilityConverter class which allows reverse, I mean hide/collapse control when true and visible when false !
using System;
using System.Windows.Data;
using System.Windows;
namespace TestBooleanToVisibilityConverter
{
class BoolToVisibleOrHidden : IValueConverter
{
#region Constructors
/// <summary>
/// The default constructor
/// </summary>
public BoolToVisibleOrHidden() { }
#endregion
#region Properties
public bool Collapse { get; set; }
public bool Reverse { get; set; }
#endregion
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
bool bValue = (bool)value;
if (bValue != Reverse)
{
return Visibility.Visible;
}
else
{
if (Collapse)
return Visibility.Collapsed;
else
return Visibility.Hidden;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Visible)
return !Reverse;
else
return Reverse;
}
#endregion
}
}
Now you can reverse this very easily in the XAML.
<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" Reverse="True"
/>
<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter2" Collapse="True"
Reverse="false" />
---------------------------------------------------------------------------------
<RadioButton Name="rbttest" IsChecked="True" GroupName="rbtMenuGroup"
Style="{StaticResource RadioButtonMenuStyle}" >
<WrapPanel>
<--Show the image when radio button unchecked-->
<Image Source="/wpf1;component/Images/test1.png"
Visibility="{Binding IsChecked, Converter={StaticResource
BoolToVisConverter},ElementName = rbttest}" Style="{StaticResource MenuIconStyle}"
/>
<--Show the image when radio button checked-->
<Image Source="/wpf1;component/Images/test2.png"
Visibility="{Binding IsChecked, Converter={StaticResource
BoolToVisConverter2},ElementName = rbttest}"
Style="{StaticResource MenuIconStyle}" ></Image>
<TextBlock Text="This is testing" />
</WrapPanel>
Reference : http://www.rhyous.com/2011/02/22/binding-visibility-to-a-bool-value-in-wpf/

How to reference another control in a TreeViewItem through a binding converter?

I would like to bind the Visibility of a TextBox based on SelectedItem of a ComboBoxin same TreeViewItemContainer. I think I can use a Converter for the Binding but I don't know how to send the ComboBox item as a parameter of the TextBox Binding. Can this be done?
<TreeView>
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Margin="2,0" Name="SkillSelectCB" ItemsSource="{Binding PotentialChildren}" />
<TextBox Margin="2,0" Width="50" Visibility="{Binding ??}" />
<Button Margin="2,0" Content="Add" />
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
This is actually in a HierarchicalDataTemplate, the example above is very minimal. The "Add" Button will add new children to the ViewModel for the TreeView based on what's selected in the ComboBox. And the visibility is the TextBox will change depending on some property of the ComboBox's SelectedItem.
So the Xaml for the TextBox:
<TextBox Margin="2,0"Width="50" Visibility="{Binding SelectedItem, ElementName=SkillSelectCB, Converter={StaticResource SkillToVisibilityConverter}}" />
And the Converter:
public class SkillToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = (Skill)value;
return (s == null || !s.Specialized) ? "Hidden" : "Visible";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Silverlight binding visibility to parent class property

I have problem with binding visibility in listbox-item template with property in parent object. Here is a little snippet from custom xaml style template:
<!-- DATA BINDING ITEM TEMPLATE -->
<StackPanel Orientation="Vertical">
<TextBlock Height="19"
....
Text="{Binding InfoTop}"/>
<Rectangle Height="1"
....
Visibility="{Binding _linesVisibility[0], RelativeSource={RelativeSource AncestorType=my:PatientsList}}"/>
<TextBlock Height="19"
....
Text="{Binding InfoMiddle}"
Visibility="{Binding _linesVisibility[0], ElementName=patientsControl}"/>
<Rectangle Height="1"
....
Visibility="{Binding _linesVisibility[1]}"/>
<TextBlock Height="19"
....
Text="{Binding InfoBottom}"
Visibility="{Binding _linesVisibility[1]}"/>
</StackPanel>
I managed to bind Text value by assigning ItemsSource in code file but i can't bind Visibility. As you can see i tried some different ideas but none of them work.
I have public variable public Visibility[] _linesVisibility = new Visibility[2]; in my custom control. This control contains listbox with custom style as above. How to bind properly my _linesVisibility to listbox-item style ?
You can't bind directly to an array:
Visibility="{Binding _linesVisibility[1]}"
This will not work.
You need to bind to a property and your class needs to implement INotifyPropertyChanged:
private Visibility backingVariable;
public Visbilility PublicProperty
{
get { return backingVariable; }
set
{
backingVariable = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("PublicVariable"));
}
}
}
It doesn't have to be a property of type Visibility. It can be any type as long as you bind through a converter that returns Visibility:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool visibility = (bool)value;
return visibility ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility visibility = (Visibility)value;
return (visibility == Visibility.Visible);
}
}
Usage:
Visibility="{Binding SomeBoolean, Converter={StaticResource boolToVisibilityConverter}}"
where the converter is declared in XAML like this:
<UserControl.Resources>
<globalConverters:BoolToVisibilityConverter x:Key="boolToVisibilityConverter" />
</UserControl.Resources>

binding radiobuttons group to a property in WPF

Let's imagine that I have:
<RadioButton GroupName="Group1" IsChecked="{Binding Path=RadioButton1IsChecked}" />
<RadioButton GroupName="Group1" IsChecked="{Binding Path=RadioButton2IsChecked}" />
And then in my data source class I have:
public bool RadioButton1IsChecked { get; set; }
public bool RadioButton2IsChecked { get; set; }
public enum RadioButtons { RadioButton1, RadioButton2, None }
public RadioButtons SelectedRadioButton
{
get
{
if (this.RadioButtonIsChecked)
return RadioButtons.RadioButton1;
else if (this.RadioButtonIsChecked)
return RadioButtons.RadioButton2;
else
return RadioButtons.None;
}
}
Can I somehow bind my radio buttons directly to SelectedRadioButton property? I really need RadioButton1IsChecked and RadioButton2IsChecked properties only to calculate the selected radiobutton.
Declare an enumeration similar to:
enum RadioOptions {Option1, Option2}
XAML:
<RadioButton IsChecked="{Binding SelectedOption, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:RadioOptions.Option1}}"/>
<RadioButton IsChecked="{Binding SelectedOption, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:RadioOptions.Option2}}"/>
Converter class:
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? parameter : Binding.DoNothing;
}
}
<RadioButton GroupName="Group1" IsChecked="{Binding Path=SelectedRadioButton, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=RadioButton1}" />
<RadioButton GroupName="Group1" IsChecked="{Binding Path=SelectedRadioButton, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=RadioButton2}" />
public enum RadioButtons { RadioButton1, RadioButton2, None }
public RadioButtons SelectedRadioButton {get;set;}
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var ParameterString = parameter as string;
if (ParameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object paramvalue = Enum.Parse(value.GetType(), ParameterString);
return paramvalue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var ParameterString = parameter as string;
var valueAsBool = (bool) value;
if (ParameterString == null || !valueAsBool)
{
try
{
return Enum.Parse(targetType, "0");
}
catch (Exception)
{
return DependencyProperty.UnsetValue;
}
}
return Enum.Parse(targetType, ParameterString);
}
}
We can create the radio buttons dynamically, ListBox can help us do that, without converters, quite simple.
The advantage is below:
if someday your enum class changes, you do not need to update the GUI (XAML file).
The steps are below:
create a ListBox and set the ItemsSource for the listbox as the enum and binding the SelectedItem of the ListBox to the Selected property.
Then the Radio Buttons for each ListBoxItem will be created.
public enum RadioButtons
{
RadioButton1,
RadioButton2,
None
}
Step 1: add the enum to static resources for your Window, UserControl or Grid etc.
<Window.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type system:Enum}"
x:Key="RadioButtons">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:RadioButtons" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Step 2: Use the List Box and Control Template to populate each item inside as Radio button
<ListBox ItemsSource="{Binding Source={StaticResource RadioButtons}}" SelectedItem="{Binding SelectedRadioButton, Mode=TwoWay}" >
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<RadioButton
Content="{TemplateBinding ContentPresenter.Content}"
IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource TemplatedParent},
Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
Now, enjoy~
References:
https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/
XAML:
<RadioButton IsChecked="{Binding Path=SelectedOption, UpdateSourceTrigger=PropertyChanged}">Option1</RadioButton>
<RadioButton IsChecked="{Binding Path=SelectedOption, UpdateSourceTrigger=PropertyChanged, Converter={v:NotBoolenConverter}}">Option2</RadioButton>
Converter:
public class NotBoolenConverter : IValueConverter
{
public NotBoolenConverter()
{
}
public override object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
bool output = (bool)value;
return !output;
}
public override object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
bool output = (bool)value;
return !output;
}
}
Works with 2 radio buttons, by binding one to the opposite of the other.

WPF MVVM Radio buttons on ItemsControl

I've bound enums to radio buttons before, and I generally understand how it works. I used the alternate implementation from this question: How to bind RadioButtons to an enum?
Instead of enumerations, I'd like to generate a runtime-enumerated set of a custom type and present those as a set of radio buttons. I have gotten a view working against a runtime-enumerated set with a ListView, binding to the ItemsSource and SelectedItem properties, so my ViewModel is hooked up correctly. Now I am trying to switch from a ListView to a ItemsControl with radio buttons.
Here's as far as I've gotten:
<Window.Resources>
<vm:InstanceToBooleanConverter x:Key="InstanceToBooleanConverter" />
</Window.Resources>
<!-- ... -->
<ItemsControl ItemsSource="{Binding ItemSelections}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ISomeType}">
<RadioButton Content="{Binding Name}"
IsChecked="{Binding Path=SelectedItem, Converter={StaticResource InstanceToBooleanConverter}, ConverterParameter={Binding}}"
Grid.Column="0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
InstanceToBooleanConverter has the same implementation as EnumToBooleanConverter from that other question. This seems right, since it seems like it just invokes the Equals method:
public class InstanceToBooleanConverter : 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;
}
}
The problem I am getting now is that I can't figure out how to send a runtime value as the ConverterParameter. When I try (with the code above), I get this error:
A 'Binding' cannot be set on the 'ConverterParameter' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
Is there a way to bind to the item instance, and pass it to the IValueConverter?
It turns out that it is much simpler to abandon using ItemsControl and instead go with ListBox.
It may be more heavy-weight, but that's mostly because it is doing the heavy lifting for you. It is really easy to do a two-way binding between RadioButton.IsChecked and ListBoxItem.IsSelected. With the proper control template for the ListBoxItem, you can easily get rid of all the selection visual.
<ListBox ItemsSource="{Binding Properties}" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemContainerStyle>
<!-- Style to get rid of the selection visual -->
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:SomeClass}">
<RadioButton Content="{Binding Name}" GroupName="Properties">
<!-- Binding IsChecked to IsSelected requires no support code -->
<RadioButton.IsChecked>
<Binding Path="IsSelected"
RelativeSource="{RelativeSource AncestorType=ListBoxItem}"
Mode="TwoWay" />
</RadioButton.IsChecked>
</RadioButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As far as I know, there's no good way to do this with a MultiBinding, although you initially think there would be. Since you can't bind the ConverterParameter, your ConvertBack implementation doesn't have the information it needs.
What I have done is created a separate EnumModel class solely for the purpose of binding an enum to radio buttons. Use a converter on the ItemsSource property and then you're binding to an EnumModel. The EnumModel is just a forwarder object to make binding possible. It holds one possible value of the enum and a reference to the viewmodel so it can translate a property on the viewmodel to and from a boolean.
Here's an untested but generic version:
<ItemsControl ItemsSource="{Binding Converter={StaticResource theConverter} ConverterParameter="SomeEnumProperty"}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton IsChecked="{Binding IsChecked}">
<TextBlock Text="{Binding Name}" />
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The converter:
public class ToEnumModelsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var viewmodel = value;
var prop = viewmodel.GetType().GetProperty(parameter as string);
List<EnumModel> enumModels = new List<EnumModel>();
foreach(var enumValue in Enum.GetValues(prop.PropertyType))
{
var enumModel = new EnumModel(enumValue, viewmodel, prop);
enumModels.Add(enumModel);
}
return enumModels;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The EnumModel:
public class EnumModel : INPC
{
object enumValue;
INotifyPropertyChanged viewmodel;
PropertyInfo property;
public EnumModel(object enumValue, object viewmodel, PropertyInfo property)
{
this.enumValue = enumValue;
this.viewmodel = viewmodel as INotifyPropertyChanged;
this.property = property;
this.viewmodel.PropertyChanged += new PropertyChangedEventHandler(viewmodel_PropertyChanged);
}
void viewmodel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == property.Name)
{
OnPropertyChanged("IsChecked");
}
}
public bool IsChecked
{
get
{
return property.GetValue(viewmodel, null).Equals(enumValue);
}
set
{
if (value)
{
property.SetValue(viewmodel, enumValue, null);
}
}
}
}
For a code sample that I know works (but it's still quite unpolished - WIP!), you can see http://code.google.com/p/pdx/source/browse/trunk/PDX/PDX/Toolkit/EnumControl.xaml.cs. This only works within the context of my library, but it demonstrates setting the Name of the EnumModel based on the DescriptionAttribute, which might be useful to you.
You are so close. When you are need two bindings for one converter you need a MultiBinding and a IMultiValueConverter! The syntax is a little more verbose but no more difficult.
MultiBinding Class
IMultiValueConverter Interface
Edit:
Here's a little code to get you started.
The binding:
<RadioButton Content="{Binding Name}"
Grid.Column="0">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource EqualsConverter}">
<Binding Path="SelectedItem"/>
<Binding Path="Name"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
and the converter:
public class EqualsConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].Equals(values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Second Edit:
The above approach is not useful to implement two-way binding using the technique linked in the question because the necessary information is not available when converting back.
The correct solution I believe is straight-up MVVM: code the view-model to match the needs of the view. The amount of code is quite small and obviates the need for any converters or funny bindings or tricks.
Here is the XAML;
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton
GroupName="Value"
Content="{Binding Description}"
IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
and code-behind to simulate the view-model:
DataContext = new CheckBoxValueCollection(new[] { "Foo", "Bar", "Baz" });
and some view-model infrastructure:
public class CheckBoxValue : INotifyPropertyChanged
{
private string description;
private bool isChecked;
public string Description
{
get { return description; }
set { description = value; OnPropertyChanged("Description"); }
}
public bool IsChecked
{
get { return isChecked; }
set { isChecked = value; OnPropertyChanged("IsChecked"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class CheckBoxValueCollection : ObservableCollection<CheckBoxValue>
{
public CheckBoxValueCollection(IEnumerable<string> values)
{
foreach (var value in values)
this.Add(new CheckBoxValue { Description = value });
this[0].IsChecked = true;
}
public string SelectedItem
{
get { return this.First(item => item.IsChecked).Description; }
}
}
Now that I know about x:Shared (thanks to your other question), I renounce my previous answer and say that a MultiBinding is the way to go after all.
The XAML:
<StackPanel>
<TextBlock Text="{Binding SelectedChoice}" />
<ItemsControl ItemsSource="{Binding Choices}">
<ItemsControl.Resources>
<local:MyConverter x:Key="myConverter" x:Shared="false" />
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton>
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource myConverter}" >
<Binding Path="DataContext.SelectedChoice" RelativeSource="{RelativeSource AncestorType=UserControl}" />
<Binding Path="DataContext" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</RadioButton.IsChecked>
<TextBlock Text="{Binding}" />
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
The viewmodel:
class Viewmodel : INPC
{
public Viewmodel()
{
Choices = new List<string>() { "one", "two", "three" };
SelectedChoice = Choices[0];
}
public List<string> Choices { get; set; }
string selectedChoice;
public string SelectedChoice
{
get { return selectedChoice; }
set
{
if (selectedChoice != value)
{
selectedChoice = value;
OnPropertyChanged("SelectedChoice");
}
}
}
}
The converter:
public class MyConverter : IMultiValueConverter
{
object selectedValue;
object myValue;
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
selectedValue = values[0];
myValue = values[1];
return selectedValue == myValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
if ((bool)value)
{
return new object[] { myValue, Binding.DoNothing };
}
else
{
return new object[] { Binding.DoNothing, Binding.DoNothing };
}
}
}

Resources