C# 在单个绑定中使用多个IValueConverter享受数据更改通知

C# 在单个绑定中使用多个IValueConverter享受数据更改通知,c#,wpf,binding,listbox,dependency-properties,C#,Wpf,Binding,Listbox,Dependency Properties,编辑:下面是问题的另一个(更简单)版本: 假设我有一个带有文本框的应用程序,用于搜索人员(在数据库、本地存储等中)。当输入一个人时,搜索就完成了,用户界面应该根据找到的人进行更新。在我的例子中,UI包含一个TextBlock,显示属性Person.ShortName的值 为此,我需要使用具有以下想法的助手类PersonQueryCreator: public class PersonQueryCreator : DependencyObject { #region Static Data M

编辑:下面是问题的另一个(更简单)版本:

假设我有一个带有
文本框的应用程序,用于搜索人员(在数据库、本地存储等中)。当输入一个人时,搜索就完成了,用户界面应该根据找到的人进行更新。在我的例子中,UI包含一个
TextBlock
,显示属性
Person.ShortName
的值

为此,我需要使用具有以下想法的助手类
PersonQueryCreator

public class PersonQueryCreator : DependencyObject
{
  #region Static Data Members

  public static DependencyProperty ShortNameToSearchProperty = DependencyProperty.Register("ShortNameToSearch", typeof(string), typeof(PersonQueryCreator), new PropertyMetadata(UpdateQueryObject));
  private static DependencyPropertyKey QueryPropertyKey = DependencyProperty.RegisterReadOnly("Query", typeof(string), typeof(PersonQueryCreator), new PropertyMetadata());
  public static DependencyProperty QueryProperty = QueryPropertyKey.DependencyProperty;

  #endregion

  #region Public Properties

  public string ShortNameToSearch
  {
    get { return (string)GetValue(ShortNameToSearchProperty); }
    set { SetValue(ShortNameToSearchProperty, value); }
  }

  public object Query
  {
    get { return (string)GetValue(QueryProperty); }
  }

  #endregion

  #region Public Methods

  public static IPerson SearchForPerson(object query)
  {
    ...
  }

  #endregion
}
这个类有一些属性我可以设置来创建查询(在这个例子中我将使用
PersonQueryCreator.ShortNameToSearch
),它生成一个查询对象,该对象在属性
PersonQueryCreator.query
中返回。此类还提供了一个静态方法
PersonQueryCreator.SearchForPerson
,该方法接受查询参数并返回
Person
对象

因此,我必须使用转换器在
TextBlock
中显示正确的人的简称。我可以将短名称
TextBlock
直接绑定到搜索
TextBox
,并使用转换器查询并返回
ShortName
属性。但是,如果我这样做,那么如果
Person.ShortName
内容发生更改(不更改
Person
对象引用本身),则
TextBlock
将不会更新

原来的问题来了

我有一个DAL对象包含一组人。DAL对象定义了人与人之间关系的有向树形图,我想显示一个人与“孩子”相关的人的
列表框

由于关系是在DAL中定义的,而不是在person对象中定义的,因此我需要定义一个应用程序逻辑,它知道如何使用适当的DAL方法。因此,我使用了一个转换器,该转换器在
ListBox.ItemsSource
绑定中使用,如下所示(很抱歉使用代码隐藏样式:)

转换器代码类似于:

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
  IPerson person = values[0] as IPerson;
  IDAL<IPerson> dal = values[1] as IDAL<IPerson>;

  IPersonRelations<IPerson> relations = dal.GetPersonRelations(person);
  IEnumerable<IPerson> children = relations.OutRelated;
  return children;
}
公共对象转换(对象[]值,类型targetType,对象参数,System.Globalization.CultureInfo区域性) { IPerson person=值[0]为IPerson; IDAL dal=作为IDAL的值[1]; IPersonRelations=dal.GetPersonRelations(person); IEnumerable children=relations.OutRelated; 返回儿童; }
然后我可以使用ItemTemplate来定义每个子角色的显示方式

它可能会起作用,但问题是,
IPersonRelations
是一个
INotifyPropertyChanged
对象,我希望它的数据更改通知能让我满意。以上面提到的方式,我不能享受它,因为它只是
ValueConverter
的一个局部变量,不会返回到WPF机制

我猜
ListBox.ItemsSource
IValueConverter
必须返回某个特定对象的唯一示例,该对象不是
INotifyPropertyChanged
DependencyObject
(或者不是我希望绑定侦听的唯一对象)。我希望有一些
CompositeBinding
类允许“连接”
ValueConverter
s,这样绑定机制将侦听连接的“路径”中的任何值

我是否错过了做这件事的简单方法


谢谢您的帮助。

我发现唯一的方法是实现一个绑定到中间值的“中间”依赖项对象

因此,我定义了一个中间对象
BindingMiddle
,它存储查询结果,并且仅在以后绑定到结果的
ShortName
属性,如下所示:

<Window.Resources>

  <local:PersonQueryCreator x:Key="QueryCreator" />

  <local:BindingMiddle x:Key="QueriedPersonContainer">
   <local:BindingMiddle.MiddleValue>
    <Binding Source="{StaticResource QueryCreator}" Path="Query">
      <Binding.Converter>
        <infra:PersonSearcherConverter />
      </Binding.Converter>
    </Binding>
   </local:BindingMiddle.MiddleValue>
  </local:BindingMiddle>

</Window.Resources>

...

    <TextBox Text="{Binding Source={StaticResource QueryCreator}, Path=ShortNameToSearch}"/>

    <TextBlock Text="{Binding Source={StaticResource QueriedPersonContainer}, Path=MiddleValue.ShortName}" />

你把一切都搞错了。您的转换器不应该访问DAL内容,这是VM应该做的。请不要在WPF的过程代码中创建或操作UI元素。这就是XAML的用途。首先,请留下代码问题,我已经在我的消息中提到了它,代码仅用于示例。其次,除了作为一个很好的设计建议之外,我不明白MVVM基本原理如何解决我在获取数据更改通知方面的主要问题。
<Window.Resources>

  <local:PersonQueryCreator x:Key="QueryCreator" />

  <local:BindingMiddle x:Key="QueriedPersonContainer">
   <local:BindingMiddle.MiddleValue>
    <Binding Source="{StaticResource QueryCreator}" Path="Query">
      <Binding.Converter>
        <infra:PersonSearcherConverter />
      </Binding.Converter>
    </Binding>
   </local:BindingMiddle.MiddleValue>
  </local:BindingMiddle>

</Window.Resources>

...

    <TextBox Text="{Binding Source={StaticResource QueryCreator}, Path=ShortNameToSearch}"/>

    <TextBlock Text="{Binding Source={StaticResource QueriedPersonContainer}, Path=MiddleValue.ShortName}" />
public class BindingMiddle : DependencyObject
{
  #region Static Data Members

  public static DependencyProperty MiddleValueProperty = DependencyProperty.Register("MiddleValue", typeof(object), typeof(BindingMiddle));

  #endregion

  #region Public Properties

  public object MiddleValue
  {
    get { return GetValue(MiddleValueProperty); }
    set { SetValue(MiddleValueProperty, value); }
  }

  #endregion
}

public class PersonQueryCreator : DependencyObject
{
  #region Static Data Members

  public static DependencyProperty ShortNameToSearchProperty = DependencyProperty.Register("ShortNameToSearch", typeof(string), typeof(PersonQueryCreator), new PropertyMetadata(ShortNameChanged));
  private static DependencyPropertyKey QueryPropertyKey = DependencyProperty.RegisterReadOnly("Query", typeof(string), typeof(PersonQueryCreator), new PropertyMetadata());
  public static DependencyProperty QueryProperty = QueryPropertyKey.DependencyProperty;

  #endregion

  #region Public Properties

  public string ShortNameToSearch
  {
    get { return (string)GetValue(ShortNameToSearchProperty); }
    set { SetValue(ShortNameToSearchProperty, value); }
  }

  public object Query
  {
    get { return (string)GetValue(QueryProperty); }
  }

  #endregion

  #region Public Methods

  public static IPerson SearchForPerson(object query)
  {
    string shortNameToSearch = query as string;
    return new Prayer(shortNameToSearch, shortNameToSearch);
  }

  #endregion

  #region Private Methods

  private static void ShortNameChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
  {
    ((PersonQueryCreator)o).SetValue(PersonQueryCreator.QueryPropertyKey, e.NewValue);
  }

  #endregion
}