Today I came across a problem I’d previously worked around but knew there had to be a better way to handle. What I wanted to do was both generate a series of RadioButtons based on an Enum and bind the IsChecked property of the RadioButtons to a property, in this case of another control, of the Enum type.
Google shows there is a LOT of information out there but nothing I could find covered exactly what I wanted to do. In the end I managed to come up with a combination of various solutions I’d seen suggested that works well for me. So what follows are the details on what I discovered.
If you want to skip the details and just view the solution yourself you can get the Visual Studio 2010 solution from my SkyDrive.
To keep this as short as possible I won’t cover what didn’t work and I’ll just explain the final solution.
The Setup
A simple enum:
public enum Modes
{
List,
Thumbnail,
IconOnly
}
and an observable property for the MainWindow:
private Modes _mode = Modes.List;
public Modes Mode
{
get { return _mode; }
set
{
_mode = value;
InvokePropertyChanged( new PropertyChangedEventArgs( "Mode" ) );
}
}
Now the trick turned out to be binding the generated RadioButtons IsChecked to the IsSelected of a ListBoxItem along with the use of an ObjectDataProvider as shown in this How To.
The ListBox
<ListBox
KeyboardNavigation.DirectionalNavigation="Cycle"
BorderBrush="Transparent"
Name="rbl1"
ItemsSource="{Binding Source={StaticResource odp}}"
SelectedItem="{Binding Mode}"
ItemTemplate="{StaticResource ViewStyleTemplate}"
ItemsPanel="{StaticResource StackPanelHorizontalTemplate}" />
The Resources
<Style
BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}" />
The first Style overrides the style of the generated RadioButtons so they appear as ToggleButtons. I needed it for my touch screen application as the normal RadioButton style is too small.
<ObjectDataProvider
MethodName="GetValues"
ObjectType="{x:Type System:Enum}"
x:Key="odp" >
<ObjectDataProvider.MethodParameters>
<x:Type
TypeName="WpfEnumBinding:Modes" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
The ObjectDataProvider simply provides the list of the various Enum values. See this How To for more info.
<DataTemplate
x:Key="ViewStyleTemplate" >
<RadioButton
IsHitTestVisible="false"
Focusable="false"
IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}"
Width="100" >
<TextBlock
Text="{Binding }"
Margin="3" />
</RadioButton>
</DataTemplate>
The template to generate the RadioButtons ensures they can’t actually be clicked/focused and, as mentioned, binds the IsChecked property to the parent ListBoxItems IsSelected property. So when you click the RadioButton the click passes through to the ListBoxItem and selects that item. This in turn, via the binding, checks the corresponding RadioButton.
The ItemsPanel isn’t worth showing here. The one remaining trick is to modify the Style of the ListBoxItem so it doesn’t show a highlight when selected. This is easily achieved as follows:
<Style
TargetType="ListBoxItem" >
<Style.Resources>
<SolidColorBrush
x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent" />
</Style.Resources>
</Style>
So that covers my original problem. But what if you don’t want to display all the values of the enumeration and you just want to work with a couple of them? The answer (based on this answer on stackoverflow) turns out to be quite straightforward, and like many other binding issues, involves using a custom Converter as follows.
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(false) ? DependencyProperty.UnsetValue : parameter;
}
}
Then your RadioButtons simply take advantage of that converter as follows:
<RadioButton
Content="List"
IsChecked="{Binding Path=Mode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static WpfEnumBinding:Modes.List}}" />
<RadioButton
Content="Thumbnail"
IsChecked="{Binding Path=Mode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static WpfEnumBinding:Modes.Thumbnail}}" />
Again if you’ve actually read this far you can get the Visual Studio 2010 solution from my SkyDrive.
I hope this saves someone else out there some time/effort.
0 comments:
Post a Comment