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);

  

 

都是一些基础,谨供学习。

绑定补充:

 

posted @ 2013-11-05 11:26  小项目笔记  阅读(18270)  评论(1编辑  收藏  举报

更多文章请关注公众号:小项目笔记

小项目笔记