Papa Mufflon
Papa Mufflon

Reputation: 20020

RadioButton IsChecked property gets overridden when changing tabs

I'm sure this behavior is known, but I'm unable to google it. I have following code:

<Window x:Class="ContentControlListDataTemplateKacke.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <TabControl ItemsSource="{Binding Items}">
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Label Content="{Binding Name}" />
                        <RadioButton Content="Option1" IsChecked="{Binding Option1}" />
                        <RadioButton Content="Option2" IsChecked="{Binding Option2}" />
                    </StackPanel>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </DockPanel>
</Window>

The code-behind is simple:

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

The ViewModel looks like this:

public class ViewModel : NotificationObject
{
    public ViewModel()
    {
        Items = new ObservableCollection<Item>
        {
            new Item {Name = "1", Option1 = true},
            new Item {Name = "2", Option2 = true}
        };
    }

    public ObservableCollection<Item> Items { get; set; }
}

And an Item like this:

public class Item : NotificationObject
{
    public string Name { get; set; }

    private bool _option1;
    public bool Option1
    {
        get { return _option1; }
        set
        {
            _option1 = value;
            RaisePropertyChanged(() => Option1);
        }
    }

    private bool _option2;
    public bool Option2
    {
        get { return _option2; }
        set
        {
            _option2 = value;
            RaisePropertyChanged(() => Option2);
        }
    }
}

I'm using Prism, so the RaisePropertyChanged raises an PropertyChanged-event. Select the second tab, then the first tab, then the second tab again and voilá, the RadioButtons on the second tab are deselected.

Why?

Another solution apart from Rachels

A colleague of mine just had the idea to bind the GroupName property of the RadioButtons to a unique string of each item. Just change the declaration of the RadioButtons into this:

<RadioButton GroupName="{Binding Name}" Content="Option1" IsChecked="{Binding Option1}" />
<RadioButton GroupName="{Binding Name}" Content="Option2" IsChecked="{Binding Option2}" />

And it works if the Name-property is unique for all items (as its the case for my problem).

Upvotes: 4

Views: 3461

Answers (2)

Rachel
Rachel

Reputation: 132558

WPF is reading all the RadioButtons as part of the same Group, and in a radio button group only one item can be selected at a time.

The load order goes:

  • Load Tab1
  • Load Tab1.Radio1. IsChecked = True
  • Load Tab1.Radio2. IsChecked = True, so set Tab1.Radio2.IsChecked = False

  • Click Tab 2

  • Load Tab2
  • Load Tab2.Radio1. IsChecked = True, so set Tab1.Radio2.IsChecked = False
  • Load Tab2.Radio2. IsChecked = True, so set Tab2.Radio1.IsChecked = False

  • By now, Tab2.Radio2 is the only one checked, and all the other Radios have been loaded and Unchecked, so their DataBound values have been updated to false.

  • Click Tab 1

  • Load Tab1.Radio1. IsChecked = False
  • Load Tab1.Radio2. IsChecked = False

If you Radio buttons are unrelated and can both be checked at once, I would suggest switching to CheckBoxes

If they're meant to be grouped and only one item can be selected at a time, I'd suggest switching to a ListBox drawn with RadioButtons, and only storing the SelectedOption in your ViewModel

Here's the style I typically use for that:

<Style x:Key="RadioButtonListBoxStyle" TargetType="{x:Type ListBox}">
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Cycle" />
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="{x:Type ListBoxItem}" >
                <Setter Property="Margin" Value="2, 2, 2, 0" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Border Background="Transparent">
                                <RadioButton
                                    Content="{TemplateBinding ContentPresenter.Content}" VerticalAlignment="Center"
                                    IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"/>

                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

It's used like this:

<ListBox ItemsSource="{Binding Options}"
         SelectedValue="{Binding SelectedValue}"
         Style="{StaticResource RadioButtonListBoxStyle}" />

Upvotes: 1

Jakub
Jakub

Reputation: 534

I used to have a problem very similar to this one where text was getting cleared when I was switching between views. If I recall correctly the following solution was what worked.

  • Bind OneWay to the properties and mark these bound properties with an attribute.
  • Every time you load the view (and hence viewmodel), use reflection on the aforementioned attribute to find the bound properties.
  • Fire off a PropertyChanged event for each of the properties to update the view correctly.

I think this results from the view loading with default settings and not querying the properties on load since nothing is raising a PropertyChanged event.

Also, it's not part of your question, but you can set the data context in XAML (via the DataContext property in Window) directly so that Visual Studio doesn't have to have an explicit codebehind file.

Upvotes: 0

Related Questions