分享
 
 
 

在完成所有绑定后仍然有许多要进行的操作 (转)

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

发布日期: 09/19/2004 | 更新日期: 09/19/2004

Chris Sells

Microsoft Corporation

摘要: Chris Sells 探究视图如何维持当前项目以及视图如何支持筛选和排序。然后,他研究绑定到一种类型的数据并映射到另一种类型的数据的转换,或者位于不同范围的值中的转换。

下载 PersonBinding2.msi 示例文件。

本页内容

我们所处的位置

当前项目

排序

筛选

转换程序

我们所处的位置

我们所处的位置

请回忆一下上一篇文章中,我们希望能够直接将对象和对象的集合绑定到 Avalon UI 元素。作为一个示例,以下代码显示了我们用于探究绑定在 Avalon 中数据的 Person 类。

class Person : IPropertyChange {

public event PropertyChangedEventHandler PropertyChanged;

void FirePropertyChanged(string propertyName) {

if( this.PropertyChanged != null ) {

PropertyChanged(this,

new PropertyChangedEventArgs(propertyName));

}

}

string name;

public string Name {

get { return this.name; }

set {

this.name = value;

FirePropertyChanged("Name");

}

}

int age;

public int Age {

get { return this.age; }

set {

this.age = value;

FirePropertyChanged("Age");

}

}

...

}

IPropertyChange 接口由 Person 类实现,以通知绑定到实例的任意控件,其中一个属性已经更改。相反,公共属性让绑定控件的数据可以访问每个属性的当前值,并应用 UI 中发起的变化。图 1 中的 Name 和 Age TextBox 控件显示了 Person 对象的一个实例,该对象绑定到每个控件的 TextContent 属性。

图 1. 管理使用 Avalon 数据绑定的 Person 对象的列表

返回页首

当前项目

在 Name 和 Age TextBox 控件绑定到单个对象时,Persons ListBox 控件绑定到 Person 对象的集合中。由于 ListBox 中的选择发生了变化,当前项目 也发生变化,所有绑定控件的数据按照它们认为合适的方式进行处理。例如,如图 1 所示,通过突出显示其列表中的对象,ListBox 反映了当前项目,同时 TextBox 将仅显示当前项目的绑定属性值。当前,跟踪哪个项目是由数据的视图 来管理的。视图是一个位于数据和共享数据视图的控件之间的对象,管理着像当前项目、筛选和排序这样的操作。实际上,在 Avalon 中,完全不需要绑定到数据,而是使用程序员或 Avalon 提供的数据视图。

例如,以下代码显示了如何使用默认视图来更新上一篇文章中的 Show 按钮实现,以显示当前选定的项目:

class Window1 : Window {

ArrayListDataCollection persons =

new ArrayListDataCollection();

void Window1_Loaded(object sender, EventArgs e) {

persons.Add(new Person("John", 10));

persons.Add(new Person("Tom", 8));

this.DataContext = this.persons;

showButton.Click += showButton_Click;

birthdayButton.Click += birthdayButton_Click;

addPersonButton.Click += addPersonButton_Click;

}

void showButton_Click(object sender, ClickEventArgs e) {

ListCollectionView view =

(ListCollectionView)Binding.GetView(persons);

Person person = (Person)view.CurrentItem.Current;

MessageBox.Show(

string.Format("Name is '{0}' and you are {1} years old",

person.Name,

person.Age));

}

...

}

这个 Show 按钮单击处理程序代码调用 Binding 对象上的静态 GetView 方法,该对象会返回与 person 数据相关联的默认视图。回忆 persons 字段是 ArrayListDataCollection 的实例(您将会想到我的上一篇文章),它是 ArrayList 类的子类,该类添加 ICollectionChange 接口的实现,以便绑定到集合的控件(如 Persons ListBox)可以注册集合本身更改时的通知。

如果已经获得要绑定的项目集合,从 GetView 方法返回的视图对象的类型将成为 ListCollectionView 类的派生,它将进一步向下延续基类 CollectionView 的继承链:

namespace System.Windows.Data {

public class ListCollectionView :

ContextAffinityCollectionView, ICurrentItem, IComparer {

public override SortDescription[] Sort { get; set; }

public override bool Contains(object item);

public ListCollectionView(System.Collections.IList list);

public override int Count { get; }

public override void Refresh();

public override bool ContainsItem(object item);

public override IEnumerator GetEnumerator();

public override int IndexOf(object item);

public IContains CustomFilter { get; set; }

public override bool CanSort { get; }

public IComparer CustomSort { get; set; }

}

public abstract class ContextAffinityCollectionView :

CollectionView {

}

}

namespace System.ComponentModel {

public abstract class CollectionView :

IEnumerable, ICollectionChange {

public virtual ICurrentItem CurrentItem { get; }

...

}

}

当用户更改绑定 ListBox 中的选择时,CollectionView 基类中的 CurrentItem 属性发生变化,然后绑定控件的其他数据使用该属性来显示它们的内容。图 2 显示了这种关系。

图 2. 项目、当前项目、视图和绑定控件

该视图还用于比只维护当前项目更不常用的任务,例如排序和筛选。

返回页首

排序

由于视图始终位于绑定控件的数据和数据本身之间。这意味着可能会贸然出现我们不希望显示的数据(这称为筛选,且它将被直接覆盖),并且可能会更改数据显示的顺序(排序)。最简单的排序方法就是设置视图的 Sort 属性:

void sortButton_Click(object sender, ClickEventArgs e) {

ListCollectionView view =

(ListCollectionView)Binding.GetView(persons);

if( view.Sort.Length == 0 ) {

view.Sort = new SortDescription[] {

new SortDescription("Name", ListSortDirection.Ascending),

new SortDescription("Age", ListSortDirection.Descending),

};

}

else {

view.Sort = new SortDescription[0];

}

view.Refresh();

}

请注意由要进行排序的属性名称和顺序(升序或降序)构建的 SortDescription 对象数组的使用。还要注意对视图对象上的 Refresh 的调用。当前,这要求使用视图的新属性来刷新绑定控件(尽管希望在 Longhorn 的将来的版本中不要求对 Refresh 显式调用)。

SortDescription 对象的数组应该涵盖大多数情况,但是如果想要更多的控件,可以通过实现 IComparer 接口为视图提供自定义排序对象。

void sortButton_Click(object sender, ClickEventArgs e) {

ListCollectionView view =

(ListCollectionView)Binding.GetView(persons);

if( view.CustomSort == null ) {

view.CustomSort = new PersonSorter();

}

else {

view.CustomSort = null;

}

view.Refresh();

}

class PersonSorter : IComparer {

public int Compare(object x, object y) {

Person lhs = (Person)x;

Person rhs = (Person)y;

// Sort Name ascending and Age descending

int nameCompare = lhs.Name.CompareTo(rhs.Name);

if( nameCompare != 0 ) return nameCompare;

int ageCompare = 0;

if( lhs.Age < rhs.Age ) ageCompare = -1;

else if( lhs.Age > rhs.Age ) ageCompare = 1;

return ageCompare;

}

}

这个自定义排序实现碰巧与以前排序说明的集合具有相同的行为,但您可以完成任何想要进行的操作来确定对象在数据绑定控件中的存储方式。此外,将视图的 Sort 属性设置为 SortDescription 对象的空数组,并且将视图的 CustomSort 属性设置为 null,可以关闭排序。

返回页首

筛选

仅仅因为所有对象按照令您高兴的某个顺序显示并不意味着您希望显示所有对象。对于出现在数据中但不属于该视图的那些恶意对象,我们需要为视图提供一个 IContains 接口的实现:

void filterButton_Click(object sender, ClickEventArgs e) {

ListCollectionView view =

(ListCollectionView)Binding.GetView(persons);

if( view.CustomFilter == null ) {

view.CustomFilter = new PersonFilter();

}

else {

view.CustomFilter = null;

}

view.Refresh();

}

class PersonFilter : IContains {

public bool Contains(object item) {

Person person = (Person)item;

// Filter adult Persons

return person.Age >= 18;

}

}

这种筛选实现仅筛选成年人,但自定义筛选对象可以完成您想做的所有操作。同样,将视图的 CustomFilter 属性设置为 null 可以关闭筛选。

返回页首

转换程序

排序和筛选是处理控件显示数据方式的两个非常有用的方法。但是,如果我们希望对数据进一步操作,而不仅仅是将其作为字符串显示,又该如何呢?例如,设想我们希望根据要显示的 Person 对象的年龄来更改 ListBox 中项目的颜色。恢复我们用于显示 ListBox 中每个 Person 对象的样式:

<Style def:Name="PersonStyle">

<Style.VisualTree>

<FlowPanel>

<Text TextContent="*Bind(Path=Name)" />

<Text TextContent=":" />

<Text TextContent="*Bind(Path=Age)" />

<Text TextContent=" years old" />

</FlowPanel>

</Style.VisualTree>

</Style>

注意,这段代码绑定到 Text 元素的 TextContent 属性,而 Text 元素则构成了用于从列表框中呈现项目的 PersonStyle 样式。我们没有理由不绑定 Foreground 属性,而绑定 TextContent 属性:

<!-- NOTE: Need more to bind the Foreground to the Age -->

<Text TextContent="*Bind(Path=Age)" Foreground="*Bind(Path=Age)" />

但是,由于 Age 是 Int32 类型,而 Foreground 是

Brush 类型,所以需要从 Int32 映射到

Brush它应用到从 Age 绑定到 Foreground 的数据。这就是转换程序 的工作。转换程序是 IDataTransformer 接口的实现(与摧毁恶势力的诡计无关)。根据需要,转换程序可以用于将数据从源(如 Person 对象的 Age)转换到目标(如 Foreground

Brush),或者相反(尽管反向转换只有在控件中的数据可以更改的情况下才有必要,如 TextBox 及其 TextContent 属性)。要在 Int32Age 和

BrushForeground 之间映射,我们需要实现自定义 IDataTransformer 接口的 Transform 方法,如下所示:

public class AgeTransformer : IDataTransformer {

public object InverseTransform(object obj, ...) {

// Not mapping back from Brush to Int32

return obj;

}

public object Transform(object obj, DependencyProperty dp, ...) {

int age = (int)obj;

if( dp.Name == "Foreground" ) {

// Map from Int32 to Brush

if( age > 18 ) {

return System.Windows.Media.Brushes.Red;

}

else {

return System.Windows.Media.Brushes.Green;

}

}

return obj;

}

}

在实现 Transform 方法的过程中,请注意要进行转换的对象作为对象的第一个参数出现。转换目标的属性作为 DependencyProperty 出现,它是一个包含很多属性的描述,但我们所使用的一个属性是 Name。这允许单个 Transformer 类在多个属性之间进行转换。

在我们获得转换程序后,我们要注意下面两个步骤:

<Window.Resources>

<!-- 1. Define a TransformerSource -->

<TransformerSource def:Name="AgeTransformer" TypeName="PersonBinding.AgeTransformer" />

<Style def:Name="PersonStyle">

<Style.VisualTree>

<FlowPanel>

<Text TextContent="*Bind(Path=Name)" />

<Text TextContent=":" />

<!-- 2. Add Transformer to Bind -->

<Text TextContent="*Bind(Path=Age)" Foreground="*Bind(Path=Age;Transformer={AgeTransformer})" />

<Text TextContent=" years old" />

</FlowPanel>

</Style.VisualTree>

</Style>

</Window.Resources>

第一步就是在 XAML 中定义名为 TransformerSource 的元素。TransformerSource 在我们将要在 XAML 中使用的用于指代自定义的转换程序类的名称和类本身的名称之间进行映射。TypeName 的格式为:

TypeName="Namespace.ClassName[,AssemblyName]"

根据上下文,AssemblyName 有时是可选的,但是想尽一切办法用于 WinHEC 版本中的各种结构。

第二步是将 TransformerSource 用于构建绑定样式中。一旦我们的自定义年龄转换程序准备就绪后,图 3 就会显示结果。

图 3. John 的年龄呈绿色,因为他小于 18 岁

而且,随着数据的更新,样式会重新应用,针对每次更改都会调用转换程序,如图 4 所示。

图 4. John 的年龄呈红色,因为他是 18 岁或大于 18 岁

返回页首

我们所处的位置

[url=http://www.msdn.microsoft.com/longhorn/understanding/columns/default.aspx?pull=/library/en-us/dnfoghorn/html/foghorn06252004.asp]本系列的第一部分中,我们曾经研究过有关将对象和对象集合绑定到项目和列表控件的数据的基础知识,包括包含数据的对象和集合(IPropertyChange 和 ICollectionChange)的要求。在第二部分中,我们探究了视图如何维持当前项目以及视图如何支持筛选和排序。我们还探究了绑定到一种类型的数据,然后将其映射到另一种类型的数据或者位于不同范围的值中的转换。所有这些功能使 Avalon 数据绑定极其强大和灵活。在本系列的最后部分中,我们将继续探究增强 Avalon 能力和灵活性的更多功能,包括在运行时选择不同的样式以应用到列表中的每个项目。最后,我们将看到所有这些数据绑定功能如何应用到手头的问题 - 实现 Solitaire。

致谢

我要再次感谢 Namita Gupta,她是 Avalon 数据绑定的程序经理。尽管从事有关这项技术方面的工作已有三年时间,她仍然对我所有比较浅薄的问题给予耐心地答复,并一直倾听我的抱怨。

同时,像往常一样,我要感谢 Mike Weinhardt 所制作的非常酷的图片(本例中的图 2)。他已经是这方面的高手了。

参考资料

Crazy About Avalon Data Binding,作者 Chris Sells

Avalon Styles Overview,Longhorn SDK

Avalon Data Binding,Longhorn SDK

Avalon Newsgroup

Chris Sells 是 MSDN Online 的内容战略家,当前专注于研究 Longhorn(Microsoft 的下一个操作系统)。他已经编写了很多书籍,包括 Mastering Visual Studio .NET 和 Windows Forms Programming in C#。在业余时间,Chris 主持各种会议,指导 Genghis 可用源项目,和 Rotor 一起休闲娱乐,并且还经常在 blogsphere 中发表一些文章。有关 Chris 及其各种项目的详细信息,请访问 http://www.sellsbrothers.com

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有