第3章:布局

本章目标

  • 理解布局的原则
  • 理解布局的过程
  • 理解布局的容器
  • 掌握各类布局容器的运用

理解 WPF 中的布局

WPF 布局原则

​ WPF 窗口只能包含单个元素。为在WPF 窗口中放置多个元素并创建更贴近实用的用户男面,需要在窗口上放置一个容器,然后在这个容器中添加其他元素。造成这一限制的原因是 Window 类继承自 ContentControl 类,在后续章节中将进一步分析ContentControl类。

布局过程

​ WPF 布局包括两个阶段:测量(measure)阶段和排列(arange)阶段。在测量阶段,容器遍历所有子元素,并询问子元素它们所期望的尺寸。在排列阶段,容器在合适的位置放置子元素。

​ 当然,元素未必总能得到最合适的尺寸—有时容器没有足够大的空间以适应所含的元素。在这种情况下,容器为了适应可视化区域的尺寸,就必须剪裁不能满足要求的元素。在后面可以看到,通常可通过设置最小窗口尺寸来避免这种情况。

注意:

布局容器不能提供任何滚动支持.相反,滚动是由特定的内容控件ScrollViewer—一提供的,ScrollViewer 控件几乎可用于任何地方。

布局容器

​ 所有 WPF 布局容器都是派生自 System.Windows.Controls.Panel 抽象类的面板(见下图)。Panel 类添加了少量成员,包括三个公有属性,下表列出了这三个公有属性的详情。

名称 说明
Background 该属性是用于为面板背景着色的画刷。如果想接收鼠标事件,就必须将该属性设置为非空值(如果想接收鼠标事件,又不希望显示固定颜色的背景,那么只需要将背景色设置为透明即可)
Children 该属性是在面板中存储的条目集合。这是第一级条目—换句话说,这些条目自身也可以包含更多的条目。
IsItemsHost 该属性是一个布尔值,如果面板用于显示与 ItemsControl 控件关联的项(例如,TreeView 控件中的节点或列表框中的列表项),该属性值为 true。在大多数情况下,甚至不需要知道列表控件使用后台面板来管理它所包含的条目的布局。但如果希望创建自定义的列表,以不同方式放置子元素(例如,以平铺方式显示图像的 ListBox控件),该细节就变得很重要了。

​ 就Panel基类本身而言没有什么特别的,但它是其他更多特类的起点。WPF提供了大量可用于安排布局的继承自Panel的类,下表中列出了其中几个最基本的类。与所有 WPF控件和大多数可视化元素一样,这些类位于 System. Windows.Controls 名称空间中。

名称 说明
StackPanel 在水平或垂直的堆栈中放置元素。这个布局容器通常用于更大、更复杂窗口中的一些小区域.
WrapPanel 在一系列可换行的行中放置元素。在水平方向上,WrapPanel 面板从左向右放置条目,然后在随后的行中放置元素。在垂直方向上,WrapPanel 面板在自上而下的列中放置元素,并使用附加的列放置剩余的条目.
DockPanel 根据容器的整个边界调整元素
Grid 根据不可见的表格在行和列中排列元素,这是最灵活、最常用的容器之一.
UnitformGrid 在不可见但是强制所有单元格具有相同尺寸的表中放置元素,这个布局容器不常用.
Canvas 使用固定坐标绝对定位元素。这个布局容器与传统 Windows 窗体应用程序最相似,但没有提供锚定或停靠功能。因此,对于尺寸可变的窗口,该布局容器不是合适的选择。如果选择的话,需要另外做一些工作。

StackPanel 面板

​ StackPanel 面板是最简单的布局容器之一。该面板简单地在单行或单列中以堆栈形式放置其子元素。例如,下面的代码段中,窗口包含4个按钮。

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">
    <StackPanel>
        <Label>A Button Stack</Label>
        <Button>button1</Button>
        <Button>button2</Button>
        <Button>button3</Button>
        <Button>button4</Button>
    </StackPanel>
</Window>

​ 认情况下,StackPane! 面板按自上而下的顺序推列元素,使每个元素的高度适合它的内≥。在这个示例中,这底味老标签和技钮的大小刚好足够适应它们内部包含的文本。所有元素都被拉伸到 SatckPane! 面板的整个宽度,这也是窗口的宽度。如果加宽窗口,StackPanel 面板也会变宽,并且按钮也会拉伸自身以适应变化。

​ 通过设置 Oricntation 属性,StackPanel 面板也可用于水平排列元素:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">
    <StackPanel Orientation="Horizontal">
        <Label>A Button Stack</Label>
        <Button MinWidth="100">button1</Button>
        <Button MinWidth="100">button2</Button>
        <Button MinWidth="100">button3</Button>
        <Button MinWidth="100">button4</Button>
    </StackPanel>
</Window>

布局属性

​ 尽管布局由容器决定,但子元素仍有一定的决定权。实际上,布局面板支持一小组布局属性,以便与子元素结合使用,下表中列出了这些布局属性。

名称 说明
HorizontalAlignment 前水平方向上新额外的空闲时,该属性决定了子元素在布局容器中如何定位。可选用 Center、Left、 Right 或 Stretch 等属性值。
VerticalAlignment 当垂直方向上有额外的空闲时,该属性决定了子元素在布局容中如何定位。可选用 Center、Top、Bottom 或 Stretch 等属性值。
Margin 该属性用于在元素的周围添加一定的空间.Margin 属性是 System.Windows.Thickness结构的一个实例,该结构具有分别用于为顶部、底部、左边和右边添加空间的独立组件。
MinWidth 和 MinHeight 这两个属性用于设置元素的最小尺寸。如果一个元素对于其他布局容器来说太大,该元素将被剪裁以适合容器。
MaxWidth 和 MaxHeight 这两个属性用于设置元素的最大尺寸。如果有更多可以使用的空间,那么在扩展子元素时就不会超出这一限制,即使将 HorizontalAlignment 和 VerticalAlignment 属性设置为 Stretch 也同样如此。
Width 和 Height 这两个属性用于显式地设置元素的尺寸。这一设置会重写为 HorizontalAlignment 和VerticalAlignment 属性设置的 Stretch 值。但不能超出MinWidth、MinHeight、MaxWidth 和 MaxHeight 属性设置的范。

对齐方式

​ 通常,对于 Label 控件,HorizontalAligament 属性的值默认为 Lef:对于 Button 控件,HorzontalAlignment 属性的售默认为 Streteh。这也是为什么每个技钮的宽度被调整为控列的宽度的原因所在。但可以改变这些细节:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">
    <StackPanel>
        <Label HorizontalAlignment="Center">A Button Stack</Label>
        <Button HorizontalAlignment="Left">button1</Button>
        <Button HorizontalAlignment="Right">button2</Button>
        <Button>button3</Button>
        <Button>button4</Button>
    </StackPanel>
</Window>

​ 现在前两个按钮的尺寸是它们应当具有的最小尺寸,并进行了对齐,而底部两个按钮被拉伸至整个 SiackPanel面板的宽度。如果改变窗口的尺寸,就会发现标签保持在中间位置,而前两个按钮分别被粘贴到两边。

注意:

​ SiackPanel面板也有自己的HorizontalAlignment 和 VerticalAlignment 属性。这两个属性职认都被设置为 Stretch,所以 StackPanel 面板完全充满它的容器、在这个示例中,这意味着stackPanel西板充滿整个窗口。如果使用不同设置,StackPanel 面板的尺寸将足够宽以容纳最宽的控件。

边距

​ 在SackPane!示例中,在当前情况下存在一个明显的问题。设计良好的窗口不只是包含元素—还应当在元素之间包貪一定的额外空间。为了添加额外的空间并使StaokPanel 面板示例中的按钮不那么紧密,可为控件设置边距。

​ 当设置边距时,可为所有边设置相同的宽度,如下所示:

<Button Margin="5">button3</Button>

​ 相应地,也可为控件的每个边以左、上、右、下的顺序设置不同的边距:

<Button Margin="5,10,5,10">Button 3</Button>

​ 在代码中,使用 Thickness 结构来设置边距:

btn. Margin = new Thickness (5) ;

​ 为得到正确的控件边距,需要采用一些艺术手歐,因內需要考慮相邻控件边距改置的相互影响。例如,如果两个技钮堆在一起,位于最高处的技钮的底部边距设置为5,而下面技钮的顶部边距也设置为5,那么在这两个按钮之间就有10个单位的空间。

​ 理想情况是,能尽可能始终如一地保持不同的边距设置,避免为不同的边设置不同的值。
例如,在StackPanel示例中,为按钮和面板本身使用相同的边距是比较合适的,如下所示:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">
    <StackPanel Margin="5">
        
        <Label Margin="3" HorizontalAlignment="Center">A Button Stack</Label>
        <Button Margin="3" HorizontalAlignment="Left">button1</Button>
        <Button Margin="3" HorizontalAlignment="Right">button2</Button>
        <Button Margin="3">button3</Button>
        <Button Margin="3">button4</Button>
        
    </StackPanel>
</Window>

尺寸设置

​ 最后,每个元素都提供了 Height 和 Width 属性,用于显式地指定元素大小。但这种设置一般不是一个好主意。相反,如有必要,应当使用最大尺寸和最小尺寸属性,将控件限制在正确范围内。

提示:

在WPF中显式地设置尺寸之前一定要三思。在良好的布局设计中,不必显式地设置尺寸.
如果确实添加了尺寸信息,那就冒险创建了一种更不稳定的布局,这种布局不能适应变化(例如,不能适应不同的语言和不同的窗口尺寸),而且可能剪裁您的内容.

​ 例如,您可能决定拉伸 StackPanel 容器中的按钮,使其适合 StackPanel,但其宽度不能超过200单位,也不能小于100单位(默认情况下,最初按钮的最小宽度是75单位)。

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">
    <StackPanel Margin="5">
        
        <Label Margin="3" HorizontalAlignment="Center">A Button Stack</Label>
        <Button Margin="3" MaxWidth="200" MinWidth="100">button1</Button>
        <Button Margin="3" MaxWidth="200" MinWidth="100">button2</Button>
        <Button Margin="3" MaxWidth="200" MinWidth="100">button3</Button>
        <Button Margin="3">button4</Button>
        
    </StackPanel>
</Window>

​ 当 StackPanel 调整按钮的尺寸时,需要考虑以下几部分信息:

  • 最小尺寸: 每个按钮的尺寸始终不能小于最小尺寸。
  • 最大尺寸: 每个按钮的尺寸始终不能超过最大尺寸(除非执行错误操作,使最大尺寸比最小尺寸还小)
  • 内容: 如果按钮中的内容需要更大的宽度,StackPanel 容器会尝试扩展技钮(可以通过检查 DesiredSized 属性确定所需的按钮大小,该属性返回最小宽度或内容的宽度,返回两者中较大的那个)。
  • 容器尺寸: 如果最小宽度大于 StackPanel 面板的宽度,按钮的一部分将被剪裁掉。否则,不允许按钮比 StackPanel 面板更宽,即使不能适合按钮表面的所有文本也同样如此。
  • 水平对齐方式: 因为默认情况下按钮的 HorizontalAlignment 属性值设置为Stretch,所以 StackPanel 面板将尝试放大按钮以占满 StackPanel 面板的整个宽度。

Border 控件

​ Border控件不是布局面板,而是非常便于使用的元素,经常与布局面板一起使用。所以,在继续介绍其他布局面板之前,现在先介绍一下 Border 控件是有意义的。

​ Border 类非常简单。它只能包含一段嵌套内容(通常是布局面板),并为其添加背景或在其周围添加边框。为了深入地理解Border 控件,只需要掌握下表中列出的属性就可以了。

名称 说明
Barckground 使用Brush 对象设置边框中所有内容后面的背景。可使用固定颜色背景,也可使用其他更特殊的背景.
BorderBush和BorderThickness 使用Brush 对象设置位于Border 对象边缘的边框的颜色,并设置边框的宽度。为显示边框,必须设置这两个属性.
CornerRadius 该属性可使边框具有雅致的圆角。ComerRadius 的值越大,圆角效果就越明显.
Padding 该属性在边框和内部的内容之间添加空间(与此相对,Margin 属性在边框之外添加空间).

​ 下面是一个具有轻微圆角效果的简单边框,该边框位于一组按钮的周围,这组按钮包含在一个StackPanel 面板中:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Border Margin="5" Padding="5" Background="LightYellow"
         BorderBrush="SteelBlue" BorderThickness="3,3,3,5" CornerRadius="3"
         VerticalAlignment="Top">

        <StackPanel >
            <Button Margin="3">button1</Button>
            <Button Margin="3">button2</Button>
            <Button Margin="3">button3</Button>
        </StackPanel>

    </Border>
</Window>

WrapPanel 和 DockPanel 面板

WrapPanel 面板

​ WrapPanel 面板在可能的空间中,以一次一行或一列的方式布置控件。默认情况下,WrapPanel.Orientation 属性设置为 Horizontal;控件从左向右进行排列,再在下一行中排列。但可将 WrapPanel.Orientation 属性设置为 Vertical,从而在多个列中放置元素。

提示:

与StackPanel 面板类似,WrapPanel 面板实际上主要用来控制用户界面中一小部分的布局细节,并非用于控制整个窗口布局。例如,可能使用 WrapPanel面板以类似工具栏控件的方式将所有按钮保持在一起。

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <WrapPanel Margin="3">
        <Button VerticalAlignment="Top">Button1</Button>
        <Button MinHeight="60">Button2</Button>
        <Button VerticalAlignment="Bottom">Button3</Button>
        <Button>Button4</Button>
        <Button VerticalAlignment="Center">Button5</Button>
    </WrapPanel>

</Window>

注意:

WrapPanel 面板是唯一一个不能通过灵活使用Grid 面板代替的面板。

DockPanel 面板

​ DockPanel 面板是更有趣的布局选项。它沿着一条外边缘来拉伸所包含的控件。理解该面板最简便的方法是,考虑一下位于许多 Windows 应用程序窗口顶部的工具栏。这些工具栏停靠到窗口顶部。与 StackPanel 面板类似,被停靠的元素选择它们布局的一个方面。例如,如果将一个按钮停靠在DockPanel 面板的顶部,该按钮会被拉伸至 DockPanel 面板的整个宽度,但根据内容和 MinHfeight 属性为其设置所需的高度。而如果将一个按钮停靠到容器左边,该技钮的高度将被拉伸以适应容器的高度,而其宽度可以根据需要自由增加。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <!--LastChildFill:最后一个子元素填充-->
    <DockPanel LastChildFill="True">
        <Button DockPanel.Dock="Top">上</Button>
        <Button DockPanel.Dock="Bottom">下</Button>
        <Button DockPanel.Dock="Left">左</Button>
        <Button DockPanel.Dock="Right">右</Button>
        <Button>中间</Button>
    </DockPanel>

</Window>

案例2:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <!--
    DockPanel.Dock:停靠方向
    LastChildFill:最后一个子元素填充
    HorizontalAlignment:水平对齐方式
    MinWidth:最小宽度
    -->
    <DockPanel LastChildFill="True">
        <Button DockPanel.Dock="Top">上-1</Button>
        <Button DockPanel.Dock="Top" HorizontalAlignment="Center" MinWidth="200">上-2</Button>
        <Button DockPanel.Dock="Top" HorizontalAlignment="Left" MinWidth="200">上-3</Button>
        <Button DockPanel.Dock="Bottom">下</Button>
        <Button DockPanel.Dock="Left">左</Button>
        <Button DockPanel.Dock="Right">右</Button>
        <Button>中间</Button>
    </DockPanel>

</Window>

嵌套布局容器

​ 很少单独使用 StackPanel、WrapPanel 和 DockPanel 面板。相反,它们通常用来设置一部分用户界面的布局。例如,可使用 DockPanel 面板在窗口的合适区域放置不同的 StackPanel 和WrapPane! 面板容器。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <!--
    LastChildFill:最后一个子元素填充
    DockPanel.Dock:停靠方向
    HorizontalAlignment:水平对齐方式
    Orientation:排列方向(水平/垂直)
    Margin:外边距
    Padding:内边距
    -->
    <DockPanel LastChildFill="True">
        <StackPanel DockPanel.Dock="Bottom" HorizontalAlignment="Right" Orientation="Horizontal">
            <Button Margin="10,10,2,10" Padding="3">OK</Button>
            <Button Margin="2,10,10,10" Padding="3">Cancel</Button>
        </StackPanel>
        <TextBox DockPanel.Dock="Top">这是一个文本框</TextBox>
    </DockPanel>

</Window>

提示:

​ 如果有一棵茂密的嵌套元素树,很可能看不到整个结构。Visual Studio 提供了一个方使的功能,用于显示一棵表示各个元素的树,并允许您通过逐步单击进入希望查看(或修改)的元素。
这一功能是指 Document Outline 窗口,可通过选择 View | Other Windows | Document Outline 菜单项来显示该窗口。

Grid 面板

​ Grid 面板是WPF 中功能最强大的布局容器。很多使用其他布局控件能完成的功能,用Grid面板也能实现。Grid 面板也是将窗口分割成(可使用其他面板进行管理的)更小区域的理想工具。实际上,由于 Grid 面板十分有用,因此在 Visual Studio 中为窗口添加新的XAML 文档时,会自动添加 Grid 标签作为顶级容器,并嵌套在 Window 根元素中。

​ Grid 面板将元素分隔到不可见的行列网格中。尽管可在一个单元格中放置多个元素(这时这些元素会相互重叠),但在每个单元格中只放置一个元素通常更合理。当然,在Grid 单元格中的元素本身也可能是另一个容器,该容器组织它所包含的一组控件。

提示:

​ 尽管Grid面板被设计成不可见的,但可将 Grid.ShowGridlines 属性设置汐 true,从而更清晰她观察Gird 面板。这一特性并不是真正试图美化窗口,反而是为了方便调试,设计该特性旨在帮助理解 Grid 面板如何将其自身分制成多个较小的区域。这一特性十分童要,因为通过该特性可准确控制 Grid 面板如何选择列宽和行高。

​ 需要两个步骤来创建基于 Crd 面板的布局。首先,选择希望使用的行和列的数厭。然后,为每个包含的元素指定恰当的行和列,从而在合适的位置放置元素。

​ Grid 面板通过使用对象填充 Grid.ColumnDefinitions和 Grid. Row Definiti ons 集合来创建网格和行。例如,如果确定需要两行和三列,可添加以下标签:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid ShowGridLines="True">

        <!--行-->
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <!--列-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
    </Grid>

</Window>

​ 为在单元格中放置各个元素,需要使用 Row 和Columm 附加属性。这两个属性的值都是从0开始的索引数。例如,以下标记演示了如何创建Grid面板,并使用按钮填充Grid 面板的部分单元格。

​ 此处存在例外情况。如果不指定 Grid Row 属性,Grid 面板会假定该属性的值为0。对于Crid. Column 属性也是如此。因此,在 Grid 面板的第一个单元格中放置元素时可不指定这两个属性。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid ShowGridLines="True">

        <!--行-->
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <!--列-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Button Grid.Row="0" Grid.Column="0">button1-1</Button>
        <Button Grid.Row="0" Grid.Column="1">button1-2</Button>
        <Button Grid.Row="1" Grid.Column="1">button2-2</Button>
        <Button Grid.Row="1" Grid.Column="2">button2-3</Button>
    </Grid>

</Window>

调整行和列

​ Grid 面板支持以下三种设置尺寸的方式:

  • 绝对设置尺寸方式: 使用设备无关单位准确地设置尺寸。这是最无用的策略,因为这种策略不够灵活,难以适应内容大小和容器大小的改变,而且难以处理本地化。

  • 自动设置尺寸方式: 每行和每列的尺寸刚好满足需要。这是最有用的尺寸设置方式。

  • 按比例设置尺寸方式。按比例将空间分割到一组行和列中。这是对所有行和列的标准设置。

​ 可通过将ColumnDefinition 对象的Width 属性或 RowDefinition 对象的 Height 属性设置为数值来确定尺寸设置方式。例如,下面的代码显示了如何设置100 设备无关单位的绝对宽度。

<ColumnDefinition Width="100"></ColumnDefinition>

为使用自动尺寸设置方式,可使用Auto值:

<ColumnDefinition Width="Auto"></ColumnDefinition>

最后,为了使用按比例尺寸设置方式,需要使用*号:

<ColumnDefinition Width="*"></ColumnDefinition>

​ 如果希望不均匀地分割剩余空间,可指定权重,权重必须放在星号之前。例如,如果有两行是按比例设置尺寸,并希望第一行的高度是第二行高度的一半,那么可以使用如下设置来分配剩余空间:

<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="2*"></RowDefinition>

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid ShowGridLines="True">

        <!--行-->
        <Grid.RowDefinitions>
            <RowDefinition  Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!--
        HorizontalAlignment:水平对齐方式
        Orientation:排列方向
        Margin:外边距
        -->
        <TextBox Margin="10" Grid.Row="0">这是一个文本框</TextBox>
        <StackPanel Grid.Row="1" HorizontalAlignment="Right" Orientation="Horizontal">
            <Button Margin="10,10,2,10">OK</Button>
            <Button Margin="2,10,10,10">Cancel</Button>
        </StackPanel>
    </Grid>

</Window>

跨越行和列

​ 您已经看到如何使用 Row 和 Calum 附加属性在单元格中放置元素。还可以使用另外两个附加属性使元素跨越多个单元格,这两个附加属性是 RowSpan和ColurmSpan。这两个属性使用元素将会占有的行数和列数进行设置。

​ 例如,下面的按钮将占据第一行中的第一个和第二个单元格的所有空间:

<Button Grid.Row="0" Grid.Column="0" Grid.RowSpan="2">跨行</Button>

​ 下面的代码通过跨越两列和两行,拉伸按钮使其占据所有4个单元格:

<Button Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2">跨行并跨列</Button>

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid UseLayoutRounding="True" >

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <TextBox Margin="10" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
            这是一个文本框
        </TextBox>
        <Button Margin="10,10,2,10" Padding="3" Grid.Row="1" Grid.Column="1">OK</Button>
        <Button Margin="2,10,10,10" Padding="3" Grid.Row="1" Grid.Column="2">Cancel</Button>
       
    </Grid>

</Window>

分割窗口

​ 每个 Windows 用户都见过分割条一能将窗口的一部分与另一部分分离的可拖动分割器,
例如,当使用Windows 资源管理器时,会看到一系列立件实(在左边)和一系列文件(在右边)、可拖动它们之间的分割条来确定每部分占据窗口的比例。

​ 理解如何使用 GridSplitter 类,从而得到所期望的效果需要一定的经验。下面列出几条指导原则:

  • GrdSpliter 对象必须放在Grid 单元格中。可与已经存在的内容一并放到单元格中,这时循要调整边距设置,使它们不相互重叠。更好的方法是预留一列或一行专门用于放置 Gridspliter对象,并将预留行或列的Heigh 或 Width 属性的值设置为 Auto。
  • Gridspliter 对象总是改变整行或整列的尺寸(而非改变单个单元格的尺寸)。为使Cridspliter对象的外观和行为保持一致,需要拉伸Gridsplitter 对象使其穿越整行或整列,而不是将其限制在单元格中。为此,可使用前面介绍过的RowSpan或ColumnSpan 属性。
  • 最初,GridSpliter 对象很小不易看见。为了使其更可用,需要为其设置最小尺寸。对于竖直分割条,需要将 VericalAlignment 属性设置为stretch(使分割条填满区域的整个高度),并将 Width 设置为固定值(如10个设备无关单位)。对于水平分割条,需要设置 HorizontalAlignmeat 属性来拉伸,并将 Height 属性设置为固定值。
  • Gridspliter 对齐方式还决定了分割条是水平的(用于改变行的尺寸)还是竖直的(用于改变列的尺寸)。对于水平分割条,需要将 VerticalAlignment 属性设置为Center(这也是默认值),以指明拖动分割条改变上面行和下面行的尺寸。对于竖直分割条,需要将 HorizontalAlignment 属性设置为 Center,以改变分割条两侧列的尺寸。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid UseLayoutRounding="True" >

        <!--行设置-->
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        
        <!--列设置-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition MinWidth="100"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition MinWidth="50"/>
        </Grid.ColumnDefinitions>

        <!--按钮-->
        <Button Grid.Row="0" Grid.Column="0" Margin="3">left-1</Button>
        <Button Grid.Row="0" Grid.Column="2" Margin="3">right-1</Button>
        <Button Grid.Row="1" Grid.Column="0" Margin="3">left-2</Button>
        <Button Grid.Row="1" Grid.Column="2" Margin="3">right-1</Button>

        <!--分割线-->
        <!--ShowsPreview:是否显示预览-->
        <GridSplitter Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Width="3" VerticalAlignment="Stretch" HorizontalAlignment="Center" ShowsPreview="False"></GridSplitter>
    </Grid>

</Window>

​ 上面的标记还包含了一处额外的细节。在声明GridSplitter 对象时,将 ShowsPreview 属性设置为 false .因此,当把分割线从一边拖到另一边时,会立即改变列的尺寸。但是如果将 ShowsPreview 属性设置为 true,当拖动分割条时就会看到一个灰色的阴影跟随鼠标指针,用于显示将在何处进行分割。并且直到释放了鼠标键之后列的尺寸才改变。如果GridSplitter 对象获得了焦点,可可以使用箭头改变相应的尺寸。

​ 如果希望分割条以更大的幅度(如每次10个单位)进行移动,可调整DragIncrement 属性。

提示:

​ 可以改变 GridSplitter 对象的填充方式,使其不只是具有阴影的灰色矩形。技巧是使用Background 属性应用填充,该属性接受简单的颜色或更复杂的画刷。

​ Grid 面板通常包含多个 GridSpliter 对象。然而,可以在一个 Grid 面板中嵌套另一个Grid面板;而且,如果确实在 Grid 面板中嵌套了Grid 面板,那么每个Grid 面板可以有自己的GridSplitter对象。这样就可以创建被分割成两部分(如左边窗格和右边窗格)的窗口,然后将这些区域(如右边的窗格)进一步分成更多的部分(例如,可调整大小的上下两部分)。

案例2:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid UseLayoutRounding="True" >

        
        <!--列设置:3列-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition MinWidth="100"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition MinWidth="50"/>
        </Grid.ColumnDefinitions>

        <!--左侧Grid-->
        <Grid Grid.Column="0">
            
            <!--行设置:2行-->
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <Button Margin="3" Grid.Row="0">Top Left</Button>
            <Button Margin="3" Grid.Row="1">Bottom Left</Button>
            
        </Grid>
        
        <!--分割线-->
        <!--ShowsPreview:是否显示预览-->
        <GridSplitter Grid.Column="1" Grid.RowSpan="2" Width="3" VerticalAlignment="Stretch" HorizontalAlignment="Center" ShowsPreview="False"></GridSplitter>

        <!--右侧Grid-->
        <Grid Grid.Column="2">

            <!--行设置:3行-->
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <Button Margin="3" Grid.Row="0">Top Right</Button>
            <Button Margin="3" Grid.Row="2">Bottom Right</Button>

            <GridSplitter Grid.Row="1" Height="3" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="False"></GridSplitter>

        </Grid>
    </Grid>

</Window>

共享尺寸组

​ 正如在前面看到的,Grid 面板包含一个行列集合,可以明确地按比例确定行和列的尺寸,或根据其子元素的尺寸确定行和列的尺寸。还有另一种确定一行或一列尺寸的方法—与其他行或列的尺寸相匹配。这是通过称为“共享尺寸组”(Shared size groups)的特性实现的。

​ 共享尺寸组的目标是保持用户界面独立部分的一致性。例如,可能希望改变一列的尺寸以适应其内容,并改变另一列的尺寸使其与前面一列改变后的尺寸相匹配。然而,共享尺寸组的真正优点是使独立的Grid 控件具有相同的比例。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid Grid.IsSharedSizeScope="True" Margin="3">
        
        <!--设置行:3行-->
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        
        <!--上方Grid-->
        <Grid Margin="3" Background="LightYellow" ShowGridLines="True">
            
            <!--设置列:3列-->
            <!--SharedSizeGroup:共享尺寸组-->
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" SharedSizeGroup="TextLabel"/>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <Label Margin="5" Grid.Column="0">这是一个长的文本</Label>
            <Label Margin="5" Grid.Column="1">其他文本</Label>
            <TextBox Margin="5" Grid.Column="2">这是一个文本框</TextBox>
        </Grid>
        
        <!--中间标签-->
        <Label Grid.Row="1">这是中间的标签</Label>

        <!--下方Grid-->
        <Grid Grid.Row="2" Margin="3" Background="LightCoral" ShowGridLines="True">

            <!--设置列:2列-->
            <!--SharedSizeGroup:共享尺寸组-->
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" SharedSizeGroup="TextLabel"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <Label Margin="5" Grid.Column="0">短的</Label>
            <TextBox Margin="5" Grid.Column="1">这是一个文本框</TextBox>
           
        </Grid>
    </Grid>

</Window>

UnitformGrid 面板

​ 有一种网格不遵循前面讨论的所有原则--UniformGrid 面板。与Grid 面板不同,UaifommCrid 面板不需要(甚至不支持)预先定义的列和行。相反,通过简单地设置Rows 和Colurs 属性来设置其尺寸。每个单元格始终具有相同的大小,因为可用的空间被均分。最后,元素根据定义的顺序被放置到适当的单元格中。UniformGrid 面板中没有 Row和 Column 附加属性,也没有空白单元格。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <!--
    Rows:行数
    Columns:列数
    -->
    <UniformGrid Rows="2" Columns="2">
        <Button>Top Left</Button>
        <Button>Top Right</Button>
        <Button>Buttom Left</Button>
        <Button>Buttom Right</Button>
    </UniformGrid>

</Window>

Canvas 面板

​ Canvas 面板允许使用精确的坐标放潭元案,如果设计数据驱动的富窗体和标准对话框,这并非好的选择;但如果需要构建其他一些不同的内容(例如,为图形工具创建绘图表面),Canvas 面板可能是个有用的工具。Canvas面板还是最轻量级的布局容器。这是因为 Canvas面板没有包含任何复杂的布局逻辑,用以改变其子元素的首选尺寸。Canvas 面板只是在指定的位置放置其子元素,并且其子元素具有所希望的精确尺寸。

​ 为在 Canvas 面板中定位元素,需要设置 Canvas.Left 和 Canvas,Top 附加属性。Canvas.Left 属性设置元素左边和 Canvas 面板左边之间的单位数,Canvas.Top 属性设置子元素顶边和Canvas 面板顶边之间的单位数。同样,这些数值也是以设备无关单位设置的,当将系统DPI设置为 96 dpi 时,设备无关单位恰好等于通常的像素。

注意:

​ 另外,可使用 Canvas.Right 属性而不是 Canvas.Lef 属性来确定元素和 Canvas 面板右边缘间的距离;可使用 Canvas.Bottom 属性而不是 Canvas.Top 属性来确定元素和 Canvas 面板底部边缘的距离。不能同时使用 Canvas.Right 和 Canvas. Left 属性,也不能同时使用 Canvas.Top 和Canvas.Bottom 属性。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <!--
    Canvas.Left: 左边距
    Canvas.Top: 上边距
    Width: 宽度
    Height: 高度
    -->
    <Canvas>
        <Button Canvas.Left="10" Canvas.Top="10">(10,10)</Button>
        <Button Canvas.Left="120" Canvas.Top="30">(120,30)</Button>
        <Button Canvas.Left="60" Canvas.Top="80" Width="50" Height="50">(60,80)</Button>
        <Button Canvas.Left="70" Canvas.Top="120" Width="100" Height="50">(70,120)</Button>
    </Canvas>

</Window>

Z 轴坐标

​ 如果 Canvas 面板中有多个互相重叠的元素,可通过设置Canvas ZIndex 附加属性来控制它们的层叠方式。

​ 添加的所有元素通常都具有相同的 Zlndex 值—0。如果元素具有相同的 ZIndex 值,就按它们在Canvas.Children 集合中的顺序进行显示,这个顺序依赖于元素在 XAMIL 标记中定义的顺序。

lnkCanvas 元素

​ WPF 还提供了 InkCanvas 元素,它与 Canvas 面板在某些方面是类似的(而在其他方面却完全不同)。和 Canvas 面板一样,InkCanvas 元素定义了4个附加属性(Top、Left、Bottom 和 Righn),可将这4个附加属性应用于子元素,以根据坐标进行定位。然而,基本的内容区别很大—零际上,InkCanvas 类不是派生自 Canvas类,甚至也不是派生自 Pane! 基类,而是直接派生自FrameworkElement类。

​ InkCanvas元素的主要目的是用于接收手写笔输入。手写笔是一种在平板 PC 中使用的类似于钢笔的输入设备,然而,InkCanvas 元素同时也可使用鼠标进行工作,就像使用手写笔一样。因此,用户可使用鼠标在 InkCanvas 元素上绘制线条,或者选择以及操作 InkCanvas 中的元素。

​ InkCanvas 元素实际上包含两个子内容集合。一个是为人熟知的Children集合,它保存任意元素,就像 Canvas 面板一样。每个子元素可根据Top、Lef、Bottom 和 Right属性进行定位。另一个是Strokes 集合,它保存 System.Windows.Ink.Stroke 对象,该对象表示用户在 InkCanvas。元素上绘制的图形输入。用户绘制的每条直线或曲线都变成独立的 Stroke 对象。得益于这两个集合,可使用 InkCanvas 让用户使用存储在 Strokes 集合中的笔画(stroke)为保存在Children 集合中的内容添加注释。

​ 根据为 EditingMode 属性设置的值,可以采用截然不同的方式使用 InkCanvas 元素。下表列出了所有选项:

名称 说明
Ink InkCanvas 元素允许用户绘制批注,这是默认模式。当用户用鼠标或手写笔绘图时,会绘制笔画.
GestureOnly InkCanvas 元素不允许用户绘制笔画批注,但会关注预先定义的特定姿势(例如在某个方向拖动手写笔或涂画内容)•能识别的姿势的完整列表由 System.Windows. Ink.Application Gesture 枚举给出.
InkAndGesture InkCanvas元素允许用户绘制笔画批注,也可以识别预先定义的姿势.
EraseByStroke 当单击笔画时,InkCanvas 元素会擦除笔画。如果用户使用手写笔,可使用手写笔的底端切换到该模式(可使用只读的 ActiveEditingMode 属性确定当前编辑模式,也可通过改变EditingModelinverted 属性来改变手写笔的底端使用的工作模式)
EraseByPoint 当单击笔画时,InkCanvas 元素会擦除笔画中被单击的部分(笔画上的一个点)
Select InkCanvas 面板允许用户选择保存在Children集合中的元素。要选择一个元素,用户必须单击该元素或拖动“套索”选择该元素。一旦选择一个元素,就可以移动该元素、改变其尺寸或将其删除
None InkCanvas 元素忽略鼠标和手写笔输入。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <InkCanvas Name="inkCanvas" Background="LightYellow" EditingMode="Ink">
            
    </InkCanvas>

</Window>

​ inkCanvas 元素会引发多种事件,当编辑模式改变时会引发 ActiveEditingModeCbanged 事件,在 GestureOnly 或 InkAndGesture 模式下删除姿势时会引发Gesture 事件,绘制完笔画时会引发SrokeColleoted 事件,擦除笔画时会引发 StokeErasing 事件和StrokeErased 事件,在 Select 模式下选择元素或改变元素时会引发 SelectionChanging 事件、SelectionChanged 事件、SelectionMoving 事件、SelectionMoved 事件、SelectionResizing 事件和 SelectionResized 事件。其中,名称以 “ing”结尾的事件表示动作将要发生,但可以通过设置 EventATgs 对象的Cancel属性取消事件。

​ 在Select 模式下,InkCanvas 元素可为拖动以及操作内容提供功能强大的设计界面。下图显示了 InkCanvas 元素中的一个按钮控件,左图中显示的是该按钮被选中的情况,而右图中显示的是选中该按钮后,改变其位置和尺寸的情况。

案例2:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <InkCanvas Name="inkCanvas" Background="LightYellow" EditingMode="Select">

        <Button InkCanvas.Top="20" InkCanvas.Left="50">hello</Button>
        
    </InkCanvas>

</Window>

布局示例

列设置

​ 布局容器(如Grid面板)使得窗口创建整个布局结构变得非常容易。例如下图中显示的窗口及设置。该窗口在一个表格结构中排列各个组件--标签、文本框以及按钮。

案例1:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid Margin="3,3,10,3" ShowGridLines="False">
        
        <!--行设置-->
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>

        <!--列设置-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="auto"/>
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Margin="3" VerticalAlignment="Center">Home:</Label>
        <TextBox Grid.Row="0" Grid.Column="1" Margin="3" Height="auto" VerticalAlignment="Center">c:\</TextBox>
        <Button Grid.Row="0" Grid.Column="2" Margin="3" Padding="2">Browser</Button>

        <Label Grid.Row="1" Grid.Column="0" Margin="3" VerticalAlignment="Center">Network:</Label>
        <TextBox Grid.Row="1" Grid.Column="1" Margin="3" Height="auto" VerticalAlignment="Center">e:\work</TextBox>
        <Button Grid.Row="1" Grid.Column="2" Margin="3" Padding="2">Browser</Button>

        <Label Grid.Row="2" Grid.Column="0" Margin="3" VerticalAlignment="Center">Web:</Label>
        <TextBox Grid.Row="2" Grid.Column="1" Margin="3" Height="auto" VerticalAlignment="Center">c:\</TextBox>
        <Button Grid.Row="2" Grid.Column="2" Margin="3" Padding="2">Browser</Button>


        <Label Grid.Row="3" Grid.Column="0" Margin="3" VerticalAlignment="Center">Secondary:</Label>
        <TextBox Grid.Row="3" Grid.Column="1" Margin="3" Height="auto" VerticalAlignment="Center">c:\</TextBox>
        <Button Grid.Row="3" Grid.Column="2" Margin="3" Padding="2">Browser</Button>
    </Grid>

</Window>

动态内容

​ 在以下示例中,用户界面可选择短文本和长文本。当使用长文本时,包含文本的按钮会自动改变其尺寸,而其它内容也会相应的调整位置。并且因为改变了尺寸的按钮共享同一布局容器,所以整个用户界面都会改变尺寸。最终的结果是所有按钮保持一致的尺寸---最大按钮的尺寸。

案例1:

xaml代码:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">

    <Grid>
        
        <!--行设置-->
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>

        <!--列设置-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <StackPanel Grid.Row="0" Grid.Column="0">
            <Button x:Name="cmdPrev" Margin="10,10,10,3">Prev</Button>
            <Button x:Name="cmdNext" Margin="10,3,10,3">Next</Button>
            <CheckBox x:Name="chkLong" Margin="10,10,10,10" Unchecked="chkLong_Unchecked" Checked="chkLong_Checked" >Show Long Text</CheckBox>
        </StackPanel>

        <TextBox Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Margin="0,10,10,10" TextWrapping="WrapWithOverflow">
            Computer viruses are artificially created programs that are destructive, infectious, and latent, causing damage to computer information or systems. It does not exist independently, but is hidden within other executable programs. After being infected with a virus in a computer, it can affect the running speed of the machine, and in severe cases, it can cause system crashes and damage; Therefore, viruses cause significant losses to users, and typically, these destructive programs are referred to as computer viruses
        </TextBox>

        <Button Grid.Row="1" Grid.Column="0" Name="cmdClose" Margin="10,3,10,10">Close</Button>
    </Grid>
</Window>

c#代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using 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 WpfApp1
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
       
        private void chkLong_Checked(object sender, RoutedEventArgs e)
        {
            this.cmdPrev.Content = "<--Go to the previous Window";
            this.cmdNext.Content = "Go to Next Window-->";
        }

        private void chkLong_Unchecked(object sender, RoutedEventArgs e)
        {
            this.cmdPrev.Content = "Prev";
            this.cmdNext.Content = "Next";
        }
    }
}

组合式用户界面

​ 许多布局容器(如 StackPanel 面板、DockPanc!面板以及 WrapPanel 面板)可以采用灵活多变的柔性方式非常得体地将内容安排到可用窗口空间中。该方法的优点是,它允许创建真正的组合式界面。换句话说,可在用户界面中希望显示的恰当部分插入不同的面板,而保留用户界面的其他部分。整个应用程序本身可以相应地改变界面,这与Web 门户站点有类似之处。

​ 下图展示了一个组合式用户界面,在一个WrapPanel 面板中放置几个独立的面板。用户可以通过窗口顶部的复选框,选择显示这些面板中的哪些面板。

视图代码:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="700">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <StackPanel Grid.Row="0"  Margin="3" Background="LightYellow" Orientation="Horizontal">
            <CheckBox Name="chb1" IsChecked="True" Click="CheckBox_Click">Panel1</CheckBox>
            <CheckBox Name="chb2" IsChecked="True" Click="CheckBox_Click">Panel2</CheckBox>
            <CheckBox Name="chb3" IsChecked="True" Click="CheckBox_Click">Panel3</CheckBox>
            <CheckBox Name="chb4" IsChecked="True" Click="CheckBox_Click">Panel4</CheckBox>
        </StackPanel>
        
        <WrapPanel Name="container" Grid.Row="1"  Background="LightBlue">

            <Grid MinWidth="200" Name="pnl1">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>

                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Button Grid.Row="0" Grid.Column="0" MinWidth="50" MinHeight="50" Margin="10">1</Button>
                <Button Grid.Row="0" Grid.Column="1" MinWidth="50" MinHeight="50" Margin="10">2</Button>
                <Button Grid.Row="1" Grid.Column="0" MinWidth="50" MinHeight="50" Margin="10">3</Button>
                <Button Grid.Row="1" Grid.Column="1" MinWidth="50" MinHeight="50" Margin="10">4</Button>
            </Grid>
            
            <TabControl MinWidth="400" Name="pnl2">
                <TabItem Header="Page1" VerticalAlignment="Center" HorizontalAlignment="Center">
                    <Label>内容1</Label>
                </TabItem>
                <TabItem Header="Page2">
                    <Label>内容2</Label>
                </TabItem>
            </TabControl>

            <StackPanel Width="200" Name="pnl3">
                <Label>计算机病毒是人为制造的,有破坏性,又有传染性和潜伏性的,对计算机信息或系统起破坏作用的程序。</Label>
                <Button Width="80" Margin="5,40,5,5">Ok</Button>
                <Button Width="80" Margin="5,5,5,5">Cancel</Button>
            </StackPanel>

            <Grid MinWidth="200" Name="pnl4">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>

                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Button Grid.Row="0" Grid.Column="0" MinWidth="50" MinHeight="50" Margin="10">1</Button>
                <Button Grid.Row="0" Grid.Column="1" MinWidth="50" MinHeight="50" Margin="10">2</Button>
                <Button Grid.Row="1" Grid.Column="0" MinWidth="50" MinHeight="50" Margin="10">3</Button>
                <Button Grid.Row="1" Grid.Column="1" MinWidth="50" MinHeight="50" Margin="10">4</Button>
            </Grid>

        </WrapPanel>
    </Grid>
   
</Window>

后台代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using 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 WpfApp1
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void CheckBox_Click(object sender, RoutedEventArgs e)
        {
            CheckBox chb = sender as CheckBox;
            string index = chb.Name.Substring(3,1);

            // 在适当的位置调用FindName方法
            FrameworkElement rootElement = this.FindName("container") as FrameworkElement;
            FrameworkElement control = LogicalTreeHelper.FindLogicalNode(rootElement, "pnl"+index) as FrameworkElement;

            if (chb.IsChecked==true)
            {
               control.Visibility = Visibility.Visible;               
            }
            else
            {
                control.Visibility = Visibility.Collapsed;                
            }
        }
    }
}

​ Visibility 属性是 UIElement 基类的一部分,因此放置于WPF窗口中的任何内容都支持该属性。该属性可使用三个值,它们来自 System.Windows.Visibility 枚举,如下表所示:

说明
Visibility 元素在窗口中正常显示
Collapsed 元素不显示,也不占用任何空间。
Hidden 元素不显示,但仍为其保留空间。

本章小结

​ 本章详细介绍了 WPF布局模型,并讨论了如何以堆栈、网格以及其他排列方式放置元素可使用嵌套的布局容器组合创建更复杂的布局,可结合使用 GridSplitter 对象创建可变的分割窗口。本章一直非常关注这一巨大变化的原因—WPF 布局模型在保持、加强以及本地化用户界面方面所具有的优点。

​ 布局内容远不止这些,接下来的几章还将列举更多使用布局容器组织元素分组的示例,还将学习允许在窗口中排列内容的几个附加功能:

  • 特殊容器
    :可以使用 ScrdlIViewer、Tabltem 以及 Expander 控件滚动内容、将内容放到单独的选项卡中以及折叠内容。与布局面板不同,这些容器只能包含单一内容。不过,可以很容易地组合使用这些容器和布局面板,以便准确实现所需的效果。第6 章将尝试使用这些容器。
  • Viewbox
    :需要一种方法来改变图形内容(如图像和矢量图形)的尺寸吗?Viewbox 是另一种特殊容器,可帮助您解决这一问题,而且 Viewbox 控件内置了缩放功能。
  • 文本布局
    :WPF 新增了用于确定大块格式化文本布局的新工具。可使用浮动图形和列表,并且可以使用分页、分列以及更复杂、更智能的換行功能来获得非常完美的结果。

课后作业

标签: none

添加新评论