WPF - 如何知道引发了什么ComboBox_SelectionChanged事件

问题描述:

有什么办法可以告诉如何在WPF中引发ComboBox_SelectionChanged事件。WPF - 如何知道引发了什么ComboBox_SelectionChanged事件

也就是说,是由于用户交互引起的事件,还是作为其绑定属性更改的结果?

+0

对谁只是删除了他的答案的家伙注释:你可能想通了,发送者是*始终*组合框,但我想你想知道你的方法体可以简化为IF(发件人是ComboBox)then {blah} else {blah} – 2009-11-06 21:25:42

在ComboBox.SelectionChanged事件中,发件人为总是组合框,并且在SelectionChangedEventArgs中没有任何内容可以帮助您。

对此我有两种解决方案。您可以在绑定上使用转换器,或者您可以检查堆栈跟踪以查看System.Windows.Controls.Primitives.Selector.OnSelectedItemsCollectionChanged(object,NotifyCollectionChangedArgs)是否在堆栈中。堆栈检查非常难看,是一种不好的做法,并且不适用于部分信任环境。所以我只会描述另一个。

使用对结合的转换器,以检测改变源

该解决方案是比较干净,但需要改变的结合。当事情没有改变时,它有时也会通知你。

第1步:创建一个转换器,则不进行转换,但有一个“转换”事件和“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无法控制你仍然可以进来,并添加事实之后的转换器。在这种情况下,你的转换器类将需要链接到之前声明的转换器,你必须克隆绑定并安装新的(一旦它们被使用后它们是不可变的),并且你还必须处理MultiBindings (如果你想支持他们)。

最后说明

需要确定是否改变是由用户作出或财产可能实际上是贫穷的UI设计症状,通常是从谁不真正了解自己的用户产生要求。

我已经有过几个项目,我最终用户指定了这样的事情发生时“当我改变这个组合框”。在几乎所有情况下,事实证明,应用程序在某些使用情况下会表现出意外,我们找到了更好的方法来实现目标。在许多情况下,用户真正想要的是“当此值首先与数据库中的值不同”或“此值不再默认时”或“此值为5时”。

+0

精美的回答。非常感谢你花时间把这些放在一起。 – mattdlong 2009-11-06 22:22:36

+0

很好的答案:) – slugster 2009-11-06 23:54:46

简答:没有。不应该有任何区别,在这两种情况下,选择都发生了变化,这是重要的。 要确定它是否是用户交互,您必须监视其他事件的组合,如DropDownOpened/Closed和KeyDown/Up以及Stylus *。

+0

发件人始终是ComboBox,因为它是触发事件的逻辑控件。 – 2009-11-06 22:09:41

+0

+1不知道为什么有人低估了这个答案。这是正确的,并包含有用的信息,尽管它没有解决原始问题。德鲁是正确的,发件人总是选择被改变的组合框。 – 2009-11-06 22:17:31

我也遇到过这样的问题,并用布尔型bInternalChange变量解决了这个问题。

想象一下将接口从°C转换成°F并将接口转换为两个ComboBox。在第一个中选择一个值将更新第二个的选择,并在第二个中选择一个值更新第一个值。如果您不区分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; 
    } 
} 
+0

我也使用过这个解决方案。如果您对bInternalChange使用try..finally,则更可靠,否则转换代码或值更新中的任何地方的异常都会导致您的同步停止工作。我经常把它封装在一个对象中,例如:using(var change = ChangeTracker.StartChange())if(change)ComboBoxC.SelectedValue = ...; StartChange()返回一个可隐式转换为bool的IDisposable。 – 2009-11-07 00:10:55