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
}