WPF学习之深入浅出话模板_第1页
WPF学习之深入浅出话模板_第2页
WPF学习之深入浅出话模板_第3页
WPF学习之深入浅出话模板_第4页
WPF学习之深入浅出话模板_第5页
已阅读5页,还剩67页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、WPF学习之深入浅出话模板 图形用户界面应用程序较之控制台界面应用程序最大的好处就是界面友好、数据显示直观。CUI程序中数据只能以文本的形式线性显示,GUI程序则允许数据以文本、列表、图形等多种形式立体显示。用户体验在GUI程序设计中起着举足轻重的作用-用户界面设计成什么样看上去才足够的漂亮?控件如何安排才简单易用并且少犯错误?这些都是设计师需要考虑的问题。WPF系统不但支持传统的Winfrom编程的用户界面和用户体验设计,更支持使用专门的设计工具Blend进行专业设计,同时还推出了以模板为核心的新一代设计理念。1.1     模板的内涵从字面上看,模板就是“具有一定规格的

2、样板”,有了它,就可以依照它制造很多一样是实例。我们常把看起来一样的东西称为“一个模子里面刻出来的。”就是这个道理。然而,WPF中的模板的内涵远比这个深刻。Binding和基于Binding数据驱动UI是WPF的核心部分,WPF最精彩的部分是什么呢?依我看,既不是美轮美奂的3D图形,也不是炫目多彩的动画,而是默默无闻的模板(Template)。实际上,就连2D/3D绘图也常常是为它锦上添花。Templdate究竟有什么能力能够使得它在WPF体系中获此殊荣呢?这还要从哲学谈起,“形而上者谓之道,形而下者谓之器”,这句话出自易经,大意是我们能够观察到的世间万物形象之上的抽象的结果就是思维,而形象之

3、下掩盖的就是其本质。显然,古人已经注意到“形”是连接本质和思维的枢纽,让我们把这句话引入计算机世界。“形而上者谓之道”指的就是基于现实世界对万物进行抽象封装,理顺它们之间的关系,这个“道”不就是面向对象思想吗!如果再把面向对象进一步提升、总结出最优的对象组合关系,“道”就上升为设计模式思想。“形而下者谓之气”指的是我们能够观察到的世间万物都是物质类容的本质表现形式。“本质与表现”或者说“类容与形式”是哲学范畴内的一对矛盾体。软件之道并非本书研究的主要类容,本书研究的是WPF。WPF全称Windows Presentation Foundation,Presentation一词的意思就是外观,呈

4、现,表现,也就是说,在WIndows GUI程序这个尺度上,WPF扮演的就是“形”的角色、是程序的外在形式,而程序的内容仍然是由数据和算法构成的业务逻辑。与WPF类似,Winform和A也都是内容的表现形式。让我们把尺度缩小到WPF内部。这个系统与程序内容(业务逻辑)的边界是Binding,Binding把数据源源不断从程序内部送出来交由界面元素来显示,又把从界面元素搜集到的数据传回程序内部。界面元素间的沟通则依靠路由事件来完成。有时候路由事件和附加事件也会参与到数据的传输中。让我们思考一个问题:WPF作为Windows的表示方式,它究竟表示的是什么?换句话说,WPF作为一种“形式”,它表现的

5、内容到底是什么?答案是程序的数据和算法-Binding传递的是数据,事件参数携带的也是数据;方法和委托的调用是算法,事件传递消息也是算法-数据在内存里就是一串串字符或字符。算法是一组组看不见摸不着的抽象逻辑,如何恰如其分的把它们展现给用户呢?加入想表达一个bool类型,同时还想表达用户可以在这两个值之间自由切换这样一个算法,你会怎么做?你一定会想使用一个CheckBox控件来满足要求;再比如颜色值实际上是一串数字,用户基本上不可能只看数字就能想象出真正的颜色,而且用户也不希望只靠输入字符来表示颜色值,这时,颜色值这一“数据内容”的恰当表现形式就是一个填充着真实颜色的色块。,而用户即可以输入值又

6、可以用取色吸管取色来设置值的“算法内容”恰当的表达方式是创建一个ColorPicker控件。相信你已经发现,控件(Control)是数据内容表现形式的双重载体。换句话说,控件即是数据的表现形式让用户可以直观的看到数据,又是算法的表现形式让用户方便的操作逻辑。作为表现形式,每个控件都是为了实现某种用户操作算法和直观显示某种数据而生,一个控件看上去是什么样子由它的“算法内容”和“数据内容决定”,这就是内容决定形式,这里,我们引入两个概念:控件的算法内容:值控件能展示哪些数据、具有哪些方法、能相应哪些操作、能激发什么事件,简而言之就是控件的功能,它们是一组相关的算法逻辑。控件的数据内容:控件具体展示

7、的数据是什么。以往的GUI开发技术(ASP.NET+Winform)中,控件内部逻辑和数据是固定的,程序员不能改变;对于控件的外观,程序员能做的改变也非常的有限,一般也就是设置控件的属性,想改变控件的内部结构是不可能的。如果想扩展一个控件的功能或者更改器外观让其更适应业务逻辑,哪怕只是一丁点的改变,也需要创建控件的子类或者创建用户控件。造成这个局面的根本原因是数据和算法的“形式”和“内容”耦合的太紧了。在WPF中,通过引入模板微软将数据和算法的内容与形式接耦合了。WPF中的Template分为两大类:ControlTemplate:是算法和内容的表现形式,一个控件怎么组织其内部结构才能让它更符

8、合业务逻辑、让用户操作起来更舒服就是由它来控制的。它决定了控件“长成什么样子”,并让程序员有机会在控件原有的内部逻辑基础上扩展自己的逻辑。DataTemplate:是数据内容的展示方式,一条数据显示成什么样子,是简单的文本还是直观的图形就由它来决定了。Template就是数据的外衣-ControlTemplate是控件的外衣,DataTemplate是数据的外衣。下面让我们欣赏两个例子:WPF中控件不在具有固定的形象,仅仅是算法内容或数据内容的载体。你可以把控件理解为一组操作逻辑穿上了一套衣服,换套衣服就变成了另外一个模样。你看到的控件默认形象实际上就是出厂时微软为它穿上的默认衣服。看到下面图

9、中的温度计,你是不是习惯性的猜到是由若干控件和图形拼凑起来的UserControl呢?实际上它是一个ProgressBar控件,只是我们的设计师为其设计了一套新衣服-这套衣服改变了其一些颜色、添加了一些装饰品和刻度线并清除了脉搏动画,效果如下图:WPF中数据显示成什么样子可以由自己来决定。比如下面这张图,只是为数据条目准备了一个DataTemplate,这个DataTemplate中用binding把一个TextBlock的Text属性值关联到数据对象的Year属性上、把一个Rectangle的Width属性和另外一个TextBlock的Text属性关联到数据对象的Price属性上,并使用St

10、ackPanel和Grid为这几个控件布局。一旦应用了这个DataTemplate,单调的数据就变成了直观的柱状图,如下图所示。以往这项工作不但需要先创建用于展示数据的UserControl,还要为UserControl添加显示/回写数据的代码。如果别的项目中也需要用到这个柱状图,你要做的事情只是将这个XAML代码发给他们。其代码如下:html view plain copy print?<DataTemplate>       <Grid>     

11、      <StackPanel Orientation="Horizontal">               <Grid>                 &#

12、160; <Rectangle Stroke="Yellow" Fill="Orange" Width="Binding Price"></Rectangle>                   <TextBlock Text="Binding&#

13、160;Year"></TextBlock>               </Grid>               <TextBlock Text="Binding Price" Margin=&quo

14、t;5,0"></TextBlock>           </StackPanel>       </Grid>   </DataTemplate>   <DataTemplate> <Grid> <StackPanel Orientation="Horizont

15、al"> <Grid> <Rectangle Stroke="Yellow" Fill="Orange" Width="Binding Price"></Rectangle> <TextBlock Text="Binding Year"></TextBlock> </Grid> <TextBlock Text="Binding Price" Margin="5,0"></

16、TextBlock> </StackPanel> </Grid> </DataTemplate>我想,尽管你还没有学习什么DataTempldate,但借助前面学习的基础一样可以看个八九不离十了。1.2       数据的外衣DataTemplate“横看成岭侧成峰,远近高低各不同”庐山的美景如此,数据又何尝不是这样呢?同样一条数据,比如具有ID、Name、PhoneNumber、Address等Student的实例,放在GridView里面有时可能是简单的文本、每个单元格只显示一个属性;放在ListBox里面有时为了避免

17、单调可以在最左端显示一个64*64的小图像,再将其它信息分两行显示在其后面;如果单独显示一个学生信息则可以用类似简历的复杂格式来展现学生的全部数据。一样的内容可以用不同的形式来展现,软件设计称之为“数据-视图”模式。以往的开发技术,如MFC、Winform、A等,视图要靠UserControl来实现。WPF不但支持UserControl还支持DataTemplate为数据形成视图。不要以为DataTempldate有多难!从Control升级到DataTemplate一般就是复制,粘贴一下再改几个字符的事儿。DataTempldate常用的地方有三处,分别是:ContentControl的Co

18、ntentTempldate属性,相当于给ContentControl的内容穿衣服。ItemControl的ItemTemplate,相当于给ItemControl的数据条目穿衣服。GridViewColumn的CellTempldate属性,相当于给GridViewColumn的数据条目穿衣服。让我们用一个例子对比UserControl和DataTemplate的使用。例子实现的需求是这样的:有一列汽车数据,这列数据显示在ListBox里面,要求ListBox的条目显示汽车的厂商图标和简要参数,单击某个条目后在窗体的详细内容区显示汽车的图片和详细参数。无论是使用UserControl还是Da

19、taTemplate,厂商的Logo和汽车的照片都是要用到的,所以先在项目中建立资源管理目录并把图片添加进来。Logo文件名与厂商的名称一致,照片的名称则与车名一致。组织结构如图:首先创建Car数据类型:csharp view plain copy print?public class Car                public string AutoMark  get

20、; set;           public string Name  get; set;           public string Year  get; set;        &

21、#160;  public string TopSpeed  get; set;         public class Car public string AutoMark get; set; public string Name get; set; public string Year get; set; public string TopSpeed get; set; 为了在ListBox里面显示Car类型的数据,我们需要准备一

22、个UserControl。命名为CarListItemView。这个UserControl由一个Car类型实例在背后支持,当设置这个实例的时候,界面元素将实例的属性值显示在各个控件里。CarListItemView的XAML代码如下:html view plain copy print?<UserControl x:Class="WpfApplication1.CarListViewItem"              

23、 xmlns="             xmlns:x="    <Grid Margin="2">          <StackPanel Orientation="Horizontal"> 

24、0;            <Image x:Name="igLogo" Grid.RowSpan="3" Width="64" Height="64"></Image>              

25、;<StackPanel Margin="5,10">                  <TextBlock x:Name="txtBlockName" FontSize="16" FontWeight="Bold"></TextBlock> 

26、0;                <TextBlock x:Name="txtBlockYear" FontSize="14"></TextBlock>              </StackPanel&g

27、t;          </StackPanel>      </Grid>  </UserControl>  <UserControl x:Class="WpfApplication1.CarListViewItem" xmlns=" xmlns:x=" <Grid Margin="2"&

28、gt; <StackPanel Orientation="Horizontal"> <Image x:Name="igLogo" Grid.RowSpan="3" Width="64" Height="64"></Image> <StackPanel Margin="5,10"> <TextBlock x:Name="txtBlockName" FontSize="16" FontW

29、eight="Bold"></TextBlock> <TextBlock x:Name="txtBlockYear" FontSize="14"></TextBlock> </StackPanel> </StackPanel> </Grid></UserControl>CarlistItemView用于支持前台显示属性C#代码为:csharp view plain copy print?/ <summary> &#

30、160;/ CarListViewItem.xaml 的交互逻辑  / </summary>  public partial class CarListViewItem : UserControl        public CarListViewItem()          

31、;      InitializeComponent();              private Car car;        public Car Car          

32、;      get  return car;           set                        car = val

33、ue;              this.txtBlockName.Text = car.Name;              this.txtBlockYear.Text = car.Year;       

34、;       this.igLogo.Source = new BitmapImage(new Uri("Resource/Image/"+car.AutoMark+".png",UriKind.Relative);                   &#

35、160; / <summary> / CarListViewItem.xaml 的交互逻辑 / </summary> public partial class CarListViewItem : UserControl public CarListViewItem() InitializeComponent(); private Car car; public Car Car get return car; set car = value; this.txtBlockName.Text = car.Name; this.txtBlockYear.Text = car.Y

36、ear; this.igLogo.Source = new BitmapImage(new Uri("Resource/Image/"+car.AutoMark+".png",UriKind.Relative); 类似的原理,我们需要为Car类型准备一个详细信息视图。UserControl名称为CarDetailView,XAML部分代码如下:html view plain copy print?<UserControl x:Class="WpfApplication1.CarDetailView" 

37、0;             xmlns="             xmlns:x="    <Border BorderBrush="Black" BorderThickness="1" Corner

38、Radius="6">          <StackPanel>              <Image x:Name="imgPhoto" Width="400" Height="250"></Image>

39、;              <StackPanel Orientation="Horizontal" Margin="5,0">                  <TextBlock Te

40、xt="Name:" FontSize="20" FontWeight="Bold"></TextBlock>                  <TextBlock x:Name="txtBlockName" FontSize="20" Mar

41、gin="5,0"></TextBlock>              </StackPanel>              <StackPanel Orientation="Horizontal" Margin="

42、;5,0">                  <TextBlock Text="AutoMark:" FontWeight="Bold"></TextBlock>             

43、     <TextBlock x:Name="txtBlockAutoMark" Margin="5,0"></TextBlock>                  <TextBlock Text="Year:" FontWeight=

44、"Bold">                                        </TextBlock>   

45、0;                  <TextBlock x:Name="txtBlockYear" Margin="5,0">                  

46、  </TextBlock>                  <TextBlock Text="Top Speed:" FontWeight="Bold">            &#

47、160;                           </TextBlock>                  <Text

48、Block x:Name="txtTopSpeed" Margin="5,0">                                      

49、;  </TextBlock>              </StackPanel>          </StackPanel>      </Border>  </UserControl>

50、0; <UserControl x:Class="WpfApplication1.CarDetailView" xmlns=" xmlns:x=" <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6"> <StackPanel> <Image x:Name="imgPhoto" Width="400" Height="250&

51、quot;></Image> <StackPanel Orientation="Horizontal" Margin="5,0"> <TextBlock Text="Name:" FontSize="20" FontWeight="Bold"></TextBlock> <TextBlock x:Name="txtBlockName" FontSize="20" Margin="5,0&q

52、uot;></TextBlock> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5,0"> <TextBlock Text="AutoMark:" FontWeight="Bold"></TextBlock> <TextBlock x:Name="txtBlockAutoMark" Margin="5,0"></Text

53、Block> <TextBlock Text="Year:" FontWeight="Bold"> </TextBlock> <TextBlock x:Name="txtBlockYear" Margin="5,0"> </TextBlock> <TextBlock Text="Top Speed:" FontWeight="Bold"> </TextBlock> <TextBlock x:N

54、ame="txtTopSpeed" Margin="5,0"> </TextBlock> </StackPanel> </StackPanel> </Border></UserControl>后台支持数据大同小异:csharp view plain copy print?/ <summary>  / CarDetailView.xaml 的交互逻辑  / </summary> 

55、 public partial class CarDetailView : UserControl        public CarDetailView()                InitializeComponent();    

56、0;         private Car car;        public Car Car                get  return car;   &

57、#160;       set                        car = value;              

58、this.txtBlockName.Text = car.Name;              this.txtBlockAutoMark.Text = car.AutoMark;              this.txtBlockYear.Text = c

59、ar.Year;              this.txtTopSpeed.Text = car.TopSpeed;              this.imgPhoto.Source = new BitmapImage(new Uri("Reso

60、urce/Image/" + car.Name + ".jpg", UriKind.Relative);                     / <summary> / CarDetailView.xaml 的交互逻辑 / </summary> public partial class Ca

61、rDetailView : UserControl public CarDetailView() InitializeComponent(); private Car car; public Car Car get return car; set car = value; this.txtBlockName.Text = car.Name; this.txtBlockAutoMark.Text = car.AutoMark; this.txtBlockYear.Text = car.Year; this.txtTopSpeed.Text = car.TopSpeed; this.imgPhot

62、o.Source = new BitmapImage(new Uri("Resource/Image/" + car.Name + ".jpg", UriKind.Relative); 最后把它们组装到窗体上:html view plain copy print?<Window x:Class="WpfApplication1.Window35"          xmlns="  

63、0;     xmlns:x="        Title="Window35" Height="350" Width="623" xmlns:my="clr-namespace:WpfApplication1">      <StackPanel Orientati

64、on="Horizontal" Margin="5">          <my:CarDetailView x:Name="carDetailView1" />          <ListBox x:Name="listBoxCars" Wi

65、dth="180" Margin="5,0" SelectionChanged="listBoxCars_SelectionChanged">                        </ListBox>    &#

66、160; </StackPanel>  </Window>  <Window x:Class="WpfApplication1.Window35" xmlns=" xmlns:x=" Title="Window35" Height="350" Width="623" xmlns:my="clr-namespace:WpfApplication1"> <StackPanel Orient

67、ation="Horizontal" Margin="5"> <my:CarDetailView x:Name="carDetailView1" /> <ListBox x:Name="listBoxCars" Width="180" Margin="5,0" SelectionChanged="listBoxCars_SelectionChanged"> </ListBox> </StackPanel>

68、;</Window>窗体的后台代码如下:csharp view plain copy print?using System;  using System.Collections.Generic;  using System.Linq;  using System.Text;  using System.Windows;  using System.Windows.Controls;  using 

69、;System.Windows.Data;  using System.Windows.Documents;  using System.Windows.Input;  using System.Windows.Media;  using System.Windows.Media.Imaging;  using System.Windows.Shapes;    namespace WpfAppl

70、ication1        / <summary>      / Window35.xaml 的交互逻辑      / </summary>      public partial class Window35 : Win

71、dow                public Window35()                        InitializeComponent();  &

72、#160;           InitialCarList();                      private void listBoxCars_SelectionChanged(object sender, Sel

73、ectionChangedEventArgs e)                        CarListViewItem viewItem = e.AddedItems0 as CarListViewItem;       

74、;       if(viewItem!=null)                                carDetailView1.Car = viewItem.Car;

75、60;                                   private void InitialCarList()        

76、                List<Car> infos = new List<Car>()                new Car() AutoMark="Aodi&quo

77、t;, Name="Aodi", TopSpeed="200", Year="1990",              new Car() AutoMark="Aodi", Name="Aodi", TopSpeed="250", Year="1998

78、",              new Car() AutoMark="Aodi", Name="Aodi", TopSpeed="300", Year="2002",            

79、0; new Car() AutoMark="Aodi", Name="Aodi", TopSpeed="350", Year="2011",              new Car() AutoMark="Aodi", Name="Aodi",&

80、#160;TopSpeed="500", Year="2020"                            foreach (Car item in infos)    

81、0;                           CarListViewItem viewItem = new CarListViewItem();           &

82、#160;      viewItem.Car = item;                  this.listBoxCars.Items.Add(viewItem);              

83、                        public class Car                public string AutoMark&#

84、160; get; set;           public string Name  get; set;           public string Year  get; set;      

85、     public string TopSpeed  get; set;           using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.

86、Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;namespace WpfApplication1 / <summary> / Window35.xaml 的交互逻辑 / </summary> public partial class Window35 : Window public Window35

87、() InitializeComponent(); InitialCarList(); private void listBoxCars_SelectionChanged(object sender, SelectionChangedEventArgs e) CarListViewItem viewItem = e.AddedItems0 as CarListViewItem; if(viewItem!=null) carDetailView1.Car = viewItem.Car; private void InitialCarList() List<Car> infos = n

88、ew List<Car>() new Car() AutoMark="Aodi", Name="Aodi", TopSpeed="200", Year="1990", new Car() AutoMark="Aodi", Name="Aodi", TopSpeed="250", Year="1998", new Car() AutoMark="Aodi", Name="Aodi", T

89、opSpeed="300", Year="2002", new Car() AutoMark="Aodi", Name="Aodi", TopSpeed="350", Year="2011", new Car() AutoMark="Aodi", Name="Aodi", TopSpeed="500", Year="2020" ; foreach (Car item in infos) CarLi

90、stViewItem viewItem = new CarListViewItem(); viewItem.Car = item; this.listBoxCars.Items.Add(viewItem); public class Car public string AutoMark get; set; public string Name get; set; public string Year get; set; public string TopSpeed get; set; 运行并单击Item项,运行效果如下图:很难说这样做是错的,但是WPF里面如此实现需求真的是浪费了数据驱动界面这

91、一重要功能。我们常说把WPF当作Winform来用指的就是这种实现方法。这种做法对WPF最大的曲解就是没有借助Binding来实现数据驱动界面,并且认为ListBoxItem里面放置的控件-这种曲解迫使数据在界面元数据间交换并且程序员只能通过事件驱动方式来实现逻辑-程序员必须借助处理ListBox的SelecttionChanged事件来推动DetaIlView来显示数据,而数据又是由CarListItemView控件转交给CarDetailView的,之间还做了一次类型转换。下图用于说明事件驱动模式与期望中数据驱动界面模式的不同:显然,事件驱动是控件和控件之间沟通或者说是形式和形式之间的沟通

92、,数据驱动则是数据与控件之间的沟通,是内容决定形式。使用DataTemplate就可以方便的把事件驱动模式转换为数据驱动模式。你是不是担心前面写的代码会被删掉呢?不会的!由UserControl升级为DataTemplate时90%的代码是Copy,10%的代码可以方向删除,再做一点点改动就可以了。让我们来试试看。首先把连个UserControl的芯剪切出来,用DataTempldate进行包装,再放到主窗体的资源字典里。最重要的是为DataTemplate里面的每一个控件设置Binding,告诉各个控件应该关注的是数据的哪个属性。因为使用BInding在控件和数据间建立关联,免去了在C#代码中访问界面元素,所以XAML代码中的大部分x:Name

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论