有什么方法可以告诉 ComboBox_SelectionChanged 事件是如何在 WPF 中引发的。
也就是说,事件是由于用户交互而引发的,还是由于它所绑定的属性发生变化而引发的?
有什么方法可以告诉 ComboBox_SelectionChanged 事件是如何在 WPF 中引发的。
也就是说,事件是由于用户交互而引发的,还是由于它所绑定的属性发生变化而引发的?
在 ComboBox.SelectionChanged 事件中,发送者始终是 ComboBox,SelectionChangedEventArgs 中没有任何内容可以帮助您。
我想到了两个解决方案。您可以在绑定上使用转换器,或者您可以检查堆栈跟踪以查看 System.Windows.Controls.Primitives.Selector.OnSelectedItemsCollectionChanged(object, NotifyCollectionChangedArgs) 是否在堆栈中。堆栈检查非常难看,是一种不好的做法,并且在部分信任环境中不起作用。所以我只描述另一个。
在绑定上使用转换器来检测更改源
此解决方案相对干净,但需要更改绑定。当事情没有改变时,它有时也会通知您。
第 1 步:创建一个不进行转换但具有“Converted”事件和“ConvertedBack”事件的转换器:
public EventingConverter : IValueConverter
{
public event EventHandler Converted;
public event EventHandler ConvertedBack;
public object Convert(object value, ...)
{
if(Converted!=null) Converted(this, EventArgs.Empty);
return value;
}
public object ConvertBack(object value, ...)
{
if(ConvertedBack!=null) ConvertedBack(this, EventArgs.Empty);
return value;
}
}
第 2 步:将绑定设置为使用此转换器的新实例(不要像通常那样使用资源字典或静态属性共享转换器实例)
<ComboBox ...>
<ComboBox.SelectedValue>
<Binding Path="..." ...>
<Binding.Converter>
<local:EventingConverter
Converted="ComboBoxSelectedValue_Converted"
ConvertedBack="ComboBoxSelectedValue_ConvertedBack" />
</Binding.Converter>
</Binding>
</ComboBox.SelectedValue>
</ComboBox>
现在将从绑定过程中调用您的 ComboBoxSelectedValue_Converted 和 ComboBoxSelectedValue_ConvertedBack 方法。
警告:如果您在这些事件中抛出异常,您将破坏绑定。
如果您无法修改执行绑定的 XAML
如果您无法控制创建绑定的 XAML(例如,您正在使用附加属性),您仍然可以在事后添加转换器。在这种情况下,您的转换器类将需要链接到先前声明的转换器,您必须克隆 Binding 并安装新的(一旦使用它们就是不可变的),您还必须处理 MultiBindings (如果你想支持他们)。
最后说明
需要确定更改是由用户还是属性做出的,实际上可能是 UI 设计不佳的症状,通常是由于用户并不真正了解自己的需求造成的。
我从事过几个项目,最终用户指定“当我更改此 ComboBox 时”会发生某事。几乎在每种情况下,应用程序在某些用例中都会出现意外行为,我们找到了实现目标的更好方法。在许多情况下,用户真正想要的是“当这个值第一次不同于数据库中的值时”或“当这个值不再是默认值时”或“当这个值是 5 时”。
简短的回答:没有。应该没有区别,在这两种情况下,选择都发生了变化,这才是最重要的。要确定它是否是用户交互,您必须监视其他事件的组合,例如 DropDownOpened/Closed 和 KeyDown/Up,以及 Stylus* 事件。
我也遇到了这种问题,并使用布尔bInternalChange
变量解决了它。
想象一个界面将°C 转换为°F,并使用两个 ComboBoxes 转换回来。在第一个中选择一个值会更新第二个的选择,在第二个中选择一个值会更新第一个。如果您不区分 UI 更改和内部更改,它会创建一个无限循环。
bool bInternalChange = false;
private void ComboBoxF_SelectionChanged(...)
{
if (!bInternalChange)
{
bInternalChange = true;
ComboBoxC.SelectedValue = ConvertFtoC(...);
bInternalChange = false;
}
}
private void ComboBoxC_SelectionChanged(...)
{
if (!bInternalChange)
{
bInternalChange = true;
ComboBoxF.SelectedValue = ConvertCtoF(...);
bInternalChange = false;
}
}