简体   繁体   中英

How to bind to radio buttons created dynamically in WPF?

I'm using WPF and MVVM in an application I'm working on.

I was creating 5 radio buttons dynamically and at the time of creation I was setting the binding using an enumeration and an IValueConverter.

This was okay because I knew I only needed 5 radio buttons, but now the number of radio buttons that I need to create can change, they could be 5 as they could be 30.

So, which is the best way to bind the radio buttons with the code? I think I can't not use an enumeration any more, unless somehow I manage to create the enumeration dynamically, which I don't know if it is possible for what I've read about dynamic enumerations...

Thanks.

Edit

Here it is, more or less, the code I'm using to create the radio buttons dynamically.

public enum MappingOptions {
        Option0,
        Option1,
        Option2,
        Option3,
        Option4
    }
private MappingOptions mappingProperty; public MappingOptions MappingProperty { get { return mappingProperty; } set {
mappingProperty= value; base.RaisePropertyChanged("MappingProperty");
} }
private void CreateRadioButtons() { int limit = 5; int count = 0; string groupName = "groupName";
parent.FormWithRadioButtons.Children.Clear(); foreach (CustomValue value in AllCustomValues) { if (count < limit) { System.Windows.Controls.RadioButton tmpRadioBtn = new System.Windows.Controls.RadioButton(); tmpRadioBtn.DataContext = this; tmpRadioBtn.Content = value.Name; tmpRadioBtn.GroupName = groupName; tmpRadioBtn.Margin = new System.Windows.Thickness(10, 0, 0, 5); string parameter = string.format("Option{0}", count.ToString());
System.Windows.Data.Binding tmpBinding = new System.Windows.Data.Binding("MappingProperty"); tmpBinding.Converter = new EnumBooleanConverter(); tmpBinding.ConverterParameter = parameter; tmpBinding.Source = this; try { tmpRadioBtn.SetBinding(System.Windows.Controls.RadioButton.IsCheckedProperty, tmpBinding); } catch (Exception ex) { //handle exeption }
parent.FormWithRadioButtons.Children.Add(tmpRadioBtn); count += 1; } else break; }
MappingProperty = MappingOptions.Option0; }
public class EnumBooleanConverter : IValueConverter { #region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string parameterString = parameter as string; if (parameterString == null) return System.Windows.DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false) return System.Windows.DependencyProperty.UnsetValue;
object parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string parameterString = parameter as string;
if (parameterString == null) return System.Windows.DependencyProperty.UnsetValue;
return Enum.Parse(targetType, parameterString); }
#endregion }

Create a class that exposes string Text and boolean IsChecked properties and that implements INotifyPropertyChanged . Call it, oh, MutexViewModel .

Create another class that implements an observable collection of these objects called Mutexes , and that handles PropertyChanged on each - eg has a constructor like:

public MutexesViewModel(IEnumerable<MutexViewModel> mutexes)
{
   _Mutexes = new ObservableCollection<MutexViewModel>();
   foreach (MutexViewModel m in Mutexes)
   {
      _Mutexes.Add(m);
      m.PropertyChanged += MutexViewModel_PropertyChanged;
   }
}

and an event handler that ensures that only one of the child objects has IsChecked set to true at any given time:

private void MutexViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
   MutexViewModel m = (MutexViewModel)sender;
   if (e.PropertyName != "IsChecked" || !m.IsChecked)
   {
     return;
   }
   foreach (MutexViewModel other in _Mutexes.Where(x: x != m))
   {
      other.IsChecked = false;
   }
}

You now have a mechanism for creating an arbitrary number of named boolean properties that are all mutually exclusive, ie only one of them can be true at any given time.

Now create XAML like this - the DataContext for this is a MutexesViewModel object, but you can also bind the ItemsSource to something like {DynamicResource myMutexesViewModel.Mutexes} .

<ItemsControl ItemsSource="{Binding Mutexes}">
   <ItemsControl.ItemTemplate>
      <DataTemplate DataType="local:MutexViewModel">
         <RadioButton Content="{Binding Text}" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
      </DataTemplate>
   </ItemsControl.ItemTemplate>
</ItemsControl>

Edit

There was a syntax error in the XAML I posted, but nothing that should have ground you to a full stop. These classes work:

public class MutexViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string Text { get; set; }

    private bool _IsChecked;

    public bool IsChecked
    {
        get { return _IsChecked; }
        set
        {
            if (value != _IsChecked)
            {
                _IsChecked = value;
                OnPropertyChanged("IsChecked");
            }
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler h = PropertyChanged;
        if (h != null)
        {
            h(this, new PropertyChangedEventArgs(propertyName));
        }
    }

}

public class MutexesViewModel
{
    public MutexesViewModel(IEnumerable<MutexViewModel>mutexes)
    {
        Mutexes = new ObservableCollection<MutexViewModel>(mutexes);
        foreach (var m in Mutexes)
        {
            m.PropertyChanged += MutexViewModel_PropertyChanged;
        }
    }

    private void MutexViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        MutexViewModel m = (MutexViewModel) sender;
        if (e.PropertyName == "IsChecked" && m.IsChecked)
        {
            foreach(var other in Mutexes.Where(x => x != m))
            {
                other.IsChecked = false;
            }
        }
    }

    public ObservableCollection<MutexViewModel> Mutexes { get; set; }

}

Create a project, add those classes, paste the ItemsControl XAML into the main window's XAML, and add this to the code-behind of the main window:

    public enum Test
    {
        Foo,
        Bar,
        Baz,
        Bat
    } ;

    public MainWindow()
    {
        InitializeComponent();

        var mutexes = Enumerable.Range(0, 4)
            .Select(x => new MutexViewModel 
               { Text = Enum.GetName(typeof (Test), x) });

        DataContext = new MutexesViewModel(mutexes);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM