Change WPF RadioButton does not update MVVM user interface

I have two radio buttons working as a RadioButton list in the user interface using MVVM. When the user control is loaded for the first time, one of the switches is selected, and the corresponding controls are displayed in the user interface ... Now, when I change the switch, the user interface is not updated.

The following is a sample XAML:

<Label Grid.Column="0" Grid.Row="3" Content="Exchange Details:" Margin="3" VerticalContentAlignment="Center" Style="{StaticResource NormalLabelStyle}"></Label>
 <Grid Grid.Column="1" Grid.Row="3" Width="200">
  <Grid.ColumnDefinitions>
   <ColumnDefinition Width="Auto"/>
   <ColumnDefinition Width="20"/>
  <ColumnDefinition Width="Auto"/>
 </Grid.ColumnDefinitions>
<RadioButton GroupName="rdoExchange" Content="Basic" IsChecked="{Binding Path=ExchangeDetailsBasic}"  Grid.Column="0" VerticalContentAlignment="Center" VerticalAlignment="Center"></RadioButton>
<RadioButton GroupName="rdoExchange" Content="Advanced" IsChecked="{Binding Path=ExchangeDetailsAdvanced}" Grid.Column="2" VerticalContentAlignment="Center" VerticalAlignment="Center"></RadioButton
 </Grid> 

 <Label Grid.Column="3" Grid.Row="0" Content="Number of Mailbox Profiles:" VerticalContentAlignment="Center" Style="{StaticResource NormalLabelStyle}" Visibility="{Binding Path=IsAdvanced}" ></Label>
 <telerik:RadNumericUpDown Grid.Column="4" Grid.Row="0" Margin="3" Value="{Binding Path=NumberofMailboxProfiles}" IsInteger="True" Minimum="1" Maximum="4"  HorizontalAlignment="Left" Visibility="{Binding Path=IsAdvanced}">< /telerik:RadNumericUpDown>

Below is my ViewModel code:

 private enum ExchangeDetails{
        Basic,
        Advanced
 }

 private bool isBasicMode = true;

 public bool ExchangeDetailsBasic {
         get {
            return this.isBasicMode;
        }

        set {
            if (value) {
                this.applicationSpecificRequirements[ExchangeDetailsKey] = ExchangeDetails.Basic.ToString();
                if (!this.isBasicMode) {
                    this.CheckBasicOrAdvancedSelecteAndDisplayView();
                }
            }
        }
    }

 public bool ExchangeDetailsAdvanced {
        get {
            return !this.isBasicMode;
        }

        set {
            if (value) {
                this.applicationSpecificRequirements[ExchangeDetailsKey] = ExchangeDetails.Advanced.ToString();
                this.CheckBasicOrAdvancedSelecteAndDisplayView();
            }
        }
    }

    public Visibility IsAdvanced { get; private set; }

    private void CheckBasicOrAdvancedSelecteAndDisplayView() {
        this.isBasicMode = this.applicationSpecificRequirements.ContainsKey(ExchangeDetailsKey) ? (this.applicationSpecificRequirements[ExchangeDetailsKey].Equals(ExchangeDetails.Basic.ToString()) ? true : false) : true;
        this.IsAdvanced = this.isBasicMode ? Visibility.Collapsed : Visibility.Visible;
    }
+3
source share
4 answers

Radio buttons, groups, and snap do not mix. It is amazing in design.

. -, . -, , .

- . , , , .

. , . , , , , , . - - Binding . . .

. . , , . , Reflector.

, , , : , .

- . . , . .

, , :

public bool Option1
{
   set
   {
      _Option1 = value;
      if (value)
      {
         Option2 = false;
         Option3 = false;
      }
      OnPropertyChanged("Option1");
   }
}

, . , . , - , , .

+7

INotifyPropertyChanged = TwoWay XAML, .

:

<Grid >
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <RadioButton GroupName="rdoExchange" Content="Basic" 
                 IsChecked="{Binding Path=ExchangeDetailsBasic, Mode=TwoWay}"  
                 Grid.Column="0" 
                 VerticalContentAlignment="Center" 
                 VerticalAlignment="Center"/>
    <RadioButton GroupName="rdoExchange" Content="Advanced" 
                 IsChecked="{Binding Path=ExchangeDetailsAdvanced, Mode=TwoWay}" 
                 Grid.Column="1" 
                 VerticalContentAlignment="Center" 
                 VerticalAlignment="Center"/>
    <Label Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" 
           Content="Number of Mailbox Profiles:" 
           VerticalContentAlignment="Center" 
           Visibility="{Binding Path=IsAdvanced, Mode=TwoWay}" />
</Grid>

ViewModel:

public class MainViewModel : ViewModelBase
{
  public MainViewModel()
  {
  }

  private bool _isBasicMode = true;
  public bool ExchangeDetailsBasic
  {
    get
    {
      return this._isBasicMode;
    }

    set
    {
      this._isBasicMode = value;
      if (value)
      {
        ExchangeDetailsAdvanced = false;
        IsAdvanced = Visibility.Collapsed;
      }
      this.OnPropertyChanged("ExchangeDetailsBasic");
    }
  }

  private bool _isAdvancedMode = false;
  public bool ExchangeDetailsAdvanced
  {
    get
    {
      return this._isAdvancedMode;
    }

    set
    {
      _isAdvancedMode = value;
      if (value)
      {
        ExchangeDetailsBasic = false;
        IsAdvanced = Visibility.Visible;
      }
      this.OnPropertyChanged("ExchangeDetailsAdvanced");
    }
  }

  private Visibility _isAdvanced = Visibility.Collapsed;
  public Visibility IsAdvanced
  {
    get
    {
      return _isAdvanced;
    }
    set
    {
      _isAdvanced = value;
      this.OnPropertyChanged("IsAdvanced");
    }
  }
}

, INotifyPropertyChanged.

public abstract class ViewModelBase : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnPropertyChanged(string propertyName)
  {
     PropertyChangedEventHandler handler = PropertyChanged;

     if (handler != null)
     {
        handler(this, new PropertyChangedEventArgs(propertyName));
     }
  }
}
0

, INotifyPropertyChanged . , , . @Zamboni .

0

, , VM .

Here is my solution: an attached property that toggles the IsChecked property of all buttons in the same group. Works on my car :-)

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace Elca.MvvmHelpers {

    public class RadioButtonHelper : DependencyObject {

        private static readonly Dictionary<string, List<RadioButton>> s_group2ButtonsMap = new Dictionary<string, List<RadioButton>>();
        private static readonly List<RadioButton> s_knownButtons = new List<RadioButton>(); 

        private static void OnRadioButtonChecked(object sender, RoutedEventArgs e) {
            RadioButton rb = (RadioButton)sender;
            UncheckOtherButtonsInGroup(rb);
        }

        public static bool? GetIsChecked(RadioButton d) {
            return (bool?) d.GetValue(IsCheckedProperty);
        }

        public static void SetIsChecked(RadioButton d, bool? value) {
            d.SetValue(IsCheckedProperty, value);
        }

        public static readonly DependencyProperty IsCheckedProperty =
            DependencyProperty.RegisterAttached("IsChecked", 
                                                typeof(bool?), 
                                                typeof(RadioButtonHelper),
                                                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal |
                                                                                     FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                                                              IsCheckedChanged));

        public static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {

            var rb = d as RadioButton;
            if (rb == null) {
                throw new Exception("IsChecked attached property only works on a FrameworkElement type");
            }

            RememberRadioButton(rb);

            if ((bool) e.NewValue) {

                rb.IsChecked = true; // this triggers OnRadioButtonChecked => other buttons in the same group will be unchecked
            }
        }

        private static void RememberRadioButton(RadioButton rb) {
            var groupName = GetGroupName(rb);

            // if this button is unknown, add it to the right list, based on its group name
            if (s_knownButtons.Contains(rb)) {
                return;
            }

            s_knownButtons.Add(rb);

            List<RadioButton> existingButtons;
            if (! s_group2ButtonsMap.TryGetValue(groupName, out existingButtons)) {
                // unknown group
                s_group2ButtonsMap[groupName] = new List<RadioButton> {rb};
                RegisterButtonEvents(rb);
            } else {
                if (! existingButtons.Contains(rb)) {
                    existingButtons.Add(rb);
                    RegisterButtonEvents(rb);
                }
            }
        }

        private static void RegisterButtonEvents(RadioButton rb) {
            rb.Unloaded += OnButtonUnloaded;
            rb.Checked += OnRadioButtonChecked;
        }

        private static void OnButtonUnloaded(object sender, RoutedEventArgs e) {
            RadioButton rb = (RadioButton) sender;

            ForgetRadioButton(rb);
        }

        private static void ForgetRadioButton(RadioButton rb) {

            List<RadioButton> existingButtons = s_group2ButtonsMap[GetGroupName(rb)];
            existingButtons.Remove(rb);
            s_knownButtons.Remove(rb);

            UnregisterButtonEvents(rb);
        }

        private static void UnregisterButtonEvents(RadioButton rb) {
            rb.Unloaded -= OnButtonUnloaded;
            rb.Checked -= OnRadioButtonChecked;
        }

        private static void UncheckOtherButtonsInGroup(RadioButton rb) {

            List<RadioButton> existingButtons = s_group2ButtonsMap[GetGroupName(rb)];

            foreach (RadioButton other in existingButtons) {

                if (other != rb) {
                    SetIsChecked(other, false);
                }
            }
            SetIsChecked(rb, true);

        }

        private static string GetGroupName(RadioButton elt) {
            string groupName = elt.GroupName;
            if (String.IsNullOrEmpty(groupName)) {
                groupName = "none"; // any value will do
            }
            return groupName;
        }
    }
}

In the view for each button:

<RadioButton MvvmHelpers:RadioButtonHelper.IsChecked="{Binding IsExplicitFileSelected, Mode=TwoWay}">
...
</RadioButton>

The VM has a logical property for each switch. Each such property is assigned a value to begin the process of listening to the attached property.

All buttons without a group name are considered part of the same group.

0
source

Source: https://habr.com/ru/post/1787280/


All Articles