Как привязать радио кнопки, созданные динамически в WPF? - PullRequest
3 голосов
/ 04 апреля 2011

Я использую WPF и MVVM в приложении, над которым я работаю.

Я создавал 5 радиокнопок динамически, и во время создания я устанавливал привязку, используя перечисление и IValueConverter.

Это было нормально, потому что я знал, что мне нужно всего 5 радиокнопок, но теперь количество радиокнопок, которые мне нужно создать, может измениться, их может быть 5, а может быть 30.

Итак, как лучше связать радиокнопки с кодом? Я думаю, что больше не могу использовать перечисление, если только каким-то образом мне не удастся создать перечисление динамически, чего я не знаю, возможно ли это для того, что я прочитал о динамических перечислениях ...

Спасибо.

Редактировать

Вот, более или менее, код, который я использую для динамического создания переключателей.

<code>public enum MappingOptions {
        Option0,
        Option1,
        Option2,
        Option3,
        Option4
    }<br>
private MappingOptions mappingProperty;
public MappingOptions MappingProperty
{
    get { return mappingProperty; }
    set 
    {<br>
        mappingProperty= value;
        base.RaisePropertyChanged("MappingProperty");<br>
    }
}<br>
private void CreateRadioButtons()
{
    int limit = 5;
    int count = 0;
    string groupName = "groupName";<br>
    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());<br>
            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
            }<br>
            parent.FormWithRadioButtons.Children.Add(tmpRadioBtn);
            count += 1;
        }
        else
            break;
    }<br>
    MappingProperty = MappingOptions.Option0;
}<br>
public class EnumBooleanConverter : IValueConverter
{
    #region IValueConverter Members<br>
    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;<br>
         if (Enum.IsDefined(value.GetType(), value) == false)
            return System.Windows.DependencyProperty.UnsetValue;<br>
         object parameterValue = Enum.Parse(value.GetType(), parameterString);<br>
         return parameterValue.Equals(value);
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string parameterString = parameter as string;<br>
        if (parameterString == null)
            return System.Windows.DependencyProperty.UnsetValue;<br>
         return Enum.Parse(targetType, parameterString);
    }<br>
    #endregion
}

1 Ответ

7 голосов
/ 04 апреля 2011

Создайте класс, который предоставляет строковые Text и логические IsChecked свойства и который реализует INotifyPropertyChanged. Назовите это, о, MutexViewModel.

Создайте другой класс, который реализует наблюдаемую коллекцию этих объектов с именем Mutexes, и который обрабатывает PropertyChanged для каждого - например, имеет конструктор вроде:

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

и обработчик событий, который гарантирует, что только один из дочерних объектов имеет IsChecked, установленный в true в любой момент времени:

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;
   }
}

Теперь у вас есть механизм для создания произвольного числа именованных логических свойств, которые являются взаимоисключающими, то есть только одно из них может быть истинным в любой момент времени.

Теперь создайте XAML следующим образом - DataContext для этого объекта является MutexesViewModel, но вы также можете связать ItemsSource с чем-то вроде {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>

Редактировать

В XAML, который я опубликовал, была синтаксическая ошибка, но ничего, что могло бы вас остановить. Эти занятия работают:

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; }

}

Создайте проект, добавьте эти классы, вставьте XAML ItemsControl в XAML главного окна и добавьте его в код основного окна:

    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);
    }
...