zoukankan      html  css  js  c++  java
  • 浅谈 WPF布局

    我们首先来了解一下图形化用户界面(Graphic User Interface)也就是我们常常听到的GUI。举个简单的例子,同样是数据,我们可以用控制台程序加格式控制符等输出,但是这些都不如GUI来的友好和方便。

    WPF相对于其它只能使用编程语言进行UI设计,具有专门用于UI设计的XAML,并且能够确保界面布局能恰倒好处的适应不同的窗口尺寸。

    我们来查看Window和Page的源码,发现Window的间接基类ContentControl和Page类都使用了一个object类型的Content属性。所以它只能包含一个元素,但是我们想放置多个元素怎么办呢?这个时候我们就需要使用布局容器。

    为什么布局容器又可以添加多个元素呢?所有的WPF布局容器都派生自System.Windows.Controls.Panel这个抽象类。我们从类层次结构图中就不难发现它所包含的一系列布局容器。我们再来看看这个Panel类的源代码,就会发现该类有一个ContentProperty("Children")特性,所以我们会在该类中找到一个UIElementCollection类型(该类实现了IList接口)的Children属性。只要是继承自UIElement的类,都可以添加到布局容器中。

    下面让我们来看看其中的一些布局容器以及与之相关联的控件属性。

    StackPanel

    它是最简单的布局容器,允许子元素在单行或者单列以堆栈的形式放置。

        <StackPanel Orientation="Vertical" Background="LightCyan">
            <Label Background="LemonChiffon">A Button Stack</Label>
            <Button>Button A</Button>
            <Button>Button B</Button>
            <Button>Button C</Button>
        </StackPanel>

    我们运行会发现,StackPanel容器会充满整个窗口,因为它的HorizontalAlignment、VerticalAlignment属性值都是Stretch。

    子元素是按照自上而下的方式排列的,因为我们设置了Orientation属性(默认值就是Vertical,所以这里我们可以不设置该属性);如果设置成Horizontal,就会从左到右排列。在默认情况下,每个子元素的高度都适合与它们自己内容的高度。如果是Horizontal,每个子元素的宽度都适合它们自己内容的长度。

    我们拉伸运行的WPF程序,会发现StackPanel始终会充满整个窗口,子元素也会根据设置做相应的变化。

    我们可以设置子元素的一些属性配合布局容器的属性,来决定布局。常见的属性有:HorizontalAlignment、VerticalAlignment、Margin、MinWidth、MinHeight、MaxWidth、MaxHeight、Height、Width。

    <Window x:Class="WPFDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Width="200" Height="350">
          
        <StackPanel Background="LightCyan">
            <Label Background="LemonChiffon">A Button Stack</Label>
            <Button HorizontalAlignment="Left">Button A</Button>
            <Button HorizontalAlignment="Center">Button B</Button>
            <Button HorizontalAlignment="Right">Button C</Button>
            <Button Margin="5" Height="40" Width="100" HorizontalAlignment="Right">Button D</Button>
            <Button MinWidth="320">Button E</Button>
        </StackPanel>
        
    </Window>

    运行这个程序,我们就会发现A、B、C三个Button分别位于水平方向上的不同位置;Button D距离四周的元素有5个单位的距离,并且指定了该按钮的高度和宽度;Button E指定了MinWidth,远大于Window的200,所以刚运行的时候左边被截断,需要我们通过拉伸才可完全看见。

    Grid

    顾名思义,Grid会以网格的形式对内容元素们进行布局。它具有如下的特点:

     · 可以通过RowDefinitions和ColumnDefinitions属性,它们分别是RowDefinition和ColumnDefinition集合。定义任意多的行和列。

     · 行的高度,列的宽度可以使用绝对数值(double数值加单位后缀px、in、cm、pt,不过在设置的时候最好不要加单位),比例值(double数值后加一个*号),自动值(字符串Auto)来灵活设置。

     · 内部元素可以使用Grid的附加属性Grid.Row、Grid.Column、Grid.RowSpan、Grid.ColumnSpan设置自己所在的行、列、纵向跨几行、横向跨几列。

     · 可以设置子元素的对齐方向。

        <Grid VerticalAlignment="Top" HorizontalAlignment="Left" ShowGridLines="True" Width="250" Height="100">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*" />
                <RowDefinition Height="1.5*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
    
            <TextBlock FontSize="20" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="0">2013 Products Shipped</TextBlock>
            <TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center">Quarter 1</TextBlock>
            <TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center">Quarter 2</TextBlock>
            <TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center">Quarter 3</TextBlock>
            <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center">50000</TextBlock>
            <TextBlock Grid.Row="2" Grid.Column="1" VerticalAlignment="Center">100000</TextBlock>
            <TextBlock Grid.Row="2" Grid.Column="2" VerticalAlignment="Center">150000</TextBlock>
            <TextBlock FontSize="16" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="3">Total Units: 300000</TextBlock>
        </Grid>

    在这个示例中,我们还设置了ShowGridLines属性,它会生成虚线的边框。我们可以修改该边框,具体可以查看陈希章老师的这篇文章:为WPF和Silverlight的Grid添加边框。

    我们还可以使用UseLayoutRounding属性来控制布局舍入。

    我们还可以使用GridSplitter来拖动分割窗口。

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="200"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="200"></ColumnDefinition>
            </Grid.ColumnDefinitions>
    
            <Button>1</Button>
            <Button Grid.Column="2">2</Button>
            <Button Grid.Row="2">3</Button>
            <Button Grid.Column="2" Grid.Row="2">4</Button>
    
            <GridSplitter Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" Width="2" VerticalAlignment="Stretch" HorizontalAlignment="Center" ShowsPreview="True"></GridSplitter>
    
        </Grid>

    这是一个左右拖动的分割窗口,所以我们把GridSplitter竖立放置铺满(VerticalAlignment="Stretch"),并跨行(Grid.RowSpan="3"),还要设置它的宽度Width(这样才可见);最后还要设置列的宽度为自动值Auto。我们还看到ShowsPreview属性为True,这个是当我们拖动分割条时,会有一个会射的阴影跟随鼠标运行,以显示预览。同理,我可以设置上下拖动的分割线,这里就不过多叙述了。

    还有一个比较有意思的是SharedSizeGroup特性。

        <Grid Margin="3">
            <StackPanel>
                <StackPanel Grid.IsSharedSizeScope="True">
                    <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 text</Label>
                        <TextBox Grid.Column="2" Margin="5">A text box</TextBox>
                    </Grid>
                    <Label Grid.Row="1" >Some text in between the two grids...</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>
                </StackPanel>
            </StackPanel>
        </Grid>

    我们可以设置需要共享尺寸的列ColumnDefinition或者行RowDefinition的SharedSizeGroup属性,把他们都设置成同一个值;然后再把Grid的IsSharedSizeScope附加属性设置到父容器上,并设置为True。(本例嵌套了2个StackPanel就是为了说明IsSharedSizeScope的设置方式,其实完全没有必要嵌套2个StackPanel,直接就可以设置在Grid上)

    还有一个更简单的网格布局容器,UniformGrid,它可以直接设置Rows、Columns属性,设置行列数;它没有Rows、Columns这些附加属性,是按照子元素的添加的顺序由左到右,由上到下排列的。

    Canvas

    直译就是画布的意思。它通常用于一些设计基本不会再有改动的小型布局。

    Canvas布局容器是最轻量级的布局容器,它没有包含负责的布局逻辑,以改变其子元素的首选尺寸。

    我们可以设置Canvas.Left、Canvas.Top(或者Canvas.Right、Canvas.Buttom)附加属性来定位子元素的位置。

        <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" Canvas.ZIndex="1">(60,80)</Button>
            <Button Canvas.Left="70" Canvas.Top="120" Width="100" Height="50">(70,120)</Button>
            <Button Canvas.Left="80" Canvas.Top="150" Width="100" Height="50">(60,80)</Button>
        </Canvas>

    默认情况下,所有子元素的都具有相同的ZIndex属性值0;如果子元素的ZIndex值相同,就按照他们在Canvas中添加的先后顺序进行显示。所以上一个示例中,最后一个Button会显示在倒数第二个Button上面;第三个因为我们显示设置了ZIndex的值为1,大于默认值0,所以第三个在第四个上面。

    这一节我们讲了3个非常具有代表性的布局容器,当然需要做好布局是一件非常困难的事情,需要考虑全面。所以还需要我们真正动手做的时候,不断的积累经验。这里只是一个简单的介绍布局容器。下一节,我们会来介绍一下WPF的控件。

  • 相关阅读:
    Docker02 Docker初识:第一个Docker容器和Docker镜像
    Docker01 CentOS配置Docker
    Jenkins02:Jenkins+maven+svn集成
    Junit01 新建Maven项目
    Junit02 Junit创建及简单实现
    Jenkins01:linux+jenkins+ant+jmeter集成
    Jenkins初识03:构建定时任务
    python 协程
    python之socket 网络编程
    python 面向对象
  • 原文地址:https://www.cnblogs.com/Mind-Hacker/p/3478328.html
Copyright © 2011-2022 走看看