在单个绑定中使用多个IValueConverter来享受数据更改通知
本文关键字:数据 通知 IValueConverter 绑定 单个 | 更新日期: 2023-09-27 17:59:52
编辑:这是问题的另一个(简单)版本:
假设我有一个带有TextBox
的应用程序,用于搜索Persons(在DB、本地存储等中)。当键入一个人时,搜索就完成了,UI应该根据找到的person更新。在我的情况下,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
对象。
因此,我必须使用Converter在TextBlock
中显示合适的人员的ShortName。我可以将短名称TextBlock
直接绑定到搜索TextBox
,并使用转换器查询和返回ShortName
属性。但是,如果我要这样做,那么如果Person.ShortName
内容发生更改(而不更改Person
对象引用本身),则TextBlock
将不会更新。
原始问题到此为止
我有一个DAL对象包含一组人。DAL对象定义了人员之间关系的有向树图,我想显示单个人员的"儿童"相关人员的ListBox
。
由于关系是在DAL中定义的,而不是在人对象中,我需要定义一个知道使用适当DAL方法的应用逻辑。因此,我使用了ListBox.ItemsSource
绑定中使用的转换器,如下所示(很抱歉使用了样式背后的代码:)。
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<IPerson>
是一个INotifyPropertyChanged
对象,我想享受它的数据更改通知。以上述方式,我无法享受它,因为它只是ValueConverter
的局部变量,不会返回到WPF机制。
我猜ListBox.ItemsSource
只是IValueConverter
必须返回某个特定对象的情况的示例,该对象不是INotifyPropertyChanged
或DependencyObject
(或者不是我希望绑定侦听的唯一对象)。我希望有一些CompositeBinding
类可以"串联"ValueConverter
,这样绑定机制就会监听串联"路径"中的任何值。
我是不是错过了一些做这种事的简单方法?
谢谢你的帮助。
我找到的唯一方法是实现一个绑定到中间值的"中间"依赖对象。
因此,我定义了一个中间对象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}" />
ValueConverter
和MiddleBinding
的代码很琐碎:
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
}