WPF基础知识、界面布局及控件Binding
WPF是和WinForm对应的,而其核心是数据驱动事件,在开发中显示的是UI界面和逻辑关系相分离的一种开放语言。UI界面是在XAML语言环境下开发人员可以进行一些自主设计的前台界面,逻辑关系还是基于c#语言进行的逻辑设计。在使用WPF做项目的时候,免不了要对界面进行布局,同时需要对其中的控件进行绑定,本文主要是对这几方面进行介绍。
首先介绍WPF的基础知识:
1 XAML是什么?
XAML(Extensible Application Markup Language)即可扩展应用程序标记语言,是WPF技术中专门用于设计UI的语言。
2、XAML的文档结构?
在程序员眼中,UI界面只是一个平面结构。但是从后台XAML代码中看来,UI界面实际上是一个树形逻辑结构。
3、XAML中属性元素的理解?
属性元素指的是某个标签的一个元素对应的这个标签的一个属性,即以元素的形式来表达一个实例的属性。XAML为对象属性赋值语法有两种:使用字符串进行简单赋值;使用属性元素进行复杂赋值。例如:
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" >
<Rectangle x:Name="rectangle" Width="200" Height="120">
<Rectangle.Fill >
<SolidColorBrush Color="Blue" />
</Rectangle.Fill >
</Rectangle>
</Grid >
4、XAML中的标记扩展?
标记扩展就是一种特殊的Attribute=Value,特殊处在于Value是由一对花括号及其括起来的内容组成。XAML编译器会对这样的内容作出解析、生成相应的对象。
5、WPF中事件处理器?
WPF支持在XAML里为对象的事件指定事件处理器,方法是使用事件处理器的函数名为对应对象事件的Attribute进行赋值。例如:
<Button x:Name="button1" Click=" button1_Click" Margin="5" Height="15" Width="50"/>
6、在自己的程序里引用类库?
步骤需要三步:
1编写类库项目并编译得到.dll文件或者获得别人编译的.dll文件。
2将类库项目或者.dll文件中引用自己的项目
3在c#和XAML中引用类库中的名称空间。
7、XAML注释语法?
<!—注释内容-->
9、 XAML中X名称空间?
X名称空间映射的是xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
<Rectangle x:Name="rectangle" Width="200" Height="120">
<Rectangle.Fill >
<SolidColorBrush Color="Blue" />
</Rectangle.Fill >
</Rectangle>
10、X名称空间大致可分为三类?
Attribute:是语言层面的东西、是给编译器看的。(Property是面向对象层面的东西,是给编程逻辑用的)
标记扩展:实际上就是一些MarkupExtension类的直接或者间接派生类。X名称空间中就包含这样的类。
XAML指令元素:有x:Code,x:XData。
11、WPF中控件?
经常用到的6类:
1 布局控件 可以容纳多个控件或嵌套其他布局控件,用于在UI上组织和排列控件。常用的Grid、StackPanel、DockPanel,共同父类是Panel。
2 内容控件 只能容纳一个其他控件。父类是ContentControl。
3 带标题内容的控件 就是一个内容控件+一个标题,标题部分可容纳一个控件或布局。
4 条目控件 可以显示一列数据,共同父类是ItemsControl。
5 带标题条目控件 就是一个条目控件+一个标题显示区。基类是HeaderedItemsControl。
6 特殊内容控件
12、WPF精髓?
WPF是数据驱动UI,数据是核心、主动的,UI属于数据并表达数据、被动的。
13、Binding是什么?
在WPF中,Binding是架在目标(UI层控件对象)和源(逻辑层对象)之间的一座桥梁。
14、Binding对源的要求是:只要它是一个对象,并且通过属性公开自己的数据,它就能作为Binding的源。
15、Binding的方向即Binding模式有哪些?
Binding模式有OneWay、TwoWay、OnTime、OneWayToSource、Default。
OneWay:每当源发生变化时,数据就会从源流向目标。
TwoWay:绑定会将源数据发送到目标,但如果目标属性发生变化,则会将变化发回给源。
OnTime:绑定会将源数据发送到目标,但是仅当启动了应用程序或DataContext发生更改时才会有这个操作,所以它不会侦听源中的更改通知。
OneWayToSource:绑定会将源数据发送到目标。
Default:根据实际情况来定,可编辑的就是TwoWay,只读的就是OneWay。在连接没有设置模式时就是Ddfault。
16、Binding的路径(Path)和没有Path的情况?
Binding的路径设置有很多种,其中最简捷的就是直接把Binding关联在Binding源上。
<TextBox x:Name=”textBox1” Text={Binding Path=Value, ElementName=slider1}/>
没有Path的情况就是Binding源本身就是数据且不需要Path来指明。
17、为Binding指定Source的几种方法?没有Source的情况?
(几种方法,在这里不一一罗列,但是没有实际用过。)
没有Source的情况就是使用DataContext作为Binding的源。
18、Binding的数据校验?Binding的数据转换?
Binding数据有效性校验是它的ValidationRules属性,Binding数据转换是它的Converter属性。
19、模板 Template?
ControlTemplate是算法内容的表现形式;DataTemplate是数据内容的表现形式
20、DataTemplate常用到的3个地方:
ContentControl的ContentTemplate属性,ItemsControl的ItemTemplate属性,GrdiViewColumn的CellTemplate属性。
WPF界面布局
在WPF做项目过程中,需要对界面元素进行合理布局,以便系统更人性化。WPF提供了一套强有力的工具:面板panel。下面将逐一介绍这些面板的使用方法。
Grid
网格面板,以表格形式布局元素,对于整个面板上的元素进行布局,有效地解决多行之间、多列之间位置的一致性。Grid很像网页中的Table,定义一个网格,需要定义行、列,划分单元格,坐标从(0,0)开始。列宽和行高,分别可以在ColumnDefinition、RowDefinition里面指定Width、Height的值。首先定义网格,然后定义元素,并指定元素所在的单元格。如果不定义单元格,默认将元素放到第一个单元格(0,0)。自动长度——自动匹配列中最长元素的宽度;比例长度——*表示占用剩余的全部宽度;两行都是*,将平分剩余宽度;一个2*,一个*,表示前者2/3宽度。使用Grid.ColumnSpan和Grid.RowSpan附加属性可以让相互间隔的行列合并,使用GridSplit控件结合Grid控件实现类似于WinForm中SplitContainer的功能。
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="44*" /> <ColumnDefinition Width="141*" /> <ColumnDefinition Width="218*" /> <ColumnDefinition Width="122*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="62*" /> <RowDefinition Height="70*" /> <RowDefinition Height="102*" /> <RowDefinition Height="116*" /> </Grid.RowDefinitions> <Button Content="Button" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="16,18,0,0" Name="button1" VerticalAlignment="Top" Width="75" Grid.Column="1" /> <Button Content="Button" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="107,38,0,0" Name="button2" VerticalAlignment="Top" Width="75" /> <Button Content="Button" Grid.Row="3" Height="23" HorizontalAlignment="Left" Margin="16,38,0,0" Name="button3" VerticalAlignment="Top" Width="396" Grid.ColumnSpan="3" Grid.Column="1" /> </Grid>
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="102" /> <ColumnDefinition Width="161" /> <ColumnDefinition Width="15" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="66" /> <RowDefinition Height="96" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> </Grid>
Canvas
画布,用于完全控制每个元素的精确位置。它是布局控件中最为简单的一种,直接将元素放到指定位置,主要来布置图画。使用Canvas时,必须指定一个字元素的位置(相对于画布),否则所有元素都将出现在画布的左上角。调整位置用Left、Right、Top和Bottom四个附加属性。如果Canvas是窗口主元素,用户改变窗口大小时,Canvas也会随之变化,字元素的位置也会随之移动,以保证相对于Canvas的位置属性不变。Canvas允许子元素的部分或全部超过其边界,默认不会剪裁子元素,同时可以使用负坐标,因此画布不需要指定大小。如果想复制画布内容,将ClipToBounds设为true即可。
<Grid> <Canvas Height="280" HorizontalAlignment="Left" Margin="22,32,0,0" Name="canvas1" VerticalAlignment="Top" Width="482" Background="Gray"> <Canvas Canvas.Left="46" Canvas.Top="58" Height="100" Name="canvas2" Width="200" Background="Red"> <Ellipse Canvas.Left="82" Canvas.Top="20" Height="100" Name="ellipse1" Stroke="Black" Width="200" Fill="White" /> </Canvas> <Canvas Canvas.Left="295" Canvas.Top="184" Height="64" Name="canvas3" Width="143" Background="Green" ClipToBounds="True" > <Ellipse Canvas.Left="24" Canvas.Top="7" Height="100" Name="ellipse2" Stroke="Black" Width="200" Fill="White" /> </Canvas > </Canvas> </Grid>
StackPanel
栈面板,可以将元素排列成一行或者一列。其特点是:每个元素各占一行或者一列。Orientation属性指定排列方式:Vertical(垂直)【默认】、Horizontal(水平)。默认情况下,水平排列时,每个元素都与面板一样高;垂直排列时,每个元素都与面板一样宽。如果元素超过了StackPanel的空间,会截断多出的内容。 元素的Margin属性用于使元素之间产生一定得间隔,当元素空间大于其内容的空间时,剩余空间将由HorizontalAlignment和VerticalAlignment属性来决定如何分配。
<Grid> <StackPanel Height="144" HorizontalAlignment="Left" Margin="33,21,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="346"> <Button Content="Button" Height="23" Name="button1" Width="75" /> <Button Content="Button" Height="23" Name="button2" Width="235" /> <Button Content="Button" Height="23" Name="button3" Width="658" /> </StackPanel> <StackPanel Height="155" HorizontalAlignment="Left" Margin="45,179,0,0" Name="stackPanel2" VerticalAlignment="Top" Width="401" Orientation="Horizontal" > <Button Content="Button" Height="23" Name="button4" Width="75" /> <Button Content="Button" Height="23" Name="button5" Width="75" /> </StackPanel> </Grid>
WrapPanel
从左至右或从上至下按顺序位置定位子元素,如果排满则自动换行至下一行或列继续排列。WrapPanel面板也提供了 Orientation属性设置排列方式,属性设置横排(Horizontal默认)和竖排(Vertical)。
<Grid> <WrapPanel Height="129" HorizontalAlignment="Left" Margin="50,36,0,0" Name="wrapPanel1" VerticalAlignment="Top" Width="260"> <Button Content="Button" Height="23" Name="button1" Width="75" /> <Button Content="Button" Height="23" Name="button2" Width="75" /> <Button Content="Button" Height="23" Name="button3" Width="75" /> <Button Content="Button" Height="23" Name="button4" Width="75" /> </WrapPanel> <WrapPanel Height="105" HorizontalAlignment="Left" Margin="99,195,0,0" Name="wrapPanel2" VerticalAlignment="Top" Width="211" Orientation="Vertical" > <Button Content="Button" Height="23" Name="button5" Width="75" /> <Button Content="Button" Height="23" Name="button6" Width="75" /> </WrapPanel> </Grid>
DockPanel
停靠面板,可以将面板的某一边指定给每个元素,当面板大小变化时,按钮将根据指定的边进行停靠。在DockPanel中,指定停靠边的控件,会根据定义的顺序占领边角,所有控件绝不会交叠。默认情况下,后添加的元素只能使用剩余空间,最后一个元素填充所有剩余空间。如果不希望最后一个元素填充剩余区域,可以将DockPanel属性LastChildFill设置为False。
DockPanel面板可以将子元素停靠在面板的上下左右。DockPanel会对每个子元素进行排序,并停靠在面板的一侧,多个停靠在同侧的元素则按顺序排序,最后一个元素填充这个Panel(这个需要设置LastChildFill属性为 True)。对于在DockPanel中的元素的停靠属性可以通过Panel.Dock的附加属性来设置。
<Grid> <DockPanel Height="100" HorizontalAlignment="Left" Margin="43,44,0,0" Name="dockPanel1" VerticalAlignment="Top" Width="281" LastChildFill="False" > <Button Content="Button" Height="23" Name="button1" Width="75" /> <Button Content="Button" Height="23" Name="button2" Width="75" /> <Button Content="Button" Height="23" Name="button3" Width="75" /> </DockPanel> <DockPanel Height="141" HorizontalAlignment="Left" Margin="140,183,0,0" Name="dockPanel2" VerticalAlignment="Top" Width="299"> <Button Content="Button" Height="23" Name="button4" Width="75" /> <Button Content="Button" Height="23" Name="button5" Width="75" /> <Button Content="Button" Height="23" Name="button6" Width="75" /> </DockPanel> </Grid>
视图框(Viewbox)
视图框可以自动缩放其内容,以填充可用的空间。它只能有一个子元素。比如,Viewbox中放置一个Canvas,默认将按比例缩放Canvas,填充区域,而此时Canvas指定的长宽已不起作用,仅保留比例。如果想禁用Viewbox的自动缩放功能,将其拉伸属性Stretch设置为None即可;如果想缩放并且不保留子元素比例,将Viewbox的Stretch属性(默认为Uniform)改为Fill(完全填充);如果想保留比例并完全填充空白区域,Stretch设置为UniformToFill。
上图分别是stretch:Stretch="Fill", Stretch="None",Stretch="Uniform",Stretch="UniformToFill"
<Grid>
<Canvas Height="261" HorizontalAlignment="Left" Margin="34,22,0,0" Name="canvas1" VerticalAlignment="Top" Width="416">
<Viewbox Canvas.Left="29" Canvas.Top="18" Height="218" Name="viewbox1" Width="348" Stretch="UniformToFill" >
<Button Content="Button" Height="23" Name="button1" Width="75" />
</Viewbox>
</Canvas>
</Grid>
Border
Border 是一个装饰的控件,此控件绘制边框及背景,在 Border 中只能有一个子控件(这个子控件又可以包含多个子控件)。
<Grid> <Border BorderThickness="20" BorderBrush="Green" Height="230" HorizontalAlignment="Left" Background="Red" Margin="35,40,0,0" Name="border1" VerticalAlignment="Top" Width="447" /> </Grid>
滚动视图控件(ScrollViewer)
滚动视图控件可以将过多的内容放入一个可滚动的区域来显示。比如一个很大的椭圆,通过滚动就可以显示全部内容。但是ScollViewer只能放一个元素,这个元素是任意的。倘若想布局多个元素,可以将多个元素放到一个面板中,再嵌入到ScollViewer中。
滚动条的可见性,默认垂直滚动条是可见的(Visiable),而水平滚动条是不可用的(Disable),此处改为Auto(需要时显示)或者Visiable(可见,不论需不需要都显示)。
UniformGrid
均布网格, 顾名思义,所有单元格均匀排布,大小都相同。你可以为其指定行数Rows和列数Columns,UniformGrid将根据行列平分画布,每个控件一个单元格。如果不指定其行数和列数,UniformGrid会根据子元素个数和大小,默认创建相同的行数和列数,布局所有子元素。由于每个单元格只包含一个子元素,不需要额外指定哪个元素属于哪个单元格,所以直接添加子元素就可以。
公共布局属性
Width、Height表示元素宽度和高度,设置该属性可以是元素具有精确的宽高。MinWidth、MaxWidth、MinHeight、MaxHeight 可以指定元素大小的界限,分别表示元素的最小宽度、最大宽度、最小高度和最大高度。当你指定一个元素的宽度和高度时,WPF会尽可能遵循你的设置。比如元素宽度>屏幕宽度,元素将被剪裁以适应可用空间。
Margin(外边距),指的是元素周围的距离,决定了元素周围留下的空白大小;Padding(内边距),指的是元素边界与其内容之间的距离。做过网页设计、用过CSS的同学对margin和padding属性肯定不陌生,但也有所区别。WPF中的Margin值可以为一个数字、一对数字和四个数字。
一个数字代表四周距离相同,为指定值。一对数字时,第一个数字表示左侧和右侧距离相同,为指定值;第二个数字表示顶部和底部距离相同,为指定值。(与CSS中顺序不同)。四个数字,分别表示左侧、顶部、右侧、底部距离,该顺序与CSS不同。
CSS中margin和padding属性顺序是:两个数字对应左右、上下;四个数字对应上、右、下、左;
HorizontalAlignment与VerticalAlignment
在父元素中,当剩余空间很大时(超过子元素需要),这两个属性可以控制字元素的位置。比如,在垂直排列的StackPanel中,面板宽度默认和最宽的元素宽度相同,其他控件的宽度默认将会被拉伸。这时,可以使用HorizontalAlignment属性来控制,默认值为Stretch(拉伸),还有Left、Center、Right。VerticalAlignment则有Stretch、Top、Center和Bottom四个枚举值。
Visibility可见度,决定元素是否可见。枚举值有两个:Collapsed和Hidden。Collapsed元素不可见,并且首选尺寸变为0,不再影响布局。Hidden元素虽然不可见,但尺寸不变,布局系统仍按可见的处理。
FlowDirection文本方向,默认情况下基于系统的本地设置。比如英语、中文都是从左往右排列,LeftToRight;希伯来文从右往左排列,RightToLeft。如果为面板指定该属性,则面板的所有子元素都按此方向排列。
Panel.ZIndex,Panel定义的一个附加属性ZIndex,用于多个元素重叠时,指定显示的上下层关系。ZIndex值大的将出现在值小的元素上方。元素显示顺序将不受文档定义顺序控制。如果不使用ZIndex,重叠元素将根据文档定义的顺序显示,后定义的元素出现在上方。
Binding基础
首先,创建一个类Student,继承INotifyPropertyChanged。定义几个字段以及一个组件属性更改引发的方法。贴代码:
public class Student:INotifyPropertyChanged { private string name; public string Name { get { return name; } set { this.name = value; NotifyPropertyChanged("Name"); } } private int id; public int ID { get { return id; } set { this.id = value; NotifyPropertyChanged("ID"); } } public int age; public int Age { get { return age; } set { this.age = value; NotifyPropertyChanged("Age"); } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
1、在前台wpf界面定义一个button控件(btnBinding_Click),用以改变Name字段的值,将控件textbox(tbDisplay)绑定到Name字段。基本步骤:
(1)所有代码都写在后台逻辑:
准备数据源:Student student = new Student();
准备Binding:
Binding binding = new Binding();
binding.Source = student;
binding.Path = new PropertyPath("Name");
使用Binding连接到数据源与Binding目标:BindingOperations.SetBinding(this.tbDisplay, TextBox.TextProperty, binding);
将上述三步操作合一:this.tbDisplay.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = student = new Student() });
(2)不过大家一般的做法是在前台界面进行绑定,同时在后台逻辑上指定绑定源,如下:
前台绑定:<TextBox Height="23" HorizontalAlignment="Left" Margin="27,12,0,0" Name="tbDisplay" Text="{Binding Path=Name}" VerticalAlignment="Top" Width="120" />
后台逻辑:
Binding bind = new Binding("Name") { Source = student };
this.tbDisplay.SetBinding(TextBox.TextProperty, bind);
(3)现在将其放在一个容器内进行绑定:
前台绑定:<StackPanel Height="226" HorizontalAlignment="Left" Margin="790,36,0,0" Name="SP" VerticalAlignment="Top" Width="239" >
<TextBox x:Name="tbDisplay" Width="171" Height="47" Margin="5,100,10,100" Text="{Binding Path=Name}" />
</StackPanel >
后台逻辑:Student student = new Student();
this.SP.DataContext = student;
2、将控件作为binding源于binding标记扩展
所有操作都放在在前台界面中:
<TextBox Height="23" HorizontalAlignment="Left" Margin="27,170,0,0" Name="tbNum" Text="{Binding Path=Value,ElementName=slider1}" VerticalAlignment="Top" Width="120" />
<Slider Height="23" HorizontalAlignment="Left" Margin="27,210,0,0" Name="slider1" VerticalAlignment="Top" Maximum="100" Minimum="0" Width="187" />
3、使用集合对象作为列表控件的ItemsSource
下面这个例子是要把List<Student>集合的实例作为ListBox的ItemsSource,同时让ListBox显示Student的Name并使用TextBox显示当前选中的条目的ID。
前台界面:
<TextBox Height="23" HorizontalAlignment="Right" Margin="0,52,571,0" Name="tbID" VerticalAlignment="Top" Width="120" />
<ListBox Height="100" HorizontalAlignment="Left" Margin="414,162,0,0" Name="lbStudent" VerticalAlignment="Top" Width="120" />
后台的逻辑:
List<Student> stuList = new List<Student>() { new Student (){ID=0,Name="Tim",Age=23}, new Student (){ID=1,Name="Tom",Age=24}, new Student (){ID=2,Name="Mike",Age=25}, }; //为listbox设置Binding this.lbStudent.ItemsSource = stuList; this.lbStudent.DisplayMemberPath = "Name"; //为textbox设置Binding Binding binding = new Binding("SelectedItem.ID") { Source = this.lbStudent }; this.tbID.SetBinding(TextBox.TextProperty, binding);
都是一些基础,谨供学习。
绑定补充: