views:

649

answers:

5

Hello, I have a radiobutton group:

<TextBlock Height="24" Text="Update Interval (min):"/>
<RadioButton x:Name="radioButtonTimerNone" IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, Mode=TwoWay}"} Content="None" />
<RadioButton x:Name="radioButtonTimerOne" IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, Mode=TwoWay}" 
          Content="1" />
<RadioButton x:Name="radioButtonTimerFive" IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, Mode=TwoWay}" 
          Content="5" />

And a property:

    public int UpdateInterval {
        get { return _updateInterval; }
        set { _updateInterval = value;
            onPropertyChanged("UpdateInterval");
        }
    }

How do I bind the radiobuttons to the property, so radioButtonTimerNone is checked when UpdateInterval is 0, radioButtonTimerOne is checked when UpdateInterval is 1, etc.
I have tried to create a converter, but it doesn't identify which rb is being set:

[ValueConversion(typeof(RadioButton), typeof(bool))] 
class UpdateIntervalToCheckedConverter : System.Windows.Data.IValueConverter
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

I expected 'value' to be a radiobutton, but it appears to be the value of UpdateInterval.

Thanks for any hints...

+1  A: 

Your value converter doesn't get told which RadioButton changed the value - all the binding knows is that the "IsChecked" property was changed, so the new value for IsChecked is the only thing it can tell the converter.

The first thing that springs to my mind is to supply a converter parameter with each of your bindings:

<RadioButton 
    x:Name="radioButtonTimerNone" 
    IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, ConverterParameter=0, Mode=TwoWay}"
    Content="None" />
<RadioButton
    x:Name="radioButtonTimerOne"
    IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, ConverterParameter=1, Mode=TwoWay}"
    Content="1" />
<RadioButton 
    x:Name="radioButtonTimerFive"
    IsChecked="{Binding UpdateInterval, Converter={StaticResource updateIntervalToCheckedConverter}, ConverterParameter=5, Mode=TwoWay}"
    Content="5" />

So now the "parameter" parameter on the "Convert" method will have the value "0", "1" or "5" depending on which RadioButton was checked. I think, though I'm not certain, that the parameter will be of type string, so you may have to take that into account when interrogating the value.

Matt Hamilton
Thanks, I've gotten it working using your suggestion. Doesn't seem very clean, though, having to pass what is essentially an ID for the control I want bound to the property....
Number8
Yeah, I don't know if there's a "cleaner" solution. I'm not a big fan of value converters in general, but if you have to use one then ConverterParameter can make your life much easier!
Matt Hamilton
This is the solution that I'd choose. While it's not very clean I generally like it better than Anderson's solution of having a bunch of properties on the ViewModel. But, I guess, that's because I don't really like to have my ViewModel cater to the View so I can support structured skinning.
dustyburwell
Er... the ViewModel is supposed to cater to the view... it's even in the name :) This is not my original opinion... I stole it from the WPF Disciples: http://groups.google.com/group/wpf-disciples/browse_thread/thread/3fe270cd107f184f/c29b3935ec9d3c4e
Anderson Imes
Is it possible to pass the radiobutton object as the parameter?
Number8
+1  A: 

If you are using MVVM and are bound to a ViewModel (I would guess that you are), I usually consider my ViewModel to be a big ValueConverter. Why not put that logic into properties for each?

Here's an example of one of them:

public bool Timer5Enabled
{
     get { return UpdateInterval == 5; }
}

And then you'd just bind to that:

<RadioButton
    x:Name="radioButtonTimerOne"
    IsChecked="{Binding Timer5Enabled, Mode=OneWay}"
    Content="1" />

The only thing you'd need to change would be to tie your interval update logic to raise OnChanged for your dependent properties:

public int UpdateInterval {
        get { return _updateInterval; }
        set { _updateInterval = value;
            onPropertyChanged("UpdateInterval");
            onPropertyChanged("Timer5Enabled");
            onPropertyChanged("...");
        }
    }

ValueConverters are good to avoid if you can.

Anderson Imes
A: 

You could probably consider having a List<...> with different intervals. The list type should preferably be some custom type (e.g. UpdateInterval) or a KeyValuePair<K,V> to decouple what is shown to the user from what is an actual value.

Then you could have a ListBox bound to this list, and each ListBoxItem templated to show a RadioButton. Then, in the template, you bind radio button's IsChecked to ListBoxItem.IsSelected.

The final touch is just to bind your property to the ListBox's SelectedItem or SelectedValue.

arconaut
A: 

This has been a very annoying problem for me.

The WPF RadioButton class has a bug that removes the binding for the IsChecked property when you assign multiple RadioButtons to the same GroupName. People have given many work arounds, but when I had to visit this again I came up with one that doesn't make me queasy.

So what you have to do is subclass the RadioButton class with this:

public class RadioButton: System.Windows.Controls.RadioButton
{
    protected override void OnClick()
    {
        base.OnClick();

        SetValue(CurrentValueProperty,CheckedValue);
    }

    public int CurrentValue
    {
        get { return (int)GetValue(CurrentValueProperty); }
        set { SetValue(CurrentValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for CurrentValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CurrentValueProperty =
        DependencyProperty.Register("CurrentValue", typeof(int), typeof(RadioButton), new FrameworkPropertyMetadata(0,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CurrentValue_Changed));

    public static void CurrentValue_Changed(object sender, DependencyPropertyChangedEventArgs e)
    {
         ((RadioButton)sender).IsChecked = ((RadioButton)sender).CurrentValue == ((RadioButton)sender).CheckedValue;
    }

    public int CheckedValue
    {
        get { return (int)GetValue(CheckedValueProperty); }
        set { SetValue(CheckedValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for CheckedValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CheckedValueProperty =
        DependencyProperty.Register("CheckedValue", typeof(int), typeof(RadioButton), new UIPropertyMetadata(0));
}

All this does is add two dependency properties so you can do an equality comparison.

So in XAML an example would be this:

  <UserControl x:Class="MyClass"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:c="clr-namespace:MVVMLibrary;assembly=MVVMLibrary"
    Height="Auto" Width="Auto">

            <c:RadioButton CurrentValue="{Binding MyValue}" CheckedValue="1"  GroupName="SearchType" >Value 1</c:RadioButton>
            <c:RadioButton Grid.Column="1" CurrentValue="{Binding MyValue}" CheckedValue="2" GroupName="SearchType" >Value 2</c:RadioButton>
  </UserControl>

So If you notice all you do is 1) Bind to the CurrentValue property 2) Set the CheckedValue property to the value that would make the RadioButton checked 3) Set the RadioButtons to the same GroupName

If you notice i made CurrentValue and CheckedValue int type. The reason I did this is so that you could actually bind CurrentValue to an enumeration. I think this is awesome, but that's just my opinion which probably doesn't count for much. :)

Hope this helps somebody.

Jose
A: 

One possibility is to implement an IValueConverter and configure it passing the expected enumeration value for every RadioButton you need to bind, as I described here in my blog.

Hemme