【WPF学习】第八章 Grid面板

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

  Grid面板将元素分割到不可见的行列网格中。尽管可在一个单元格中放置多个元素,但在每个单元格中放置一个元素通常更合理。当然,在Grid单元格中的元素本身也可能是另一个容器,该容器组织它所包含的一组控件。

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

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

<Window x:Class="GridLayout.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
    </Grid>
</Window>

  正如本例所演示的,在RowDefinition或ColumnDefinition元素中不比提供任何信息。如果保持它们为空,Grid面板将在所有行和列之间平均分配空间。

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

<Window x:Class="GridLayout.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Button Margin="5" Grid.Row="0" Grid.Column="0">1,1</Button>
        <Button Margin="5" Grid.Row="0" Grid.Column="1">1,2</Button>
        <Button Margin="5" Grid.Row="0" Grid.Column="2">1,3</Button>
        <Button Margin="5" Grid.Row="1" Grid.Column="0">2,1</Button>
        <Button Margin="5" Grid.Row="1" Grid.Column="1">2,2</Button>
        <Button Margin="5" Grid.Row="1" Grid.Column="2">2,3</Button>
    </Grid>
</Window>

  效果图如下所示:

  每个元素必须被明确地放在对应的单元格中。可在单元格中放置多个元素,或让单元格保持为空。此处存在例外情况。如果不指定Grid.Row属性,面板会假定该属性的值为0.对于Grid.Column属性也是如此。因此,在Grid面板的第一个单元格中放置元素时可不指定这两个属性。

一、调整行和列

  如果Grid面板只是按比例分配尺寸的行和列的集合,它也就没什么用处了。幸运的是,情况并非如此。为了充分发挥Grid面板的潜能,可更改每一行和每一列的尺寸设置方式。

  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>

  使用这种设置方式,实现简答的示例如下所示:

<Window x:Class="GridLayout.MessageWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MessageWindow" Height="300" Width="300">
    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBox Margin="10" Grid.Row="0" Grid.Column="0">This is a message!</TextBox>
        <StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Margin="10,10,2,10" Padding="3">OK</Button>
            <Button Margin="2,10,10,10" Padding="3">Cancle</Button>
        </StackPanel>
    </Grid>
</Window>

  效果如下图所示:

 

 

 二、布局舍入

  WPF使用分辨率无关的测量系统。尽管该测量系统为使用各种不同的硬件提供了灵活性,但有时也会引入一些问题。其中一个问题是元素可能被对齐到子像素(subpixel)边界——换句话说,使用没有和物理像素准确对齐的小书坐标定位元素。可通过为相邻的布局容器提供非整数尺寸强制发生这个问题。但是当不希望发生这个问题时,在某些情况下该问题也可能会出现,例如当创建按比例设置尺寸的Grid面板时就可能会发生该问题。

  例如,假设使用一个包含两列且具有200像素的Grid面板。如果将该面板均匀分成两个按比例设置尺寸的列,那么意味每列为100像素宽。但是如果这个Grid面板的宽度为175像素,就不能很清晰地分割成两列,并且每列为87.5像素。这意味着第二列会和原始的像素边界稍有些错误。这通常不是问题,但是如果该列包含一个形状元素、一个边框或一副图像,那么该内容的显示可能是模糊的,因为WPF会使用反锯齿功能“混合”原本清晰的像素边界边框。

  如果这个问题影响到布局,可以采用一种方法很方面地解决该问题。只需要将布局容器的UseLayouyRounding属性设置为true:

  <Grid UseLayoutRounding="True">

  现在,WPF会确保布局容器中的所有内容对齐到最近的像素边界,从而消除了所有模糊问题。

三、跨越行和列

  使用两个附加属性使元素跨越多个单元格,这两个附加属性是RowSpan和ColumnSpan。这两个属性使用元素将会占有的行数和列数进行设置。

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

<Button Margin="5" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2">1,1</Button>

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

<Button Margin="5" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2">1,1</Button>

  通过跨越行和列可得到更有趣得效果,当需要在由分割器或更长的呢rio难怪区域分开的表格结构中放置元素时,这是非常方便的。

  使用行和列跨越特征,下面是简单的示例:

<Window x:Class="GridLayout.MessageSpanWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MessageSpanWindow" Height="300" Width="300">
    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBox Margin="10" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">This is a test</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">Cancle</Button>
    </Grid>
</Window>

效果如下所示:

 

 

 四、分割窗口

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

  在WPF中,分隔条由GridSplitter类表示,它是Grid面板的功能之一。通过为Grid面板添加GridSplitter对象,用户就可以改变行和列的尺寸。

  大多数开发人员认为WPF中的GridSplitter类不是最直观的。理解如何使用GridSplitter类,从而得到所期望的效果需要一定的经验。下面列出几条指导原则:

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

  根据上面的规则,下面给出实例,如下所示:

<Window x:Class="GridLayout.GridSplitterWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="GridSplitterWindow" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition MinWidth="100"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition MinWidth="50"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Button Grid.Row="0" Grid.Column="0" Margin="3">Left</Button>
        <Button Grid.Row="0" Grid.Column="2" Margin="3">Right</Button>
        <Button Grid.Row="1" Grid.Column="0" Margin="3">Left</Button>
        <Button Grid.Row="1" Grid.Column="2" Margin="3">Right</Button>
        <GridSplitter Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" 
                      Width="3" VerticalAlignment="Stretch" HorizontalAlignment="Center" 
                      ShowsPreview="False"></GridSplitter>
    </Grid>
</Window>

  最终效果如下所示:

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

  ShowPreview不是唯一可设置的GridSplitter属性。如果希望分割条以更大的幅度(如每次10个单位)进行移动,可调整DragIncrement属性。如果希望控制列的最大尺寸和最小尺寸,只需要在ColumnDefinitions部分设置合适的属性。

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

<Window x:Class="GridLayout.DoubleGridSplitterWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DoubleGridSplitterWindow" Height="300" Width="300">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition ></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition ></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid Grid.Column="0" VerticalAlignment="Stretch">
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Button Margin="3" Grid.Row="0">Top Left</Button>
            <Button Margin="3" Grid.Row="1">Bottom Left</Button>
        </Grid>
        <GridSplitter Grid.Column="1" Width="3" HorizontalAlignment="Center" VerticalAlignment="Stretch"
                      ShowsPreview="False"></GridSplitter>
        <Grid Grid.Column="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Button Grid.Row="0" Margin="3">Top Right</Button>
            <Button Grid.Row="2" Margin="3">Bottom Right</Button>
            <GridSplitter Grid.Row="1" Height="3" VerticalAlignment="Center" HorizontalAlignment="Stretch" 
                          ShowsPreview="False"></GridSplitter>
        </Grid>
    </Grid>
</Window>

  效果图如下所示:

 

 五、共享尺寸组

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

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

  例如如下图所示,其中窗口中有两个Grid面板,上面的面板有三列,下面的面板只有两列。在实际中,可能两个面板第一列设置的列宽是Auto,此时,上面的列内容较多时,会自动扩大,而第二个grid的第一列内容偏少时,第二列的列宽会偏小,这样导致上下两个Grid面板的第一列显示不对其。如果想实现两个Grid的某一列列宽一致,可以设置Grid面板实现共享尺寸组功能。

 

 具体实现如下面代码所示:

<Window x:Class="GridLayout.SharedSizeGroup"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SharedSizeGroup" Height="300" Width="300">
    <!--设置Grid面板启动共享尺寸组-->
    <Grid Grid.IsSharedSizeScope="True" Margin="3">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" Margin="3" Background="LightYellow" ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <!--设置共享尺寸组名,相同的组名可以实现共享-->
                <ColumnDefinition Width="Auto" SharedSizeGroup="TextLabel"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Margin="5">A very long bit of text</Label>
            <Label Grid.Column="1" Margin="5">More test</Label>
            <TextBox Grid.Column="2" Margin="5">A Text Box</TextBox>
        </Grid>
        <Label Grid.Row="1">Some test in two grid</Label>
        <Grid Grid.Row="2" Margin="3" Background="LightYellow" ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="TextLabel"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Margin="5">Short</Label>
            <TextBox Grid.Column="1" Margin="5">A text box</TextBox>
        </Grid>
    </Grid>
</Window>

六、UniformGrid面板

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

  下面列举一个示例,该例使用4个按钮填充UniformGrid面板:

<Window x:Class="GridLayout.UniformGridWindows"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="UniformGridWindows" Height="300" Width="300">
    <UniformGrid Rows="2" Columns="2">
        <Button>Top Left</Button>
        <Button>Top Right</Button>
        <Button>Bottom Left</Button>
        <Button>Bottom Right</Button>
    </UniformGrid>
</Window>

  最终效果如下图所示:

 

   与Grid面板相比,UniformGrid面板很少使用。Grid面板是用于创建简单乃至复杂窗口布局的通用工具。UniformGrid面板是一种更特殊的布局容器,主要用于在刻板的网络中快速地布局元素(例如,为特定有限构建播放面板)。WPF开发人员可能永远不会使用UniformGrid面板。

posted @ 2020-01-12 17:02  Peter.Luo  阅读(2376)  评论(0编辑  收藏  举报