First, sorry for my english. I have a problem in a project I'm working on and I can't put the finger on what is wrong.
I would really appreciate if someone can help me or give me an advice to solve this issue.
I could create a minimal reproduction case to better explain my issue:

让我们创建一个名为Foo的类:

    public class Foo
    {
        public Foo(byte _id, string _name)
        { 
            ID = _id;
            Name = _name;
        }
        public byte ID { get; set; }
        public string Name { get; set; }
        public bool Option1 { get; set; }
        public bool Option2 { get; set; }
        public bool Option3 { get; set; }
    }

然后在MainWindow中,让我们创建一个名为‘foos’的‘ObservableCollection’,并用一些值填充它:

    public partial class MainWindow : Window
    {
        public ObservableCollection<Foo> Foos { get; set; } = new ObservableCollection<Foo>();
        public MainWindow()
        {
            InitializeComponent();

            DataContext = this;

            Foos.Add(new Foo(1, "foo 1") { Option1 = true });
            Foos.Add(new Foo(2, "foo 2") { Option2 = true });
            Foos.Add(new Foo(3, "foo 3") { Option3 = true });

        }
    }

In the UI, I have now a datagrid binded to Foos displaying some elements (Name & ID).
On the right side I have some RadioButton linked to the 'SelectedItem' of the datagrid displaying the Option 1,2&3:

<StackPanel Orientation="Horizontal">
    <DataGrid x:Name="DtgSource" ItemsSource="{Binding Foos}"
              AutoGenerateColumns="False"
              CanUserAddRows="False"
              Width="100"
              SelectionChanged="DtgSource_SelectionChanged"
              SelectedValuePath="Name">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding ID}" IsReadOnly="True"/>
            <DataGridTextColumn Binding="{Binding Name}" IsReadOnly="True"/>
        </DataGrid.Columns>
    </DataGrid>
    <StackPanel Orientation="Vertical" >
        <RadioButton GroupName="Options" IsChecked="{Binding ElementName=DtgSource,
            Path=SelectedItem.Option1}" Content="Option 1"/>
        <RadioButton GroupName="Options" IsChecked="{Binding ElementName=DtgSource,
            Path=SelectedItem.Option2}" Content="Option 2"/>
        <RadioButton GroupName="Options" IsChecked="{Binding ElementName=DtgSource,
            Path=SelectedItem.Option3}" Content="Option 3"/>
    </StackPanel>
</StackPanel>

有时,当我在数据网格中 Select 一个对象时,即使我从未点击过任何单选按钮,选项也会发生变化: 我有这样的行为:

  • Select foo 1:单选按钮状态:op1:True|op2:False|op3:False
  • Select foo 2:单选按钮状态:op1:False|op2:True|op3:False
  • Select foo 3:单选按钮状态:op1:False|op2:False|op3:True
  • Select foo 1:单选按钮状态:op1:True|op2:False|op3:False
  • Select FOO 3:单选按钮状态:OP1:FALSE|OP2:FALSE|Op3:False

ui

我是否遗漏了什么,或者有没有更好的实现来避免这种行为? 在我正在工作的项目中,我有除单选按钮之外的其他控件,我只使用RadioButton来面对这个问题.

我try 使用 Select 面板的其他控件,如ListView和ListBox.-&gt;相同的行为

I tried also to implement Foo differently to have more information to debug and to catch the moment the options are modified:
SelectionChanged method:

    private void DtgSource_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var d = sender as DataGrid;
        Foo selitem = d.SelectedItem as Foo;
        Console.WriteLine($"SelectionChanged to {d.SelectedValue}| options -> {selitem.Option1}|{selitem.Option2}|{selitem.Option3}");
    }

用于调试的选项实现:

    public class Foo
    {
        private bool option1;
        public bool Option1 { get => option1; set { if (option1 != value) { option1 = value; Console.WriteLine($"{Name}|Option1 set to {value}"); } } }
    //...

如上所述,控制台中的输出如下:

SelectionChanged to foo 1| options -> True|False|False 
SelectionChanged to foo 2| options -> False|True|False  
SelectionChanged to foo 3| options -> False|False|True 
foo 3|Option3 set to False
SelectionChanged to foo 1| options -> True|False|False
SelectionChanged to foo 3| options -> False|False|False

通过在任何选项的"set"上插入断点并查看调用堆栈,我看到调用是由"外部代码"完成的-&>用户界面

/!\在问这个问题之前,我可以找到这个:wpf RadioButton Binding Issue 这似乎是有关联的.但公认的答案是避免使用‘GroupName’属性.这是我试过的东西,遗憾的是,行为完全相同.

如我所说,如能提供任何帮助,请理解这里正在发生的事情:)

推荐答案

Explanation

当双向结合IsChecked与个体bool的性质并且结合源改变时,TL;DR-RadioButton基团是有问题的.

这里的基本问题是,当SelectedItem更改时,每个RadioButton各自的IsChecked属性每次只更新一个.然而,在分组模式下,RadioButtonprogrammed,当任何一个更改为True时,立即将组中的其他按钮更新为False,这当然是有意义的.

不幸的是,这样做的副作用是,当您更改 Select 时,有一段时间按钮并不都指向同一个对象,但WPF仍然将按钮视为一个组,因此错误对象的OptionX个属性被设置为False.

以下是事情发生的一步一步:

初始状态-按钮未绑定.在网格中 Select Foo1.

  1. Button1.IsChecked = Foo1.Option1 (true).其他按钮仍未绑定,没有进一步操作.
  2. Button2.IsChecked = Foo1.Option2 (false).没有进一步的行动.
  3. Button3.IsChecked = Foo1.Option3 (false).没有进一步的行动.

在网格中 Select Foo2.

  1. Button1.IsChecked = Foo2.Option1 (false).没有进一步的行动.
  2. Button2.IsChecked = Foo2.Option2 (true).然而,这也导致了:Foo2.Option1.IsChecked = Button1.IsChecked = false102.后者不是拼写错误;而是不匹配,因为Button3仍然绑定到Foo1,尽管它是无害的,因为Foo1.Option3已经是假的.
  3. Button3.IsChecked = Foo2.Option3 (false).

在网格中 Select Foo1.

  1. Button1.IsChecked = Foo1.Option1 (true).这也导致了:101 and 102.又一次不匹配.它对Foo2.Option3是无害的,因为无论如何它都是False,但是它错误地强制将Foo2.Option2设置为False,因为Button2还没有将其绑定源切换回Foo1,因此它错误地将Foo2.Option2而不是Foo1.Option2设置为False.
  2. Button2.IsChecked = Foo1.Option2 (false).
  3. Button3.IsChecked = Foo1.Option3 (false).

在网格中 Select Foo2.

  1. Button1.IsChecked = Foo2.Option1 (false).
  2. Button2.IsChecked = Foo2.Option2 (false).
  3. Button3.IsChecked = Foo2.Option3 (false).

因此,您可以看到问题在于,对于每RadioButton个绑定源代码,一次更改一个,而不是自动更改.当任何绑定更改导致RadioButton被选中时,组中的其他按钮会立即受到影响,即使它们自己的绑定源尚未更新.因此,如果绑定源可能更改,则不能安全地将具有分组RadioButtonIsChecked绑定到单个bool支持属性.这是否可以被称为WPF中的错误,我将留给其他人来 comments ,但至少这种行为是可以解释的.


Workaround

如果你想坚持使用单独的bool个后台属性,而不想处理枚举,一个解决方法是取消RadioButton的分组,并在视图模型方面处理独占性,如下所示:

public class Foo : INotifyPropertyChanged
{
    public Foo(byte _id, string _name)
    {
        ID = _id;
        Name = _name;
    }
    public byte ID { get; set; }
    public string Name { get; set; }

    bool _option1;
    public bool Option1
    {
        get => _option1;
        set
        {
            if (value == _option1)
                return;
            _option1 = value;
            if (value)
            {
                this.Option2 = false;
                this.Option3 = false;
            }
            OnPropertyChanged();
        }
    }

    bool _option2;
    public bool Option2
    {
        get => _option2;
        set
        {
            if (value == _option2)
                return;
            _option2 = value;
            if (value)
            {
                this.Option1 = false;
                this.Option3 = false;
            }
            OnPropertyChanged();
        }
    }

    bool _option3;
    public bool Option3 
    {
        get => _option3;
        set
        {
            if (value == _option3)
                return;
            _option3 = value;
            if (value)
            {
                this.Option1 = false;
                this.Option2 = false;
            }
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName]string property = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
}

(不要省略属性更改通知的额外代码,否则当一个按钮发生更改时,UI不会更新其他按钮).

在XAML中:

        <StackPanel x:Name="_stack"
                    Orientation="Vertical" DataContext="{Binding ElementName=DtgSource, Path=SelectedItem}">
            <RadioButton GroupName="1" 
                         IsChecked="{Binding Option1}" Content="Option 1" />
            <RadioButton GroupName="2"
                         IsChecked="{Binding Option2}" Content="Option 2" />
            <RadioButton GroupName="3"
                         IsChecked="{Binding Option3}" Content="Option 3" />
        </StackPanel>

在这里,您需要指定不同的组名以覆盖默认的分组行为.

Other Workarounds

无枚举解决方法的一个缺点是它不能很好地伸缩,如果您每次都不虚报正确的属性,则可能会导致细微的错误.

如果你愿意使用枚举,Rufo爵士在下面的 comments 中的例子很棒,值得成为它自己的答案.另一个好的 Select (实际上是我更喜欢的)是这个问题的公认答案:

How do you work around the binding problems with radio button groups

这些绑定起作用而单独的bool属性绑定不起作用的原因有点微妙,但最终,当RadioButton由于在互斥的组中而被UI取消判断时,它们都可以防止支持属性被更改.

.net相关问答推荐

为什么Linq中的运算符逻辑不匹配结果,当值为0或在VB. NET中没有

为什么Regex.Escape支持数字符号和空格?

.NET MAUI ListView - ObservableCollection - 在异步方法期间不更新

如何计算给定2个字符串的距离相似性度量?

图像 UriSource 和数据绑定

移位比Java中的乘法和除法更快吗? .网?

为什么这个多态 C# 代码会打印它的功能?

设置日志(log)文件名以在 Log4j 中包含当前日期

AutoMapper 的替代品

在 WinForms 应用程序中查找焦点控件的首选方法是什么?

OpenCV的.Net(dotNet)包装器?

使用多个 MemoryCache 实例

.Net 中的 Int128?

静态析构函数

如何从 .NET 中的流中获取 MemoryStream?

我不了解应用程序域

监听依赖属性的变化

在 WPF DataGrid 中绑定 ComboBoxColumn 的 ItemsSource

IronPython 与 Python .NET

在 WPF 中设置 Tab 键顺序