【问题标题】:Using multiple IValueConverters in single Binding to enjoy data change notification在单个 Binding 中使用多个 IValueConverter 享受数据更改通知
【发布时间】:2014-04-24 21:11:39
【问题描述】:

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

假设我有一个带有TextBox 的应用程序用于搜索人员(在数据库、本地存储等中)。键入人员时,搜索完成,UI 应根据找到的人员进行更新。在我的例子中,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 对象定义了人员之间关系的有向树图,我想显示一个人的“孩子”相关人员的ListBox

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

  BindingBase relationsBinding = new MultiBinding()
  {
    Bindings =
    {
      new Binding() { Source = this, Path = new PropertyPath("Person") },
      new Binding() { Source = this, Path = new PropertyPath("DAL") }
    },
    Converter = PersonToRelationsConverter.Instance,
  };
  ListBox relationsList = new ListBox()
  {
    ItemTemplate = relationsBinding 
  };
  relationsList.SetBinding(ListBox.ItemsSourceProperty, relationsBinding);

转换器代码类似于:

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;
}

然后我可以使用 ItemTemplate 来定义每个孩子的显示方式。

它可能有效,但问题是IPersonRelations&lt;IPerson&gt; 是一个INotifyPropertyChanged 对象,我想享受它的数据更改通知。上述方式我无法享受它,因为它只是ValueConverter 的局部变量,不会返回到WPF 机制。

我猜ListBox.ItemsSource 只是IValueConverter 必须返回某些特定对象而不是INotifyPropertyChangedDependencyObject 的示例(或者不是我希望绑定监听的唯一对象) )。我希望有一些 CompositeBinding 类允许“连接”ValueConverters,因此绑定机制将监听连接“路径”中的任何值。

我是否想念一些非常简单的方法来做这样的事情?

感谢您的帮助。

【问题讨论】:

  • 你搞错了。您的转换器不应该访问 DAL 的东西,这就是 VM 应该做的。并且请不要在 WPF 的程序代码中创建或操作 UI 元素。这就是 XAML 的用途。
  • 首先,请留下代码隐藏问题,我已经在我的消息中提到了它,代码仅用于示例。其次,除了作为一个很好的设计建议之外,我不明白 MVVM 基础知识将如何解决我获取有关数据更改的通知的主要问题。

标签: c# wpf binding listbox dependency-properties


【解决方案1】:

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

所以我定义了一个中间对象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}" />

ValueConverterMiddleBinding 的代码很简单:

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
}

【讨论】:

    猜你喜欢
    • 2021-07-04
    • 2020-02-10
    • 2017-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多