.NET Framework已经算是一个很易用的库了。可以自动地为我们做很多事情,而且大都做得还不错。但是自动完成的事情很可能会有隐患,因为Framework本身是并不了解业务逻辑的。它自动完成的事情,可能会给我们帮倒忙。
RadioButton就是其中一个。
先来从设置值的角度介绍一下WPF里的Dependency Property(以下简称DP)。在WPF里控制一个控件的DP,有太多的方式。可以用Style,可以用Animation,可以用Data Binding,可以用Trigger,还有最基本直接赋值。控件会综合上面各个方面的值,其及优先级等因素来决定一个DP的最终的值是多少。关于这方面的更多的知识可以参考雨痕关于DP的文章或MSDN。(不过不确定MSDN上有没有有关DP设值优先级的系统的介绍哦。)
多数的控件不会自动的更改自己的某个属性的值。但是总有一些例外。RadioButton就是其中一个。它自动设置什么值了?答案是IsChecked属性。RadioButton的特点是一组RadioButton只有一个被选中。当一个RadioButton被选中的时候,其它所有的RadioButton就会被自动地设置IsChecked属性为False。
问题来了,设置一个属性的方法那么多,它自己自动设置这个属性的时候,应该用什么方法呢?这个问题早在.NET 3.0时就已经有人发现,并在微软的论坛上讨论过。MSFT的Sam Bent也承认了这个Bug的存在。但问题就在于,无论RadioButton用哪种现有的方法去使得IsChecked属性为False,“总会有些人不高兴”。3.0里RadioButton的自动,会让Style和Template的值失效;在3.5中,Fix了3.0中的这个Bug,但是却导致Binding失效。
经过笔者验证,在.NET 3.5 SP1中使用了与3.5相同的逻辑,即使Binding失效。下面我们将用一个“为微软选CEO”的示例程序进行验证。
首先定义一个Person类,如下:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Person
1
using System.ComponentModel;
2
using System.Diagnostics;
3![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
namespace BindingRadioButton.Model
5![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
7
///
8
/// </summary>
9
public class Person : INotifyPropertyChanged
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
11![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Private Fields#region Private Fields
12![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
13
private bool isCeo;
14
private string name;
15![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
#endregion
17![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Public Properties#region Public Properties
19![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
21
///
22
/// </summary>
23
public bool IsCeo
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return isCeo; }
26
set
27![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
28
if (value != isCeo)
29![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
30
isCeo = value;
31
OnPropertyChanged("IsCeo");
32
}
33
}
34
}
35![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
36![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
37
///
38
/// </summary>
39
public string Name
40![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
41![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return name; }
42
set
43![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
44
if (value != name)
45![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
46
name = value;
47
OnPropertyChanged("Name");
48
}
49
}
50
}
51![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
52
#endregion
53![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
54![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
INotifyPropertyChanged Members#region INotifyPropertyChanged Members
55![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
56![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
57
///
58
/// </summary>
59
public event PropertyChangedEventHandler PropertyChanged;
60![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
61
#endregion
62![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
63
protected virtual void OnPropertyChanged(string propertyName)
64![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
65
PropertyChangedEventHandler temp = PropertyChanged;
66
if (temp != null)
67![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
68
temp(this, new PropertyChangedEventArgs(propertyName));
69
Trace.WriteLine(string.Format("{0} {1} CEO\n", Name, IsCeo ? "is" : "is not"));
70
}
71
}
72
}
73
}
74![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
由于实现了INotifyPropertyChanged接口,这个类的实例可以被Binding。我们在PropertyChanged的时候,输出一条信息,说明当前的CEO是谁。注意里面IsCeo为False的时候也会输出消息哦。说明谁被撤职了。
然后建立UI如下。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
UI
1
<Window x:Class="BindingRadioButton.DemoWindow"
2
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4
xmlns:model="clr-namespace:BindingRadioButton.Model"
5
Title="RadioButton Binding Lost"
6
Height="300" Width="300">
7
<Window.Resources>
8
<model:Person x:Key="First" Name="Bill Gates"/>
9
<model:Person x:Key="Second" Name="Steve Ballmer"/>
10
</Window.Resources>
11
<DockPanel Margin="12">
12
<GroupBox DockPanel.Dock="Top"
13
Header="Select a CEO for MSFT"
14
Padding="9" Margin="0,0,0,12">
15
<StackPanel>
16
<RadioButton DataContext="{StaticResource First}"
17
IsChecked="{Binding IsCeo}"
18
Content="{Binding Name}"
19
GroupName="ceo" Margin="0,0,0,5"/>
20
<RadioButton DataContext="{StaticResource Second}"
21
IsChecked="{Binding IsCeo}"
22
Content="{Binding Name}"
23
GroupName="ceo"/>
24
</StackPanel>
25
</GroupBox>
26![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
27
<Label DockPanel.Dock="Top" Content="Message:"
28
Padding="0" Margin="0,0,0,5"/>
29
<TextBox Name="messageBox"/>
30
</DockPanel>
31
</Window>
其中的messageBox用于显示Trace输出的消息。运行结果如下。
![](https://images.cnblogs.com/cnblogs_com/nankezhishi/WPFBug/RadioButton/Run.png)
图1. 刚启动
现在MSFT没有CEO,我们点Steve Ballmer选择一个之后如图2。
![](https://images.cnblogs.com/cnblogs_com/nankezhishi/WPFBug/RadioButton/SelectOne.png)
图2. 选择一个
然后选择另一个作为CEO,之后如图3。
![](https://images.cnblogs.com/cnblogs_com/nankezhishi/WPFBug/RadioButton/SelectAnotherOne.png)
图3. 选择另一个
有心的读者应该已经可以看出,到这里已经可以证明有一个绑定已经失效了。不过我们继续用更明显的方式继续点点。
再选回Steve Ballmer,如图4。
![](https://images.cnblogs.com/cnblogs_com/nankezhishi/WPFBug/RadioButton/SelectAnotherOneAgain.png)
图4. 再选择一个
结果再没有消息出来了。很明显,Binding失效了。
根据“小明”同学的回复,此BUG已经在.NET4.0中修复。
同系列的其它文章:
[WPF Bug清单](序)与之(1)——可以多选的单选ListBox
[WPF Bug清单]之(3)——暗中创建文件的打开文件对话框
[WPF Bug清单]之(4)——点击RadioButton的空白没有反应
[WPF Bug清单]之(5)——隐藏模态对话框后变成非模态
[WPF Bug清单]之(6)——Button的IsCancel属性失效
[WPF Bug清单]之(7)——顽固的Error Template
[WPF Bug清单]之(8)——RowDefinition中MaxHeight在一定条件下失效
[WPF Bug清单]之(9)——消失的光标