wpf学习笔记
XAML
是微软公司为构建应用程序用户界面而创建的一种新的“可扩展应用程序标记语言”,提供了一种便于扩展和定位的语法来定义和程序逻辑分离的用户界面。
XAML是一种基于XML的,格式组织良好的标记语言(比HTML要严格和准确)。其支持快速高效实现应用程序用户界面。
特点:
定义应用程序的界面元素
显示的声明WPF资源(样式、模板、动画等)
可扩展性(自定义UI控件)
集中关注于界面的设计和实现
XAML命名空间的语法:
xmlns[:可选映射前缀]="命名空间描述"
注意:没有加可选映射前缀的xmlns是WPF默认的命名空间,一个xaml文件只能有一个默认的命名空间
一个完整的xaml文件,必须具备两个命名空间。
<Window x:Class="HelloWpf.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300">
<Grid>
<Button Margin="101,114,102,125" Name="btn1" Click="btn1_Click">请单击按钮</Button>
</Grid>
</Window>
以上代码呈现为一个包括按钮控件的窗口。代码中包括三个元素:
(1)处于顶层的Window元素,它呈现为整个窗口;
(2)Grid元素用于实现布局,其中可放置任何控件;
(3)Button元素表示按钮控件。
虽然这些代码很简单,但是包含了XAML的多个核心概念,例如基本语法规则、Window及其他根元素、命名空间和Code-Behind类,属性和事件等。
属性设置
<Grid Name="hh"/> 等同于 <Grid><Grid.Name>hh</Grid.Name></Grid>
Window及其他根元素
开发人员常用的根元素包括Window、Page、StackPanel、Canvas、Grid等。下面简单介绍一下这些根元素:
<Window>
元素
该元素对应的是System.Windows.Window类,其呈现为与用户交互的,最常用的标准窗口。使用该元素呈现的窗口显示了客户区域、最大化最小化关闭按钮、图标、系统菜单、边框等。
<Page>
元素
该元素对应的是System.Windows.Controls.Page类,其封装了一个可实现导航的内容页,该内容页可宿主在Window、 NavigationWindow、Frame、UserControl等对象中。该元素对应的基类是 System.Windows.Controls.Panel。
<StackPanel>
元素
该元素对应的是System.Windows.Controls.StackPanel类,其能够将子元素置于可垂直或者水平排列的单行中。与< StackPanel>类似的还有<DockPanel>、<TabPanel>、<WrapPanel>
等 元素,它们的基类都是System.Windows.Controls.Panel。
<Canvas>
元素
该元素对应的是System.Windows.Controls.Canvas类,其定义了一个可使用相对坐标(相对于Canvas定义的区域)显式定位 子元素的区域,例如使用属性Top、Left、Bottom、Right。该元素对应的基类是 System.Windows.Controls.Panel。
<Grid>
元素
该元素对应的是System.Windows.Controls.Grid类,其允许开发人员自定义一个包括行和列的表格区域。接着,可在单元格中放入其他子元素。该元素对应的基类是System.Windows.Controls.Panel。
通常情况下,在使用根元素时,必须引用适当的命名空间。在下面一节中将介绍有关命名空间的知识。
布局
应用程序界面设计中,合理的元素布局至关重要,它可以方便用户使用,并将信息清晰合理地展现给用户。在WPF中非常抵制基于坐标的布局,而是注重创建更灵活的布局,使布局能够适应内容的变化、不同的语言以及各种窗口尺寸。WPF提供了一套功能强大的工具-面板(Panel),来控制用户界面的布局。你可以使用这些面板控件来排布元素。如果内置布局控件不能满足需要的话,还可以创建自定义的布局元素。
遵循以下几条重要原则:
不应显式设定元素(如控件)的尺寸。元素应当可以改变尺寸以适合它们的内容。例如当添加更多的文本时按钮应当能够扩展。可通过设置最大和最小尺寸来限制可以接受的控件尺寸范围。
不应使用屏幕坐标指定元素的位置。元素应当由它们的容器根据它们的尺寸、顺序以及(可选的)其他特定于具体布局容器的信息进行排列。如果需要在元素之间添加空白空间,可使用 Margin属性。
布局容器的子元素“共享”可用的空间。如果空间允许,布局容器会根据每个元素的内容尽可能为元素设置更合理的尺寸。它们还会向一个或多个子元素分配多余的空间。
可嵌套的布局容器。典型的用户界面使用Grid面板作为开始,Grid面板是WPF中功能最强大的容器,Grd面板可包含其他布局容器,包含的这些容器以更小的分组排列元素,比如带有标题的文本框、列表框中的项、工具栏上的图标以及一列按钮等。
如果创建WPF应用程序时遵循了这些原则,将会创建出更好的、更灵活的用户界面。如果不遵循这些原则,最终将得到不是很适合WPF的并且难以维护的用户界面。
常用布局属性
属性名称 | 说明 |
---|---|
HorizontalAlignment(水平对齐) | 布局中子元素定位,可选值:Center、Left、Right、Stretch(延伸) |
VerticalAlignment(垂直对齐) | 布局中子元素定位,可选值:Center、Top、Bottom、Stretch(延伸) |
Margin(边距) | 元素周围边距:左、上、右、下 |
Min Width 和 Min Height | 元素最小尺寸 |
Max Width 和 Max Height | 元素最大尺寸 |
Width 和 Height | 显示设置元素尺寸 |
面板(Panel)
WPF用于布局的面板主要有6个,StackPanel(栈面板)、WrapPanel(环绕面板)。DockPanel(停靠面板)、Canvas(画布)、Grid(网格面板)和UniformGrid(均布网格)。
名称 | 说明 |
---|---|
StackPanel | 在水平或垂直的堆栈中放置元素。这个布局容器通常用于更大、更复杂窗口中的一些小区域 |
WrapPancl | 在一系列可换行的行中放置元素。在水平方向上, Wrappanel 面板从左向右放置条目,然后在随后的行中放置元素。在垂直方向上, Wrappanel I面板在自上而下的列中放置元素,并使用附加的列放置剩余的条目 |
DockPanel | 根据容器的整个边界调整元素 |
Grid | 根据不可见的表格在行和列中排列元素,这是最灵活、最常用的容器之一 |
UniformGrid | 在不可见但是强制所有单元格具有相同尺寸的表中放置元素,这个布局容器不常用 |
Canvas | 使用固定坐标绝对定位元素。这个布局容器与传统 Windows窗体应用程序最相似,但没有提供锚定或停靠功能。因此,对于尺寸可变的窗口,该布局容器不是合适的选择。如果选择的话,需要另外做一些工作 |
StackPanel:栈面板
栈面板,可以将元素排列成一行或者一列,其特点是:每个元素各占一行或者一列
属性:
Orientation(定向)属性指定排列方式:Vertical(垂直)【默认】、Horizontal(水平)
默认情况下,水平排列时,每个元素都与面板一样高;垂直排列时,每个元素都与面板一样宽。如果包含的元素超过了面板空间,它只会截断多出的内容。
Margin(边距)属性用于使元素之间产生一定得间隔,当元素空间大于其内容的空间时,剩余空间将由HorizontalAlignment和 VerticalAlignment属性来决定如何分配。
HorizontalAlignment/VerticalAlignment,指自身相对于父元素的位置。
<StackPanel x:Name="stackpanel" Orientation="Vertical">
<Label>stackpanel:Vertical(垂直)【默认】每个元素都与面板一样宽</Label>
<Button Content="第一个"></Button>
<Button Content="第二个"></Button>
<Button Content="第三个"></Button>
<Button Content="第四个" ></Button>
</StackPanel>
WrapPanel:环绕面板
WrapPanel布局面板将各个控件从左至右按照行或列的顺序罗列,当长度或高度不够时就会自动调整进行换行,后续排序按照从上至下或从左至右的顺序进行。
Orientation——根据内容自动换行。当Orientation属性的值设置为 Horizontal:元素是从左向右排列的,然后自上至下自动换行。当Orientation属性的值设置为Vertical:元素是从上向下排列的,然后从左至右自动换行。
ItemHeight——所有子元素都一致的高度。每个子元素填充高度的方式取决于它的VerticalAlignment属性、Height属性等。任何比ItemHeight高的元素都将被截断。
ItemWidth——所有子元素都一致的宽度。每个子元素填充高度的方式取决于它的VerticalAlignment属性、Width属性等。任何比ItemWidth高的元素都将被截断。
示例效果图如下所示,图1是窗体宽度较小时候的效果,图2是窗体宽度拉大以后的效果
<WrapPanel Orientation="Horizontal" Background="#FF82CFEC" Grid.Row="1" Grid.Column="1">
<Button Width="100">按钮1</Button>
<Button Width="100">按钮2</Button>
<Button Width="100">按钮3</Button>
<Button Width="100">按钮4</Button>
<Button Width="100">按钮5</Button>
<Button Width="100">按钮6</Button>
</WrapPanel>
DockPanel:停靠面板
DockPanel会对每个子元素进行排序,并将根据指定的边进行停靠,多个停靠在同侧的元素则按顺序排序。在DockPanel中,指定停靠边的控件,会根据定义的顺序占领边角,所有控件绝不会交叠。
默认情况下,后添加的元素只能使用剩余空间,无论对DockPanel的最后一个子元素设置任何停靠值,该子元素都将始终填满剩余的空间。如果不希望最后一个元素填充剩余区域,可以将DockPanel属性LastChildFill设置为false,还必须为最后一个子元素显式指定停靠方向。
1、填充整个剩余空间
<DockPanel>
<Button DockPanel.Dock="Left" Content="ButtonLeft"></Button>
<Button DockPanel.Dock="Top" Content="ButtonTop"></Button>
<Button DockPanel.Dock="Right" Content="ButtonRight"></Button>
<Button DockPanel.Dock="Bottom" Content="ButtonBottom"></Button>
<Button Content="ButtonTop"></Button>
</DockPanel>
2、最后元素不填充剩余空间
<DockPanel LastChildFill="False">
<Button DockPanel.Dock="Left" Content="ButtonLeft"></Button>
<Button DockPanel.Dock="Top" Content="ButtonTop"></Button>
<Button DockPanel.Dock="Right" Content="ButtonRight"></Button>
<Button DockPanel.Dock="Bottom" Content="ButtonBottom"></Button>
<Button DockPanel.Dock="Top" Content="最后一个Button不填充剩余空间"></Button>
</DockPanel>
Canvas:画布面板
画布,用于完全控制每个元素的精确位置。他是布局控件中最为简单的一种,直接将元素放到指定位置,主要来布置图面。使用Canvas,必须指定一个子元素的位置(相对于画布),否则所有元素都将出现在画布的左上角。调整位置用Left、Right、Top和Bottom四个附加属性。如果Canvas是窗口主元素(即最外层的布局面板是Canvas),用户改变窗口大小时,Canvas也会随之变化,子元素的位置也会随之移动,以保证相对于Canvas的位置属性不变。
Canvas允许子元素的部分或全部超过其边界,默认不会裁剪子元素,同时可以使用负坐标,即溢出的内容会显示在Canvas外面,这是因为默认 ClipToBounds=”False”,因此画布不需要指定大小。
Z顺序
如果 Canvas面板中有多个互相重叠的元素,可通过设置 Canvas. Zindex附加属性来控制它们的层叠方式。
添加的所有元素通常都具有相同的 Zindex值-0。如果元素具有相同的Index值,后放入的显示在上面。
<Canvas Margin="586,155,14,144" >
<Button Width="200" Height="20" Content="按钮1" Canvas.ZIndex="1"></Button>
<Button Width="200" Height="20" Content="按钮3"></Button>
<Button Width="200" Height="20" Content="按钮4"></Button>
<Button Width="200" Height="20" Content="按钮5"></Button>
<Button Height="20" Canvas.Right="10" Canvas.Bottom="10" Content="按钮2"></Button>
</Canvas>
InkCanvas
Grid:网格面板
Grid顾名思义就是“网格”,以表格形式布局元素,对于整个面板上的元素进行布局,它的子控件被放在一个一个事先定义好的小格子里面,整齐配列。 Grid和其他各个Panel比较起来,功能最多也最为复杂。
要使用Grid,首先要向RowDefinitions(行)和ColumnDefinitions(列)属性中添加一定数量的RowDefinition和 ColumnDefinition元素,从而定义行数和列数。而放置在Grid面板中的控件元素都必须显示采用Row和Column附加属性定义其放置所在的行和列,这两个属性的值都是从0开始的索引数,如果没有显式设置任何行或列,Grid将会隐式地将控件加入在第0行第0列。
注意:尽管Grid面板被设计成不可见的,但可将Grid.ShowGridLines属性设置为True,从而更清晰的观察Grid面板,方便调试,可以更准确地控制Grid面板如何选择列宽和行高。
<Grid ShowGridLines="True" Margin="34,340,592,21" Background="#FFD5ECB5">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button>不指定</Button>
<Button Grid.Row="0" Grid.Column="1">0,1</Button>
<Button Grid.Row="1" Grid.Column="2">1,2</Button>
</Grid>
调整行高和列宽
如果Grid面板只是按比例分配尺寸的行和列的集合,它也就没有什么用处了。为了充分发挥Grid面板的潜能,可更改每一行和每一列的尺寸设置方式。
Grid面板支持以下三种设置尺寸的方式:
名称 | 说明 |
---|---|
绝对设置尺寸方式 | 使用设备无关单位准确地设置尺寸,就是给一个实际的数字,但通常将此值指定为整数。这是最无用的策略,因为这种策略不够灵活,难以适应内容大小和容器大小的改变,而且难以处理本地化。 |
自动设置尺寸方式 | 值为Auto,实际作用就是取实际控件所需的最小值,每行和每列的尺寸刚好满足需要,这是最有用的尺寸设置方式。 |
按比例设置尺寸方式 | 按比例将空间分割到一组行和列中。这是对所有行和列的标准设置。通常值为* 或N* ,实际作用就是取尽可能大的值,当某一列或行被定义为* 则是尽可能大,当出现多列或行被定义为* 则是代表几者之间按比例方设置尺寸。 |
为了获得最大的灵活性,可混合使用这三种尺寸设置方式。例如,创建几个自动设置尺寸的行,然后通过按比例设置尺寸的方式让最后的一行或两行充满剩余的空间,这通常是很有用的。
可通过设置ColumnDefinition对象的Width属性或者RowDefinition对象的Height属性来确定尺寸设置方式。
多行和多列 : 使元素跨越多个单元格,这两个属性是RowSpan和ColumnSpan。
<!--设置尺寸,占多格-->
<Grid ShowGridLines="True" Background="#FFB5ECEA" >
<Grid.RowDefinitions>
<!--自动行高-->
<RowDefinition Height="Auto"></RowDefinition>
<!--固定行高-->
<RowDefinition Height="10"></RowDefinition>
<RowDefinition Height="10"></RowDefinition>
<!--按比例行高-->
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="2*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.ColumnSpan="2">占2格行</Button>
<Button Grid.Row="1" Grid.Column="2" Grid.RowSpan="2">1,2</Button>
</Grid>
分割窗口
每个Windows用户都见过分隔条—能将窗口的一部分与另一部分分类的可拖动分割器。例如,当使用Windows资源管理器时,会看到一系列文件夹(在左边)和一系列文件(在右边)。可拖动它们之间的分隔条来确定每部分占据窗口的比例。
在WPF中,分隔条由GridSplitter类表示,它是Grid面板的功能之一。通过为Grid面板添加GridSplitter对象,用户就可以改变行和列的尺寸。例如:
<!--设置分割线,分割窗口-->
<Grid ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0">按钮</Button>
<TextBox Grid.Row="0" Grid.Column="2">文本框</TextBox>
<GridSplitter Grid.Row="0" Grid.Column="1" Width="3" VerticalAlignment="Stretch"
HorizontalAlignment="Center" ShowsPreview="False" />
</Grid>
注意:为了成功地创建GridSplitter对象,务必为VerticalAlignment(垂直对齐方式)、HorizontalAlignment(水平对齐方式)以及width属性(或Height属性)提供相应的属性值。
理解如何使用GridSplitter类,从而得到所期望的效果需要一定的经验,下面列出几条指导原则:
(1)GridSplitter对象必须放在Grid单元格中,可与以及存在的内容一并放到单元格中,这时需要调整边距设置,使他们不相互重叠。更好的方法是预留一列或一行专门用于放置GridSplitter对象,并将预留行或列的Height或Width属性的值设置为Auto。
(2)GridSplitter对象总是改变整行或整列的尺寸(而非改变单个单元格的尺寸)。为使GridSplitter对象的外观和行为保持一致,需要拉伸GridSplitter对象使其穿越整行或整列,而不是将其限制在单元格中。为此,可使用RowSpan或ColumnSpan属性。例如,上面的例子中GridSplitter对象的RowSpan属性设置为2,因此被拉伸充满整列。如果不使用该设置,GridSplitter对象会显示在顶行(放置它的行)中,即使这样,拖动分隔条时也会改变整列的尺寸。
(3)GridSplitter对象很小不易看见,为了使其更可用,需要为其设置最小尺寸。在上面的例子中,对于垂直分隔条,需要将VerticalAlignment属性设置为Stretch(使分隔条填满区域的整个高度),并将Width设置为固定值。对于水平分隔条,需要设置HorizontalAlignment属性来拉伸,并将Height属性设置为固定值。
(4)GridSplitter对齐方式还决定了分隔条是水平的(用于改变行的尺寸)还是竖直的(用于改变列的尺寸)。对于水平分隔条,需要将VerticalAlignment属性设置为Center(这也是默认值),以指明拖动分隔条改变上面行和下面行的尺寸。对于垂直分隔条,需要将HorizontalAlignment属性设置为Center,以改变分隔条两侧列的尺寸。
(5) 在上面的例子中还包含了一处额外的细节。在声明GridSplitter对象时,将ShowPreview属性设置为false,因此,当把分隔条从一边拖到另一边时,会立即改变列的尺寸。但是如果将ShowPreview属性设置为true,当拖到分隔条时就会看到一个灰色的阴影跟随鼠标指针,用于显示将在何处进行分割。并且直到释放了鼠标键之后列的尺寸才改变。
UniformGrid面板
通过简单地设置Rows和Columns属性来设置其尺寸。每个单元格始终具有相同的大小,Uniformgrid面板很少使用,许多WPF开发人员可能永远不会使用 Uniformgrid面板。
<UniformGrid Rows="2" Columns=" 2" >
<Button>Top Left</Button>
<Button>Top Right</Button>
<Button>Bottom Left</Button>
<Button>Bottom Right</Button>
</UniformGrid>
Border控件
Border控件不是布局面板,而是非常便于使用的元素,经常与布局面板一起使用。Border类非常简单。它只能包含一段嵌套内容(通常是布局面板),并为其添加背景或在其周围添加边框。
名称 | 说明 |
---|---|
Background(背景) | 使用 Brush对象设置边框中所有内容后面的背景。可使用固定颜色背景,也可使用其他更特殊的背景 |
Borderbrush和BroderThickness(边框) | 使用 Brush对象设置位于 Border对象边缘的边框的颜色,并设置边框的宽度。为显示边框,必须设置这两个属性 |
CornerRadius(圆角) | 该属性可使边框具有雅致的圆角。 CornerRadius的值越大,圆角效果就越明显 |
Padding(内边距) | 该属性在边框和内部的内容之间添加空间(与此相对, Margin属性在边框之外添加空间) |
属性
属性使用
1、简单使用
<StackPanel Background="#FFC1EDB3"></StackPanel>
2、复杂使用
<StackPanel >
<StackPanel.Background>#FFC1EDB3</StackPanel.Background>
</StackPanel>
依赖属性
依赖属性就是一种自己可以没有值,并且可以通过绑定从其他数据源获取值。依赖属性可支持WPF中的样式设置、数据绑定、继承、动画及默认值。
1、属性变更通知
示例:当鼠标移动到Button按钮上面时,文字的前景色变为红色,离开时变为默认颜色黑色
<Button Height="30" Width="200">鼠标移动到上面,前景色变为红色
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="Red"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
2、属性值继承
是指属性值自顶向下沿着元素树进行传递
<StackPanel >
<TextBlock>我使用的是继承的fontsize</TextBlock>
<TextBlock FontSize="15">我使用的是自己的fontsize</TextBlock>
</StackPanel>
3、节省内存空间
依赖属性和CLR属性在内存的使用上是截然不同的,每个CLR属性都包含一个非static的字段,因此当我们实例化一个类型的时候,就会创建该类型所拥有的所有CLR属性,也就是说一个对象所占用的内存在调用new操作进行实例化的时候就已经决定了、而wpf允许对象在创建的时候并不包含用于存储数据的空间,只保留在需要用到数据的时候能够获得该默认值,即用其他对象数据或者实时分配空间的能力。
三、如何自定控件、自定义依赖属性
自定义用户控件
1、自定义用户控件MyUserControl,继承UserControl。
2、在MyUserControl.xaml中设计控件
<Grid>
<TextBlock>我是自定义的依赖属性</TextBlock>
</Grid>
自定义依赖属性
1、声明依赖属性变量。依赖属性的声明都是通过public static来公开一个静态变量,变量的类型必须是DependencyProperty
2、在属性系统中进行注册。使用DependencyProperty.Register方法来注册依赖属性,或者是使用DependencyProperty.RegisterReadOnly方法来注册
3、使用.NET属性包装依赖属性
//1、声明依赖属性变量
public static DependencyProperty TextProperty;
//2、在属性系统中进行注册
TextProperty =DependencyProperty.Register("MyText", //属性名称
typeof(string), //属性类型
typeof(TestDependencyPropertyWindow), //该属性所有者,即将该属性注册到那个类上
new PropertyMetadata("")); //属性默认值
//3、使用.NET属性包装依赖属性:属性名称与注册时候的名称必须一致,即属性名MyText对应注册时的MyText
public string MyText
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// 参数:
// defaultValue:
// 依赖项属性,作为某种特定类型的值通常提供默认值。
// propertyChangedCallback:
// 对是有效的属性值发生更改时由属性系统调用的处理程序实现的引用。
public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback);
//1、声明依赖属性变量
public static readonly DependencyProperty MyColorProperty;
//2、在属性系统中进行注册
static MyUserControl()
{
MyColorProperty = DependencyProperty.Register("MyColor", typeof(string), typeof(MyUserControl),
new PropertyMetadata("Red", (s, e) =>
{
var mdp = s as MyUserControl;
if (mdp != null)
{
try
{
var color = (Color)ColorConverter.ConvertFromString(e.NewValue.ToString());
mdp.Foreground = new SolidColorBrush(color);
}
catch
{
mdp.Foreground = new SolidColorBrush(Colors.Black);
}
}
}));
}
//3、使用.NET属性包装依赖属性:属性名称与注册时候的名称必须一致,
//即属性名MyColor对应注册时的MyColor
public string MyColor
{
get{return (string)GetValue(MyColorProperty);}
set{SetValue(MyColorProperty, value);}
}
快速定义依赖属性的快捷方式:
输入propdp,连续按两下Tab健,自动生成定义依赖属性的语法。和输入cw连续按两下Tab健,自动生成Console.Write()一样。
附加属性(特殊的依赖属性)
依赖属性和附加属性区别
1、依赖属性
依赖属性:自己没有值,通过绑定的数据源 来获取值,依赖别人来传值,依赖属性的对象称为依赖对象。
几种应用依赖属性的场景:
1. 希望可在样式中设置属性。
2. 希望属性支持数据绑定。
3. 希望可使用动态资源引用设置属性。
4. 希望从元素树中的父元素自动继承属性值。
5. 希望属性可进行动画处理。
6. 希望属性系统在属性系统、环境或用户执行的操作或者读取并使用样式更改了属性以前的值时报告。
7. 希望使用已建立的、WPF 进程也使用的元数据约定,例如报告更改属性值时是否要求布局系统重新编写元素的可视化对象。
依赖对象创建时并不包含存储数据空间。WPF中必须使用依赖对象作为依赖属性的宿主。
<StackPanel>
<TextBox Name="tbColor"></TextBox>
<local:MyUserControl MyColor="{Binding Path=Text,ElementName=tbColor}" ></local:MyUserControl>
</StackPanel>
2、附加属性
特殊的依赖属性不同之处在于附加属性被应用到的类并非定义附加属性的那个类。
附加属性就是自己没有这个属性,在某些上下文中需要就被附加上去。比如TextBox的Grid.Row属性,如果我们定义TextBox类时定义一个Row属性是没有意义的,因为我们并不知道一定会放在Grid里,这样就造成了浪费。
<TextBox Grid.Row="1"></TextBox>
<TextBox Grid.Row="2"></TextBox>
定义附加属性,需要使用 Register Attached()方法,而不是使用 Register(()方法。
自定义附加属性给其他控件使用
1、创建MyProperty类继承DependencyObject
2、添加一个附加属性,快捷方法:键入"propa"双击"tap",并添加相应的值改变事件及事件处理逻辑
public static double GetAngle(DependencyObject obj)
{
return (double)obj.GetValue(AngleProperty);
}
public static void SetAngle(DependencyObject obj, double value)
{
obj.SetValue(AngleProperty, value);
}
public static readonly DependencyProperty AngleProperty =
DependencyProperty.RegisterAttached("Angle", typeof(double), typeof(MyProperty), new PropertyMetadata(0.0,OnAngleChanged));
//添加相应的值改变事件及事件处理逻辑
private static void OnAngleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var element = obj as UIElement;
if (element != null)
{
element.RenderTransformOrigin = new Point(0.5, 0.5);
element.RenderTransform = new RotateTransform((double)e.NewValue);
}
}
3、使用自定义附加属性
<Canvas >
<Ellipse Fill="Red" Width="100" Height="60" Canvas.Left="56" Canvas.Top="98" local:MyProperty.Angle="90"/>
<Rectangle Fill="Blue" Width="80" Height="80" Canvas.Left="285" Canvas.Top="171" local:MyProperty.Angle="45" />
<Button Content="Hello" Canvas.Left="265" Canvas.Top="78" FontSize="20" local:MyProperty.Angle="60"/>
</Canvas>
事件
简单实用实例:
<Button Name="button1" Height="30" Width="200" Click="click1">鼠标点击</Button>
private void click1(object sender, RoutedEventArgs e)//sender触发事件的控件 e事件参数
{
MessageBox.Show("鼠标点击");
button1.Content = "鼠标已点击";
}
打开新窗口:
1、新建窗口WrapPanelWindow
2、在主窗口中
<Button x:Name="buttonw" Click="buttonw_Click">环绕布局</Button>
3、在主窗口文件中
private void buttonw_Click(object sender, RoutedEventArgs e)
{
WrapPanelWindow wrapPanelWindow = new WrapPanelWindow();
wrapPanelWindow.Show();
}
打开文件:
<Button x:Name="button3" Margin="31,338,583.6,41" Click="button3_Click">打开文件</Button>
<Image x:Name="image1" Margin="268,328,359.6,10"/>
private void button3_Click(object sender, RoutedEventArgs e)
{
//SaveFileDialog 保存文件
OpenFileDialog openFile = new OpenFileDialog();//打开文件
openFile.Filter = "文本文件|*.txt|JPG图片|*.jpg";//过滤器
if (openFile.ShowDialog() == true)
{
String fileNmae = openFile.FileName;
image1.Source = new BitmapImage(new Uri(fileNmae));
}
}
逻辑树与可视树
逻辑树
逻辑树就是描述WPF界面元素的实际构成,它是由程序在XAML中所有的UI元素组成。最显著的特点就是由布局控件、或者其他常用的控件组成。Window、Grid、StackPanel、TextBox其实就是XAML界面的逻辑树。
可视树
可视树是由界面上可见的元素构成的,这些元素主要是由从Visual或者Visual3D类中派生出来的类。
Window、Grid、StackPanel、TextBox它们本身就包含一些由Visual或者Visual3D类派生出的一些可视树的元素来组成的。
路由事件
路由事件与一般事件的区别在于:路由事件是一种用于元素树的事件,当路由事件触发后,它可以向上或向下遍历可视树和逻辑树,他用一种简单而持久的方式在每个元素上触发,而不需要任何定制的代码(如果用传统的方式实现一个操作,执行整个事件的调用则需要执行代码将事件串联起来)。
所谓的路由策略就是指:路由事件实现遍历元素的方式。
三种路由策略(路由类型)
路由事件一般使用以下三种路由策略:
- 冒泡:由事件源向上传递一直到根元素。Button→Canvas→GridA→GridRoot→Window
- 直接:只有事件源才有机会响应事件。
- 隧道:从元素树的根部调用事件处理程序并依次向下深入直到事件源。有一个preview前缀
一般情况下,WPF提供的输入事件都是以隧道/冒泡对实现的。隧道事件常常被称为Preview事件。如果同时存在的话,先执行管道事件然后才执行配对的冒泡事件
自定义路由事件
依赖项属性是使用 Dependencyproperty Register()方法注册的,而路由事件是使用Eventmanager. Registerroutedevent()方法注册的。
public static RoutedEvent RegisterRoutedEvent(string name, RoutingStrategy routingStrategy, Type handlerType, Type ownerType);
四个参数:事件的名称、路由类型(路由策略)、定义事件处理程序语法的委托 、拥有事件的类。
//1、声明并注册路由事件,使用冒泡策略
public static readonly RoutedEvent MyClientEvent = EventManager.RegisterRoutedEvent("MyClick",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyUserControl));
//2、通过.NET事件包装路由事件
public event RoutedEventHandler MyClick
{
add{AddHandler(MyClientEvent, value);}
remove{RemoveHandler(MyClientEvent, value);}
}
/// 3、使用按钮的单击事件激发路由事件
private void Button_Click(object sender, RoutedEventArgs e)
{
RoutedEventArgs arg = new RoutedEventArgs();
arg.RoutedEvent = MyClientEvent;
RaiseEvent(arg);
}
//MyUserControl.xaml
<Button Height="30" Width="100" Content="调用自定义路由事件" Click="Button_Click"></Button>
//使用自定义路由事件
//使用自定义路由事件
<local:MyUserControl MyClick="MyClick" ></local:MyUserControl>
private void MyClick(object sender, RoutedEventArgs e)
{ //使用了自定义路由事件
MessageBox.Show("Hello:" + e.Source.ToString());
}
参数说明
private void buttonw_Click(object sender, RoutedEventArgs e)
两个参数:第一个为:System.Object对象,名为sender,第二个参数(一般名为e)是一个派生于System.EventArgs的类。sender参数就是该处理程序被添加的元素,参数e是RoutedEventArgs的一个实例提供了4个有用的属性:
Source—逻辑树中开始触发该事件的的元素。
originalSource–可视树中一开始触发该事件的元素。
handled—布尔值,设置为true表示事件已处理,在这里停止。
RoutedEvent—真正的路由事件对象,(如Button.ClickEvent)当一个事件处理程序同时用于多个路由事件时,它可以有效地识别被出发的事件。
生命周期事件
生命周期事件是在元素被初始化,加载或卸载时发生这些事情。都是在FrameworkElement类中定义的。
所有元素的生命周期
名称 | 说明 |
---|---|
Initialized | 当元素被初始化,并已根据Xaml标记设置了元素的属性之后发生,这时元素已经初始化,但窗口的其他部分可能尚未初始化,但是,此时还没应用样式和数据绑定,这时,IsInitialized属性为true。 |
Loaded | 当整个窗口已经初始化并应用了样式和数据绑定时,该事件发生,就是在元素呈现之前的最后一站,这时IsLoaded属性为true。 |
UnLoaded | 当元素被释放时,该事件发生。原因是包含元素的窗口被关闭或者特定的元素被从窗口中删除。 |
Window类的常用生命周期事件
名称 | 说明 |
---|---|
ContentRendered | 在窗口首次呈现后立即发生,ContentRendered事件表明窗口已经完全可见,并且已经准备好接收输入 |
Activated | 当用户切换到该窗口时发生(从其他窗口切换到当前窗口),当窗口第一次加载也会引发该事件 |
Deactivated | 当用户从该窗口切换到其他窗口时发生。 |
Closing | 当关闭窗口时发生,不管是用户关闭窗口,还是通过代码WIndow.Close()。 |
Closed | 当窗口已经关闭后发生,但是仍可以访问元素对象,当然是在UnLoaded事件尚未发生之前 |
输入事件
输入事件是当用户使用某些种类的外设硬件进行交互时发生的事件,如鼠标、键盘、手写笔或者多触控屏。输入事件可通过继承自InputEventArgs的自定义事件参数类型传递额外的信息。
键盘输入
事件执行顺序
名称 | 路由类型 | 说明 |
---|---|---|
PreviewKeyDown | 隧道 | 当按下一个键时发生 |
KeyDown | 冒泡 | 当按下一个键时发生 |
PreviewTextInput | 隧道 | 当输入完成且元素正在接收文本输入时发生,对不会产生文本输入的按键(Ctrl键、shift键、方向键等),不会引发该事件 |
TextInput | 冒泡 | 当输入完成且元素正在接收文本输入时发生,对不会产生文本输入的按键,不会引发该事件 |
PreviewKeyUp | 隧道 | 当释放一个键时发生 |
KeyUp | 冒泡 | 当释放一个键时发生 |
焦点
在Windows的世界中,用户每次只能使用一个控件,当前接收用户按键的控件是具有焦点的控件。为了让控件能接受焦点,必须将Focusable设置为true,这是所有控件的默认值,如果为TextBox元素的Focusable元素设置为false,就不会获取焦点了
获取键盘状态
当发生按键事件时,经常需要知道更多的信息,不仅需要知道按下的是哪个键,其他键是否按下了也同样重要,特别是Shift键、Ctrl键、Alt键。
KeyBoardDevice属性提供了KeyBoardDevice类的一个实例,它的属性包含了当前是哪个元素具有焦点以及按下了哪些修饰键,可用代码显示。
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
MessageBox.Show("你点击了Control键");
}
//还可以使用KeyBoard类,该类和KeyboardDevice类非常类似,只是Keyboard类由静态成员构成。
if (Keyboard.IsKeyDown(Key.LeftCtrl))
{
MessageBox.Show("按下了Ctrl键盘");
}
鼠标输入
所有元素的鼠标单击事件(按顺序排序)
名称 | 路由类型 | 说明 |
---|---|---|
PreviewMouseLeftButtonDown PreviewMouseRightButtonDown | 隧道 | 按下鼠标左键时发生 |
MouseLeftButtonDown MouseRightButtonDown | 冒泡 | 按下鼠标左键时发生 |
PreviewMouseLeftButtonUp PreviewMouseRightButtonUp | 隧道 | 按下鼠标右键时发生 |
MouseLeftButtonUp MouseRightButtonUp | 冒泡 | 按下鼠标右键时发生 |
PreviewMouseWheel | 隧道 | 鼠标滚轮动作 |
MouseWheel | 冒泡 | 鼠标滚轮动作 |
捕获鼠标
当鼠标被一个元素捕获时,就不能与其他元素进行交互了(不能单击窗口中的其他元素),鼠标捕获通常用于短时间的操作。当点击button按钮后,整个窗口的其他元素和按钮就不能点击了,只有把光标移开该窗口,然后点击一下,这个窗口的元素才可以点击。
Mouse.Capture(btn1);
鼠标拖放
拖放操作的方法和事件都集中在System.Windows.DragDrop类中。
拖放操作有以下3个步骤:
1)、用户单击元素,并保持鼠标按键为按下状态,这时,某些信息被搁置起来,并且拖放操作开始。
2)、用户将鼠标移动到其他元素上,如果该元素可接受正在拖动的内容的类型,鼠标指针会变成拖放图标,否则鼠标指针会变成内部有一条线的圆形。
3)、当用户释放鼠标键时,元素接收信息并决定如何处理接收到的信息,在没有释放鼠标键时,可按下Esc键取消该操作。
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--为源元素设置MouseDown事件-->
<Label Name="lbl1" MouseDown="lbl1_MouseDown" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">Hello,World!!!</Label>
<TextBox Name="textBox1" Grid.Column="1" Height="35" Margin="10"></TextBox>
<!--为目标属性设置AllowDrop属性,然后再Drop事件中写内容-->
<Label Name="lbl2" Grid.Row="1" Drop="lbl2_Drop" AllowDrop="True" HorizontalAlignment="Center" VerticalAlignment="Center">今天天气好晴朗!!!</Label>
</Grid>
/// 源元素的MouseDown事件。
private void lbl1_MouseDown(object sender, MouseButtonEventArgs e)
{
//转换成Label对象。
Label lbl = (Label)sender;
//DragDrop类用于拖动操作。
//DoDragDrop()方法用于启动拖放操作。参数: 源对象,源对象的属性,拖放的效果(复制,移动...)
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Scroll);
}
/// 目标元素的Drop事件。
private void lbl2_Drop(object sender, DragEventArgs e)
{
//获取指定数据对象,格式由字符串指定。
((Label)sender).Content = e.Data.GetData(DataFormats.Text);
}
控件
控件通用属性
Visibility控件是否可见:枚举类型:Visible表示可见、 Collapsed不可见,不占空间。Hidden不可见,不占空间。
IsEnabled:控件是否可用:bool类型。
Background:背景“色”
Fontsize字体大小
背景和前景
所有控件都继承自System.Windows.Control
所有控件都有背景和前景:
背景:Background 空间表面
前景:Foreground 文本
使用Brush(画刷)对象,SolidColorBrush单色画刷
<TextBlock Background="Yellow" Foreground="Red">
button1.Background = new SolidColorBrush(Colors.AliceBlue);
button1.Foreground = new SolidColorBrush(Color.FromRgb(3, 55, 55)); //红绿蓝:0-255
字体
字体属性
名称 | 说明 |
---|---|
FontFamily | 字体名称 |
FontSize | 字体尺寸 |
FontWeight | 字体粗细 |
FontStyle | 字体风格 |
FontStretch | 字体拉伸压缩程度 |
鼠标光标
System.Windows.Input.Cursors
设置鼠标光标样式,可设样式详见System.Windows.Input.Cursors,设置方法如下:
<Button Cursor="Help">鼠标光标</Button>
stackPanel1.Cursor = Cursors.Wait;
常用控件
内容控件
内容控件,可包含显示一块内容,可以嵌套元素的控件,只能包含一个子元素
标签
可以为控件设置焦点快捷键,使用Target绑定元素,下划线指示快捷键,按下Alt显示快捷键字符,如下例子按下Atl+A,textA得到焦点。
<Label Target="{Binding ElementName=textA}">标签_A</Label>
<TextBox Name="textA" ></TextBox>
按钮
Button、CheckBox复选框、RadioButton单选框。继承自ButtonBase类。
- Button
- CheckBox: IsChecked:是否选中,bool?
- RadioButton:GroupName组名,同组中只能选中一个
可空类型:bool、 datetime等值类型不可以为null,但是可以在类型后?则为可空类型。bool?转换为bool需要强制类型转换
<StackPanel Grid.Row="0" Grid.Column="1" >
<StackPanel x:Name="StackPanel1">
<CheckBox IsChecked="True">CheckBox1</CheckBox>
<CheckBox>CheckBox2</CheckBox>
<CheckBox>CheckBox3</CheckBox>
</StackPanel>
<StackPanel x:Name="StackPanel2">
<RadioButton GroupName="Group1">RadioButton1</RadioButton>
<RadioButton GroupName="Group1">RadioButton2</RadioButton>
<RadioButton GroupName="Group1">RadioButton3</RadioButton>
</StackPanel>
<Button Name="Button1" Click="Button1_Click">确定选择</Button>
</StackPanel>
private void Button1_Click(object sender, RoutedEventArgs e)
{
string s1 = null;
foreach (CheckBox chk in StackPanel1.Children)
{
if ((bool)chk.IsChecked)
{
s1 = s1 + "--" + chk.Content;
}
}
foreach (RadioButton radioButton in StackPanel2.Children)
{
if ((bool)radioButton.IsChecked)
{
s1 = s1 + "--" + radioButton.Content;
}
}
MessageBox.Show(s1);
}
工具提示
ToolTip,鼠标放到工具栏的工具上的信息提示框,相对于直接使用ToolTip,为元素设置ToolTip属性(不能进行复杂信息设置)更加简单。不能接受焦点(在ToolTip中放了按钮,能显示不能点击)
<Button ToolTip="按钮">按钮</Button>
<Button Content="Tooltip复杂写法">
<Button.ToolTip>
<ToolTip>
<StackPanel Background="#FFD5E87E">
<TextBlock FontWeight="Heavy">Tooltip复杂写法</TextBlock>
</StackPanel>
</ToolTip>
</Button.ToolTip>
</Button>
ToolTipService
ToolTipService定义了许多与ToolTip相同的属性,方便对简单的只有文本提示时使用。
名称 | 说明 |
---|---|
ShowDuration | 工具提示显示时间3000=3秒 |
InitialShowDelay | 工具提示延时出现时间 |
BetweenShowDelay | 工具提示出现后,在这个时间内,移动到另一个控件,不用等待延时提示时间 |
ShowOnDisabled | 是否显示禁用控件,默认false |
HasDropShadow | 是否具有阴影扩散,使其与窗口区分开来 |
<Button ToolTip="ToolTipService演示" ToolTipService.InitialShowDelay="3000"
ToolTipService.ShowDuration="3000" ToolTipService.BetweenShowDelay="3000"
ToolTipService.HasDropShadow="True">
ToolTipService演示
</Button>
Popup
与ToolTip作用类似,可以接受焦点,可以放按钮并点击。通过设置IsOpen属性值显示或隐藏,默认false。StaysOpen属性默认为true,一直显示。
特殊控件
用于构造用户界面中比较大的部分区域
ScrollViewer
VerticalScrollBarVisibility属性:是否显示滚动条,VerticalBarVisibility枚举值:默认Vertical显示,Auto自动,Disabled不显示,Hidden隐藏但可滚动
HorizontalScrollBarVisibility属性:水平滚动条默认"Disabled"
<ScrollViewer VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled">
<TextBox TextWrapping="Wrap">哈哈哈哈哈哈哈哈哈哈或或或或或或哈哈哈哈哈哈哈哈哈哈或或或或或或或</TextBox>
</ScrollViewer>
GroupBox
具有圆角和标题的方框
<GroupBox Header="GroupBox" Margin="164,66,407.6,265"></GroupBox>
TabControl与TabItem
TabControl:TabStripPlacement选项卡的显示Top、Left、Bottom、Right
TabItem :IsSelected为true是未选中,如下:2为true默认显示2选项卡
<TabControl TabStripPlacement="Top">
<TabItem Header="1"></TabItem>
<TabItem Header="2" IsSelected="True"></TabItem>
<TabItem Header="3"></TabItem>
</TabControl>
Expander
通过单击小箭头,显示或隐藏所包含内容
IsExpanded:是否扩展(将内容展开)不,默认False不展开
ExpandDirection:扩展方向(内容展开方向)Up、Down、Left、Right
<Expander IsExpanded="False" ExpandDirection="Down">
<TextBlock>哈哈哈哈哈</TextBlock>
</Expander>
文本控件
TextBox
MaxLength:可输入的最大字符数
TextWrapping:是否自动换行(Wrap自,Nowrap不自动换行);
IsReadOnly:是否只读
AcceptsReturn:是否支持enter回车键
AcceptsTab:是否支持tab键
VerticalScrollBarVisibility:垂直滚动条
HorizontalScrollBarVisibility:水平滚动条
IsUndoEnabled:是否支持撤消ctrl+z
<TextBox TextWrapping="Wrap" IsReadOnly="False" MaxLength="50" AcceptsReturn="True" AcceptsTab="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsUndoEnabled="False">
哈哈哈哈哈哈哈哈哈哈哈或或或或或或或或或或或或或
</TextBox>
PasswordBox
密码框Password属性为密码
列表控件
ListBox
SelectionMode:选择模式:Multiple 多选,Single 单选(默认)、Extended按住ctrl可进行多选,选中值为SelectedItem或SelectedItems
<WrapPanel>
<ListBox Name="ListBox1" SelectionMode="Extended" Height="70" Width="200" VerticalAlignment="Top">
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
<ListBoxItem>3</ListBoxItem>
<ListBoxItem>4</ListBoxItem>
<ListBoxItem>5</ListBoxItem>
<ListBoxItem>6</ListBoxItem>
<ListBoxItem>7</ListBoxItem>
</ListBox>
<Button Height="40" Width="100" Click="Button_Click">查看选中项</Button>
</WrapPanel>
private void Button_Click(object sender, RoutedEventArgs e)
{
string selectedText = null;
foreach(ListBoxItem listBoxItem in ListBox1.SelectedItems){
selectedText = selectedText + "--" + listBoxItem.Content;
}
MessageBox.Show(selectedText);
}
范围控件
ScrollBar、Slider、ProgressBar继承自RangeBase类
Value:当前值
Minimum:最小值
Maximum:最大值
ScrollBar
一般不用ScrollBar,而是用ScrollViewer (封装了两个ScrollBar)
Slider
Orientation:水平或垂直
Delay:移动时间,毫秒值
Interval:再次移动时间
TickPlacement:刻度条显示位置
TickFrequency:刻度间距
Tick:不规则刻度间距"10,30,50"
IsSnapToTickEnabled:是否跳到最近刻度,默认false
IsSelectionRangeEnabled:可选择范围显示阴影
SelectionStart、SelectionEnd:可选择范围
<Slider Orientation="Horizontal" Delay="5" Interval="2" Value="10"
Minimum="0" Maximum="100" TickPlacement="BottomRight" TickFrequency="10" IsSnapToTickEnabled="True" IsSelectionRangeEnabled="True" SelectionStart="30" SelectionEnd="80"></Slider>
ProgressBar
进度条:IsIndeterminate是否不确定模式;
日期控件
Calendar与 DatePicker
SelectedDate/SelectedDates:选中日期
DisplayDateStart、DisplayDateEnd:设置可选日期范围
IsTodayHighlighted:当前日期今天突出显示
FirstDayOfWeek:日历第一行为星期几
SelectionMode:只有Calendar有
<Calendar Margin="3,196,591,67" SelectionMode="SingleRange"></Calendar>
<DatePicker Name="date1" Margin="214,162,350,213" IsTodayHighlighted="True" FirstDayOfWeek="Sunday" ></DatePicker>
其他控件
Image: Source属性为图片地址,相对路径。
菜单分为普通莱单(Menu)和上下文莱单( ContextMenu,俗称右键菜单)两种;
WPF中的Menu可以显示在任意位置,但是一般使用布局显示到页面顶部
Menu下面可以放多个 MenuItem(其实是放到Items属性中),
MenuItem下还可以放 MenuItem(还是 Items属性中);属性 Header显内容
<Menu Margin="10,261,600.6,122">
<MenuItem Header="文件">
<MenuItem Header="打开"></MenuItem>
<MenuItem Header="关闭"></MenuItem>
</MenuItem>
<MenuItem Header="工具"></MenuItem>
</Menu>
ToolBar:工具条控件,放入其中的控件都有新的默认外观
显示图标的方法: Button的 Content中放Image
资源:图标库
Application类。
Application对象用的名称空间是system.windows
1、手动创建Application对象步骤。
1.1)、把项目中的App.Xaml文件从项目中排除。
1.2)、新建Startup类。
1.3)、在类中声明静态的、无返回值的Main方法。
1.4)、在方法中创建application对象和窗口对象。
public class Startup
{
[STAThread] //指定应用程序的 COM 线程模式是单线程单元(STA)
static void Main()
{
//方法1:
//创建Application对象。
Application app = new Application();
//实例化窗口。
MainWindow mw = new MainWindow();
//当向Application.Run()方法传递一个窗口时,该窗口被设置为主窗口,可通过Application.MainWindow属性在整个应用程序中访问这个窗口。然后使用Run()方法触发Application.Startup事件显示主窗口。
app.Run(mw);
//方法2:
//创建Application对象。
Application app = new Application();
//实例化窗口。
MainWindow mw = new MainWindow();
//设置应用程序主窗口。
app.MainWindow = mw;
//弹出窗口
mw.Show();
//当向Application.Run()方法传递一个窗口时,该窗口被设置为主窗口,可通过Application.MainWindow属性在整个应用程序中访问这个窗口。然后使用Run()方法触发Application.Startup事件显示主窗口。
app.Run();
//方法3
//创建application对象。
Application app = new Application();
//设置uri。
app.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
app.Run();
}
}
2、设置应用程序关闭方式。
通常只要窗口尚未关闭,Application类就保持应用程序处于有效状态,如果这不是期望的行为,可调整Application.ShutdownMode属性。如果手动创建Application对象就需要在调用Run()方法之前设置ShutDownMode属性。如果使用的是App.Xaml文件,那么可在xaml文件中单独设置ShutdownMode属性。 当执行Application.Run()方法的时候会导致Application.Run()方法立即返回。
ShutdownMode枚举值
名称 | 说明 |
---|---|
OnLastWindowClose | 默认行为,只要至少还有一个窗口存在,应用程序就会保持运行状态。 |
OnMainWindowClose | 传统方式,只要主窗口还处于打开状态,应用程序就会保持运行状态。 |
OnExceptionShutdown | 除非调用Application.Shutdown()方法,否则应用程序就不会结束(即使所有程序都已经关闭)。 |
3、常用的应用程序事件。
名称 | 说明 |
---|---|
Startup | 该事件在调用Application.Run()方法之后,并且在主窗口显示之前执行。 |
Exit | 该事件在应用程序关闭时(不管什么原因),并在Run方法即将返回之前发生,要放在Run()方法之前 |
SessionEnding | 该事件在Windows对话结束时发生 |
Activated | 当激活应用程序中的窗口时发生该事件,当切换到另外一个window程序时也会触发。 |
Deactivated | 当取消激活应用程序中的窗口时发生该事件,当切换到另外一个window程序时也会触发。 |
DispatcherUnhandledException | 应用程序只要发生未处理的异常,都会进入该事件。还可将Handled属性设置为true,继续运行应用程序。 |
4、显示初始界面。
WPF应用程序的速度快,但并不能在瞬间启动,第一次启动应用程序的时候,会有一些延迟,因为公共语言运行时,首先要初始化 .Net环境,然后启动应用程序,这个时候显示初始界面就有作用了。在Run方法之前执行以下代码。
//屏幕初始化的时候的背景图片。
SplashScreen splashScreen = new SplashScreen("1.png");
//显示初始屏幕。
splashScreen.Show(true);
//淡出初始界面时间(单位是秒)。
splashScreen.Close(TimeSpan.FromSeconds(1));
5、访问当前Application对象。
通过Application.Current.MainWindow属性可在应用程序的任何位置获取当前应用程序实例。从而在多个窗口之间进行基本交互。
假设MainWindow(主窗口)中有一个Test()方法(public),那么在Window1窗口中可通过以下方式进行访问:
//获取主窗口对象。
MainWindowainWindow mw = (MainWindow)Application.Current.MainWindow;
//调用主窗口Test()方法。
mw.Test();
6、窗口之间进行交互步骤。
6.1)、删除App.xaml文件。
6.2)、新建一个App的类且继承自Application,在里面设置窗口启动项,在写一个Window类型的集合。
6.3)、在主窗口实例化子窗口时,为子窗口设置Ower属性等于主窗口。
6.4)、在子窗口中通过Application.Current.MainWindow属性得到主窗口对象,并调用方法。
6.5)、在主窗口中循环遍历集合中的数据进行设置子窗口。
App类:
public class App : Application
{
[STAThread]
public static void Main()
{
//实例化类。
App app = new App();
app.InitializeComponent();
app.Run();
}
private void InitializeComponent()
{
//设置主窗口启动项。
this.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
}
//声明一个Window类型的集合,用于放置子窗口。
private List<Window> listWindow = new List<Window>();
public List<Window> ListWindow
{
get { return listWindow; }
set { this.listWindow = value; }
}
}
MainWinow后台代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
//窗口加载的时候把Winow1窗口弹出来。
Window1 w1 = new Window1();
//为Window1Owner属性。
w1.Owner = this;
//显示出来。
w1.Show();
//将Winow1加入到集合中。
((App)App.Current).ListWindow.Add(w1);
}
/// 此方法用于将主窗口标题设置为当前时间。
public void SetMainWinowTime()
{
this.Title = DateTime.Now.ToString();
}
/// 此方法用于设置子窗口标题为当前时间。
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < ((App)App.Current).ListWindow.Count; i++)
{
//将集合中的向转换为Window1对象,然后调用SetWindowTime()方法。
((Window1)((App)App.Current).ListWindow[i]).SetWindowTime();
}
}
}
Window窗口后台代码:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
/// 此方法用于设置子窗口标题为当前时间。
public void SetWindowTime()
{
this.Title = DateTime.Now.ToString();
}
/// 此方法用于获取主窗口,然后调用SetMainWinowTime()方法。
private void Button_Click(object sender, RoutedEventArgs e)
{
((MainWindow)Application.Current.MainWindow).SetMainWinowTime();
}
}
7、单实例应用程序。
实现步骤:
7.1)、删除App.xaml文件。
7.2)、引用Microsoft.VisualBasic程序集。
7.3)、创建SignelInstanceApplication类,让其继承自Application类,在类中重写OnStartup方法(用于实例化窗口),创建Activate()方法(用于激活当前窗口)。
7.4)、创建SignelApplicationManager类,此类继承自WindowsFormsApplicationBase类,此类中包含了3个重要的成员。
7.5)、写一个Startup类,在里面写Main方法,实例化SignelApplicationManager类。
SignelInstanceApplication类:
public class SignelInstanceApplication : System.Windows.Application
{
/// 重写OnStartup()方法,在方法中实例化主窗口。
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
base.OnStartup(e);
//在OnStartup()方法中实例化窗口对象(在这里是主窗口)。
MainWindow mw = new MainWindow();
mw.Show();
}
/// <summary>
/// 此方法用于激活当前窗口(让窗口显示最前端)。
/// </summary>
public void Activate()
{
System.Windows.MessageBox.Show("此程序已运行"); //再做其他的处理。
this.MainWindow.Show();
this.MainWindow.Activate();
}
}
SignelApplicationManager类:
class SignelApplicationManager : WindowsFormsApplicationBase
{
//声明application对象。
SignelInstanceApplication app;
/// 在构造函数中设置IsSingleInstance为单例模式。
public SignelApplicationManager()
{
//IsSingleInstance:确定此应用程序是否为单例应用程序。
this.IsSingleInstance = true;
}
/// 在OnStartup方法中实例化SignelInstanceApplication对象。
protected override bool OnStartup(StartupEventArgs eventArgs)
{
base.OnStartup(eventArgs);
//实例化SignelInstanceApplication对象。
app = new SignelInstanceApplication();
app.Run();
//返回false。
return false;
}
/// 在OnStartupNextInstance调用激活函数。
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
base.OnStartupNextInstance(eventArgs);
app.Activate();
}
}
Startup类:
public class Startup
{
[STAThread()]
public static void Main(string[] args)
{
//实例化SignelApplicationManager对象。
SignelApplicationManager sm = new SignelApplicationManager();
//调用Run()。
sm.Run(args);
}
}
程序集资源
直接在项目中添加资源如图片(可以新建一个文件夹用于存放相关资源),将资源属性中的Build Action(生成操作) 设为Resource。
内容文件
将资源属性中的Build Action(生成操作) 设为Content。
- 希望改变资源文件,又不想重新编译应用程序。
- 资源文件非常大。
- 资源文件是可选的,并且可以不随程序集一起部署。
- 资源是声音文件。
元素绑定
1、绑定表达式。
数据绑定表达式使用的是XAML标记扩展(因此具有花括号),用到的是System.Windows.Data.Bingding类的一个实例
基本语法是:{Binding ElementName=源元素 Path=源属性}
ElementName:绑定源元素名
Path:绑定源元素属性
Mode:绑定方式
UpdateSourceTrigger:触发方式
XAML代码:
<TextBox Name="TextBox1"/>
<TextBlock Name="TextBlock1" Text="TextBlock" FontSize="{Binding ElementName=TextBox1,Path=Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>