MVVM: Binding radio buttons to a view model? MVVM: Binding radio buttons to a view model? wpf wpf

MVVM: Binding radio buttons to a view model?


If you start with Jason's suggestion then the problem becomes a single bound selection from a list which translates very nicely to a ListBox. At that point it's trivial to apply styling to a ListBox control so that it shows up as a RadioButton list.

<ListBox ItemsSource="{Binding ...}" SelectedItem="{Binding ...}">    <ListBox.ItemContainerStyle>        <Style TargetType="{x:Type ListBoxItem}">            <Setter Property="Template">                <Setter.Value>                    <ControlTemplate TargetType="{x:Type ListBoxItem}">                        <RadioButton Content="{TemplateBinding Content}"                                     IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>                    </ControlTemplate>                </Setter.Value>            </Setter>        </Style>    </ListBox.ItemContainerStyle></ListBox>


Looks like they fixed binding to the IsChecked property in .NET 4. A project that was broken in VS2008 works in VS2010.


For the benefit of anyone researching this question down the road, here is the solution I ultimately implemented. It builds on John Bowen's answer, which I selected as the best solution to the problem.

First, I created a style for a transparent list box containing radio buttons as items. Then, I created the buttons to go in the list box--my buttons are fixed, rather than read into the app as data, so I hard-coded them into the markup.

I use an enum called ListButtons in the view model to represent the buttons in the list box, and I use each button's Tag property to pass a string value of the enum value to use for that button. The ListBox.SelectedValuePath property allows me to specify the Tag property as the source for the selected value, which I bind to the view model using the SelectedValue property. I thought I would need a value converter to convert between the string and its enum value, but WPF's built-in converters handled the conversion without problem.

Here is the complete markup for Window1.xaml:

<Window x:Class="RadioButtonMvvmDemo.Window1"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    Title="Window1" Height="300" Width="300">    <!-- Resources -->    <Window.Resources>        <Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">            <Setter Property="Background" Value="Transparent"/>            <Setter Property="ItemContainerStyle">                <Setter.Value>                    <Style TargetType="{x:Type ListBoxItem}" >                        <Setter Property="Margin" Value="5" />                        <Setter Property="Template">                            <Setter.Value>                                <ControlTemplate TargetType="{x:Type ListBoxItem}">                                    <Border BorderThickness="0" Background="Transparent">                                        <RadioButton                                             Focusable="False"                                            IsHitTestVisible="False"                                            IsChecked="{TemplateBinding IsSelected}">                                            <ContentPresenter />                                        </RadioButton>                                    </Border>                                </ControlTemplate>                            </Setter.Value>                        </Setter>                    </Style>                </Setter.Value>            </Setter>            <Setter Property="Control.Template">                <Setter.Value>                    <ControlTemplate TargetType="{x:Type ListBox}">                        <Border BorderThickness="0" Padding="0" BorderBrush="Transparent" Background="Transparent" Name="Bd" SnapsToDevicePixels="True">                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />                        </Border>                    </ControlTemplate>                </Setter.Value>            </Setter>        </Style>    </Window.Resources>    <!-- Layout -->    <Grid>        <!-- Note that we use SelectedValue, instead of SelectedItem. This allows us         to specify the property to take the value from, using SelectedValuePath. -->        <ListBox Style="{StaticResource RadioButtonList}" SelectedValuePath="Tag" SelectedValue="{Binding Path=SelectedButton}">            <ListBoxItem Tag="ButtonA">Button A</ListBoxItem>            <ListBoxItem Tag="ButtonB">Button B</ListBoxItem>        </ListBox>    </Grid></Window>

The view model has a single property, SelectedButton, which uses a ListButtons enum to show which button is selected. The property calls an event in the base class I use for view models, which raises the PropertyChanged event:

namespace RadioButtonMvvmDemo{    public enum ListButtons {ButtonA, ButtonB}    public class Window1ViewModel : ViewModelBase    {        private ListButtons p_SelectedButton;        public Window1ViewModel()        {            SelectedButton = ListButtons.ButtonB;        }        /// <summary>        /// The button selected by the user.        /// </summary>        public ListButtons SelectedButton        {            get { return p_SelectedButton; }            set            {                p_SelectedButton = value;                base.RaisePropertyChangedEvent("SelectedButton");            }        }    }} 

In my production app, the SelectedButton setter will call a service class method that will take the action required when a button is selected.

And to be complete, here is the base class:

using System.ComponentModel;namespace RadioButtonMvvmDemo{    public abstract class ViewModelBase : INotifyPropertyChanged    {        #region INotifyPropertyChanged Members        public event PropertyChangedEventHandler PropertyChanged;        #endregion        #region Protected Methods        /// <summary>        /// Raises the PropertyChanged event.        /// </summary>        /// <param name="propertyName">The name of the changed property.</param>        protected void RaisePropertyChangedEvent(string propertyName)        {            if (PropertyChanged != null)            {                PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);                PropertyChanged(this, e);            }        }        #endregion    }}

Hope that helps!