C# WPF-如何告诉是什么引发了ComboBox\u SelectionChanged事件

C# WPF-如何告诉是什么引发了ComboBox\u SelectionChanged事件,c#,wpf,combobox,selectionchanged,C#,Wpf,Combobox,Selectionchanged,是否有任何方法可以告诉您ComboBox_SelectionChanged事件是如何在WPF中引发的 也就是说,引发事件的原因是用户交互,还是它绑定到的属性发生了更改?简短回答:不。不应该有区别,在这两种情况下,选择都发生了更改,这才是最重要的。 要确定这是否是用户交互,您必须监视其他事件的组合,如下拉打开/关闭和按键向下/向上,以及触控笔*事件 在ComboBox.SelectionChanged事件中,发件人始终是ComboBox,SelectionChangedEventArgs中没有任何

是否有任何方法可以告诉您ComboBox_SelectionChanged事件是如何在WPF中引发的


也就是说,引发事件的原因是用户交互,还是它绑定到的属性发生了更改?

简短回答:不。不应该有区别,在这两种情况下,选择都发生了更改,这才是最重要的。
要确定这是否是用户交互,您必须监视其他事件的组合,如下拉打开/关闭和按键向下/向上,以及触控笔*事件

在ComboBox.SelectionChanged事件中,发件人始终是ComboBox,SelectionChangedEventArgs中没有任何内容可以帮助您

我想到了两种解决办法。您可以在绑定上使用转换器,也可以检查堆栈跟踪以查看堆栈中是否存在System.Windows.Controls.Primitives.Selector.OnSelectedItemsCollectionChanged(对象,NotifyCollectionChangedArgs)。堆栈检查非常难看,是一种糟糕的做法,在部分信任的环境中不起作用。所以我只描述另一个

在绑定上使用转换器检测更改源

此解决方案相对干净,但需要更改绑定。当事情没有改变时,它有时也会通知你

步骤1:创建不进行转换但具有“Converted”事件和“ConvertedBack”事件的转换器:

步骤2:将绑定设置为使用此转换器的新实例(不要像通常那样使用资源字典或静态属性共享转换器实例)


现在,将从绑定过程中调用ComboBoxSelectedValue\u Converted和ComboBoxSelectedValue\u ConvertedBack方法

警告:如果在这些事件中引发异常,将破坏绑定

如果无法修改执行绑定的XAML

如果您无法控制创建绑定的XAML(例如,您使用的是附加属性),那么您仍然可以在事实发生后加入转换器。在这种情况下,您的转换器类需要链接到先前声明的转换器,您必须克隆绑定并安装新的绑定(一旦使用它们,它们是不可变的),并且还必须处理多绑定(如果您希望支持它们)

最后一个音符

需要确定更改是由用户还是属性进行的实际上可能是UI设计不佳的症状,通常是由于用户不真正了解自己的需求造成的


我曾经做过几个项目,最终用户指定“当我更改这个组合框时”会发生这样或那样的事情。在几乎所有情况下,事实证明应用程序在某些用例中都会表现出意外的行为,我们找到了更好的方法来实现这一目标。在许多情况下,用户真正想要的是“当此值首次与数据库中的值不同时”或“当此值不再是默认值时”或“当此值为5时”。我也遇到了此类问题,并使用布尔
bInternalChange
变量解决了它

想象一下,一个界面将°C转换为°F,然后返回两个组合框。在第一个选项中选择值将更新第二个选项,在第二个选项中选择值将更新第一个选项。如果不区分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;
    }
}

对于刚刚删除答案的人的一条评论:您可能发现发送者总是一个组合框,但我想您想知道您的方法体可以简化为if(发送者是组合框),然后{blah}else{blah}发送方总是组合框,因为它是触发事件的逻辑控件。+1不知道为什么有人否决了这个答案。它是正确的,包含有用的信息,即使它没有解决最初的问题。Drew是正确的,发送者总是选择更改的组合框。回答得很好。非常感谢您抽出时间来整理这些内容。我也使用了这个解决方案。如果对bInternalChange使用try..finally,则更可靠,否则转换代码或值更新中的任何异常都将导致同步停止工作。我经常将其封装在一个对象中,例如:使用(var change=ChangeTracker.StartChange())if(change)ComboBoxC.SelectedValue=。。。;StartChange()返回可隐式转换为bool的IDisposable。
<ComboBox ...>
  <ComboBox.SelectedValue>
    <Binding Path="..." ...>
      <Binding.Converter>
        <local:EventingConverter
          Converted="ComboBoxSelectedValue_Converted"
          ConvertedBack="ComboBoxSelectedValue_ConvertedBack" />
      </Binding.Converter>
    </Binding>
  </ComboBox.SelectedValue>
</ComboBox>
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;
    }
}