WPF中文网

什么是Transform转换

在WPF框架中有一个抽象类叫Transform,它定义了实现二维平面中的转换的功能。它包括旋转 (RotateTransform)、缩放 (ScaleTransform)、倾斜 (SkewTransform) 和平移 (TranslateTransform)4个子类。它定义如何将点从一个坐标空间映射或转换到另一个坐标空间。 此映射由转换 Matrix 来描述,它是一个三行三列 Double 值的集合。

这个类可以实现控件的各种转换,比如放置、缩放、倾斜和平移。

首先我们来看一下Transform的定义:

public abstract class Transform : GeneralTransform, IResource
{
    public static Transform Identity { get; }
    public abstract Matrix Value { get; }
    public override GeneralTransform Inverse { get; }

    public static Transform Parse(string source);
    public Transform Clone();
    public Transform CloneCurrentValue();
    public override Rect TransformBounds(Rect rect);
    public override bool TryTransform(Point inPoint, out Point result);

}

Identity表示获取一个Transform 标识转换,Value表示当前的Matrix矩阵变换,Inverse表示获取当前转换的反函数,它的类型是GeneralTransform。

对于任何Transform对象,一旦使用它去变换某个元素后,它的Inverse属性就会有值。它是GeneralTransform类型,也就是一个变换器,用于将元素变换到原始状态,相当于是Transform的反向操作。需要注意的是,Inverse是用于返回到原始状态的,而不上一次转换的状态。

以下内容来自官网的解释

通过操作矩阵值,可以旋转、缩放、倾斜和移动(转换)对象。 例如,如果将第三行第一列的值(OffsetX 值)更改为 100,可使用它来将对象沿 x 轴移动 100 个单位。 如果将第二行第二列中的值更改为 3,可使用它来将对象的当前高度拉伸三倍。 如果同时更改这两个值,将使对象沿 x 轴移动 100 个单位,并将其高度拉伸 3 倍。 由于WPF仅支持仿射转换,因此右边列中的值始终为 0, 0, 1。

由于直接操作矩阵去设置旋转、缩放、倾斜和移动功能比较复杂,所以WPF提供了4个子类来设置相应的功能。例如,借助 ScaleTransform 类,通过设置其 ScaleX 和 ScaleY 属性即可缩放对象,而无需操作转换矩阵。 同样,借助 RotateTransform 类,只需设置其 Angle 属性,即可旋转对象。

下表中展示了2D转换

子类说明效果备注
RotateTransform按指定的 Angle 旋转元素。表示旋转一个对象的角度
ScaleTransform按指定的 ScaleX 和 ScaleY 量缩放元素。表示放大或缩小一个对象
SkewTransform按指定的 AngleX 和 AngleY 量倾斜元素。表示倾斜一个对象
TranslateTransform按指定的 X 和 Y 量移动(转换)元素。表示移动一个对象
TransformGroup将多个 TransformGroup 对象组合成单个 Transform,然后你可以应用它来转换属性。
MatrixTransform建其他 Transform 类不提供的自定义转换。 使用 MatrixTransform 时,可直接操作矩阵。

转换和坐标系

转换对象时,不只是转换对象,还会转换该对象所在的坐标空间。 默认情况下,转换基于目标对象坐标系的原点 (0,0) 居中。 唯一例外是 TranslateTransform;由于其转换效果无论基于何处居中都相同,因此 TranslateTransform 没有可设置的中心属性。

以下示例使用 RotateTransform 围绕默认中心点 (0, 0) 将 Rectangle 元素(一种 FrameworkElement)旋转 45 度。 下图显示了旋转效果。

围绕点 (0,0) 旋转 45 度的矩形元素

<Canvas Width="200" Height="200">
  <Rectangle 
    Canvas.Left="100" Canvas.Top="100"
    Width="50" Height="50" 
    Fill="RoyalBlue" Opacity="1.0">
    <Rectangle.RenderTransform>
      <RotateTransform Angle="45" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>

默认情况下,元素会围绕其左上角 (0, 0) 旋转。 RotateTransform、ScaleTransform 和 SkewTransform 类提供 CenterX 和 CenterY 属性,以便可以指定应用转换的点。

下面的示例也使用 RotateTransform 将 Rectangle 元素旋转 45 度;但是,这次对 CenterX 和 CenterY 属性进行了设置,使得 RotateTransform 以 (25, 25) 为中心。 下图显示了旋转效果。

围绕点 (25, 25) 旋转 45 度矩形元素

<Canvas Width="200" Height="200">
  <Rectangle 
    Canvas.Left="100" Canvas.Top="100"
    Width="50" Height="50" 
    Fill="RoyalBlue" Opacity="1.0">
    <Rectangle.RenderTransform>
      <RotateTransform Angle="45" CenterX="25" CenterY="25" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>

FrameworkElement转换

FrameworkElement定义了两个有关转换的属性,分别是LayoutTransform和RenderTransform,前者在布局步骤前应用的转换,后者在布局步骤完成后应用,官方推荐使用RenderTransform,原因是可获得性能优势。另外,因为它们继承自 Animatable 类,所以 Transform 类可以被制作成动画。 若要对 Transform 进行动画处理,请将兼容类型的动画应用到要进行动画处理的属性。关于动画,我们会在下一章进行讲解。

下一节,我们将依次演示WPF的转换功能。

——重庆教主 2023年11月2日

GridSplitter控件用来分割窗体的布局,必须放在Grid栅格控件中配合使用,通过鼠标按住GridSplitter进行左右或上下拖动,即可调整行列尺寸。

注意事项:

1、如果您希望GridSplitter控件可以水平调整左右的Grid列宽时,那么HorizontalAlignment属性必须设置为Stretch或者Center。

2、如果您希望GridSplitter控件可以垂直调整行高,那么VerticalAlignment属性必须设置为Stretch或者Center。

3、ShowsPreview属性表示拖动时是否及时绘制调整尺寸。

接下来,我们通过一个例子来说明它的用法

前端代码

<Window x:Class="HelloWorld.MainWindow"
        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:HelloWorld"
        mc:Ignorable="d" FontSize="14"
        Title="WPF中文网之控件课程 - www.wpfsoft.com" Height="350" Width="500">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0" Background="LightBlue">
            <TextBlock TextWrapping="Wrap" Padding="10" LineHeight="20">
                您好,我是站长重庆教主,欢迎来到 WPF中文网,我们的网址是:http://www.wpfsoft.com。您还可以在百度、B站、51CTO、csdn搜索我的名字,以便找到我其它的技术文章或视频课程。本站上线于2023年8月3日,在一个稀松平常的午后,我突然想搭建一个关于学习和分享WPF框架的博客网站,于是开始注册域名、购买空间、安装网站、设置栏目,不到3个小时,WPF中文网就诞生了。
            </TextBlock>
        </Border>
        <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center"   ShowsPreview="False"/>
        <Border Grid.Column="2" Background="LightCoral">
            <TextBlock TextWrapping="Wrap" Padding="10" LineHeight="20">
接下来的日子里,我将从WPF的起源、概述、学习路径等,一路写下去,一直把WPF最后一滴知识详尽才会封笔,我明白这是一场耗费个人巨大精力的战争。但是,那些我曾踩过的坑与走过的弯路,都无时无刻不提醒着我,尽量像讲故事一样,把这一切都写下来吧,总结自己,照亮来者。
            </TextBlock>
        </Border>

    </Grid>
</Window>

接下来我们看看F5运行后,可以用鼠标左右拖动的窗体效果。最好是为GridSplitter单独分配一行或者一列,同时,GridSplitter需要跨越整行或整列,这样的效果会更好。如上面的代码所示,我们在Grid中分割了3个单元格(3列),将GridSplitter居在放置,简单设置一下GridSplitter的属性,就可以达到我们的目的了。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:033-《GridSplitter分割控件》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月30日

通过前面的章节学习,我们来尝试利用学过的布局控件,设计一款简单的UI界面。

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <!--Top-->
        <DockPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Background="#126A74" Height="60">
            <TextBlock Text="WPF中文网应用程序" Foreground="White" FontSize="20" Margin="15,15"/>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                <Border Height="35" Margin="10"  BorderThickness="1" BorderBrush="#CA5100" Background="#CA5100" CornerRadius="10">
                    <TextBlock  Text="退出系统" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10 0 10 0"/>
                </Border>
            </StackPanel>            
        </DockPanel>
        <!--left-->
        <StackPanel Grid.Row="1" Grid.Column="0" Width="350">
            <Border Height="250" Margin="10" Padding="5"  Background="#439D84">
                <TextBlock  Text="参数区域" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontSize="16"/>
            </Border>
            <Border Height="250" Margin="10" Padding="5"  Background="#9CDC7B">
                <TextBlock  Text="参数区域" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontSize="16"/>
            </Border>
            <Border Height="110" Margin="10" Padding="5"  Background="#E97752">
                <TextBlock  Text="参数区域" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontSize="16"/>
            </Border>            
        </StackPanel>
        <!--right-->
        <Grid Grid.Row="1" Grid.Column="1">
            <Border Margin="10" Padding="5" BorderThickness="1" BorderBrush="#F7BD93" CornerRadius="5" Background="#FDD2B2">
                <TextBlock  Text="主体区域" Margin="5" />
            </Border>
            <Border Width="150" Height="150" Margin="10" Padding="5" BorderThickness="1"  Background="Red" BorderBrush="Gray" CornerRadius="75">
                <TextBlock  Text="开始运行" Margin="5" HorizontalAlignment="Center"  FontSize="16" FontWeight="Bold" VerticalAlignment="Center" Foreground="White"/>
            </Border>
        </Grid>
        <!--bottom-->
        <Grid  Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Background="#CA5100">
            <TextBlock Text="版本:1.0.2.5  |  版权所有:中国WPF中文网科技有限公司  www.wpfsoft.com" Margin="10 5 10 5" Foreground="White"/>
        </Grid>
    </Grid>

我们通过Grid栅格布局出界面的主要区域,然后给每个区域进行二次布局,比如Top区域,我们DockPanel和StackPanel的组合,设计成左右两个子区域,左边是应用程序的名称,右边是退出按钮。

中间区域,分为两部分组成,左边是一个StackPanel,里面有一个Border,右边是一个Grid。(注:左侧的StackPanel改成Grid的效果会更好哦)

底部区域是一个Grid,和一个TextBlock内容控件。好,我们布局控件的内容就先介绍到这里,在后续的章节中, 我们还会给出更多的例子来诠释WPF的其它知识点,而这些知识都有可能用以布局控件,所以,我们还会进一步学习布局控件的实战。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:014-《WPF布局Demo示例》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

严格来说,Border并不是一个布局控件,因为它并不是Panel的子类,而是Decorator装饰器的子类,而Decorator继承于FrameworkElement。要了解Border的用法,我们要先看看它的父类Decorator。

public class Decorator : FrameworkElement, IAddChild
{
    public Decorator();

    public virtual UIElement Child { get; set; }
    protected override int VisualChildrenCount { get; }
    protected internal override IEnumerator LogicalChildren { get; }

    protected override Size ArrangeOverride(Size arrangeSize);
    protected override Visual GetVisualChild(int index);
    protected override Size MeasureOverride(Size constraint);

}

Decorator 装饰器只有一个Child 属性,说明Decorator只能容纳一个子元素(UIElement),也就是Border只能容纳一个子元素。那我们再看看Border的结构定义:

public class Border : Decorator
{
    public static readonly DependencyProperty BorderThicknessProperty;
    public static readonly DependencyProperty PaddingProperty;
    public static readonly DependencyProperty CornerRadiusProperty;
    public static readonly DependencyProperty BorderBrushProperty;
    public static readonly DependencyProperty BackgroundProperty;

    public Border();

    public Thickness BorderThickness { get; set; }
    public Thickness Padding { get; set; }
    public CornerRadius CornerRadius { get; set; }
    public Brush BorderBrush { get; set; }
    public Brush Background { get; set; }

    protected override Size ArrangeOverride(Size finalSize);
    protected override Size MeasureOverride(Size constraint);
    protected override void OnRender(DrawingContext dc);

}

我们直接以表格的形式给出Border的相关属性。

属性说明
BorderThickness 设置Border边框的厚度(像素宽度)
Padding 设置子元素相对于Border边框的距离
CornerRadius 设置Border的圆角
BorderBrush 设置Border边框的颜色画刷
Background设置Border的背景颜色画刷

正是因为Border有这么多实用的属性, 所以, 我们通常在布局界面时,Border(装饰器)控件是首选。接下来,我们以一个例子来说明Border有多么好用。

<WrapPanel Margin="10">
    <Border Height="35" Margin="10" Padding="5" BorderThickness="1" BorderBrush="Gray">
        <TextBlock  Text="矩形 - Border控件" Margin="5" />
   </Border>
    <Border Height="35" Margin="10" Padding="5" BorderThickness="1" BorderBrush="Gray" CornerRadius="20">
        <TextBlock  Text="椭圆 - Border控件" Margin="5" />
    </Border>
    <Border Width="150" Height="150" Margin="10" Padding="5" BorderThickness="1" 
            Background="Red" BorderBrush="Gray" CornerRadius="75">
        <TextBlock  Text="圆形Border控件" Margin="5" HorizontalAlignment="Center" 
                    FontSize="16" FontWeight="Bold" VerticalAlignment="Center" Foreground="White"/>
    </Border>
</WrapPanel>

我们分别写了3个Border,第一个Border被设计成矩形,第二个Border增加了圆角属性,第三个Border通过CornerRadius属性,将值设置为宽度或高度的一半,就形成了一个正圆。

将来,我们再配合WPF的模板、样式、触发器会让Border的用法更上一层楼。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:013-《Border控件(边框布局)》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月22日

Canvas控件允许我们像Winform一样拖拽子控件进行布局,而子控件的位置相对于Canvas来说是绝对的,所以我将它称为绝对布局。我们来看看它的结构定义:

public class Canvas : Panel
{
    public static readonly DependencyProperty LeftProperty;
    public static readonly DependencyProperty TopProperty;
    public static readonly DependencyProperty RightProperty;
    public static readonly DependencyProperty BottomProperty;

    public Canvas();

    public static double GetBottom(UIElement element);
    public static double GetLeft(UIElement element);
    public static double GetRight(UIElement element);
    public static double GetTop(UIElement element);
    public static void SetBottom(UIElement element, double length);
    public static void SetLeft(UIElement element, double length);
    public static void SetRight(UIElement element, double length);
    public static void SetTop(UIElement element, double length);
    protected override Size ArrangeOverride(Size arrangeSize);
    protected override Geometry GetLayoutClip(Size layoutSlotSize);
    protected override Size MeasureOverride(Size constraint);

}

观察它的结构,我们可以看到它提供了4个依赖属性,分别是LeftProperty,RightProperty,TopProperty和BottomProperty。其实是将这4个属性附加到子元素身上,以此来设置子元素距离Canvas上下左右的像素位置。

<Canvas>
    <Button  Content="WPF中文网1" Margin="5" />
    <Button  Content="WPF中文网2" Margin="5" />
    <Button  Content="WPF中文网3" Margin="5" />
    <Button  Content="WPF中文网4" Margin="5" />
    <Button  Content="WPF中文网5" Margin="5" />
</Canvas>

上面的示例并没有指定button控件在Canvas控件中的上下左右停靠位置,所以这5个button默认会显示在Canvas的左上角,且只能显示最后一个,前面4个会被遮盖。我们来看看下面的例子。

<Canvas>
    <Button  Content="WPF中文网1" Margin="5" Canvas.Left="50"/>
    <Button  Content="WPF中文网2" Margin="5" Canvas.Top="50"/>
    <Button  Content="WPF中文网3" Margin="5" Canvas.Right="50"/>
    <Button  Content="WPF中文网4" Margin="5" Canvas.Bottom="50"/>
    <Button  Content="WPF中文网5" Canvas.Left="200" Canvas.Top="150" />
</Canvas>

上面的源代码中,我们从上到下,分别来分析一下5个button。

第一个button,设置了Canvas.Left="50",它将保持距离Canvas左边50像素。

第二个button,设置了Canvas.Top="50",它将保持距离Canvas顶部50像素。

第三个button,设置了Canvas.Right="50",它将保持距离Canvas右侧50像素。

第四个button,设置了Canvas.Bottom="50",它将保持距离Canvas底部50像素。

第五个button,设置了Canvas.Left="200" Canvas.Top="150",也就是同时距离Canvas左边200像素,顶部150像素。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:012-《Canvas控件(绝对布局)》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月22日

VirtualizingStackPanel 类(虚拟化元素)和StackPanel 类在用法上几乎差不多。其作用是在水平或垂直的一行中排列并显示内容。它继承于一个叫VirtualizingPanel的抽象类,而这个VirtualizingPanel抽象类继承于Panel布局基类。

public class VirtualizingStackPanel : VirtualizingPanel, IScrollInfo, IStackMeasure
{
    public static readonly DependencyProperty IsVirtualizingProperty;
    public static readonly DependencyProperty VirtualizationModeProperty;
    public static readonly DependencyProperty OrientationProperty;
    public static readonly RoutedEvent CleanUpVirtualizedItemEvent;

    public VirtualizingStackPanel();

    public double VerticalOffset { get; }
    public double HorizontalOffset { get; }
    public double ViewportHeight { get; }
    public double ViewportWidth { get; }
    public double ExtentHeight { get; }
    public double ExtentWidth { get; }
    public bool CanVerticallyScroll { get; set; }
    public bool CanHorizontallyScroll { get; set; }
    public Orientation Orientation { get; set; }
    public ScrollViewer ScrollOwner { get; set; }
    protected override bool CanHierarchicallyScrollAndVirtualizeCore { get; }
    protected internal override Orientation LogicalOrientation { get; }
    protected internal override bool HasLogicalOrientation { get; }

    public static void AddCleanUpVirtualizedItemHandler(DependencyObject element, CleanUpVirtualizedItemEventHandler handler);
    public static void RemoveCleanUpVirtualizedItemHandler(DependencyObject element, CleanUpVirtualizedItemEventHandler handler);
    public virtual void LineDown();
    public virtual void LineLeft();
    public virtual void LineRight();
    public virtual void LineUp();
    public Rect MakeVisible(Visual visual, Rect rectangle);
    public virtual void MouseWheelDown();
    public virtual void MouseWheelLeft();
    public virtual void MouseWheelRight();
    public virtual void MouseWheelUp();
    public virtual void PageDown();
    public virtual void PageLeft();
    public virtual void PageRight();
    public virtual void PageUp();
    public void SetHorizontalOffset(double offset);
    public void SetVerticalOffset(double offset);
    protected override Size ArrangeOverride(Size arrangeSize);
    protected override double GetItemOffsetCore(UIElement child);
    protected override Size MeasureOverride(Size constraint);
    protected virtual void OnCleanUpVirtualizedItem(CleanUpVirtualizedItemEventArgs e);
    protected override void OnClearChildren();
    protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args);
    protected virtual void OnViewportOffsetChanged(Vector oldViewportOffset, Vector newViewportOffset);
    protected virtual void OnViewportSizeChanged(Size oldViewportSize, Size newViewportSize);
    protected override bool ShouldItemsChangeAffectLayoutCore(bool areItemChangesLocal, ItemsChangedEventArgs args);
    protected internal override void BringIndexIntoView(int index);

}

VirtualizingStackPanel 类有什么作用?

比如在ListBox集合控件中需要显示500条数据,那整个屏幕只能显示20条,剩余的480条数据在ListBox控件要不要一次性绘制出来?其实就算绘制出来,用户的屏幕也看不见,只能是拖动滚动条才能看见后面的数据。既然屏幕只能显示20条数据,何不只绘制20条数据的UI子元素,剩下的480条数据的子元素在拖动滚动条时才绘制,这将大大减少计算机的性能消耗,提高UI界面的呈现速度,提高软件的流畅性。

所以,VirtualizingStackPanel 类的作用是开启虚拟化技术,延迟那些看不见的子元素的绘制与渲染。

要开启这项技术,只需要设置Listbox集合控件的附加属性VirtualizingStackPanel.IsVirtualizing="True"即可。因为ListBox的ItemsPanel(元素布局模板)默认采用了VirtualizingStackPanel控件布局。

——重庆教主 2023年8月20

官方解释,定义一个区域,从中可以按相对位置水平或垂直排列各个子元素。我们还是先来看一下它的结构定义:

public class DockPanel : Panel
{
    public static readonly DependencyProperty LastChildFillProperty;
    public static readonly DependencyProperty DockProperty;

    public DockPanel();

    public bool LastChildFill { get; set; }

    public static Dock GetDock(UIElement element);
    public static void SetDock(UIElement element, Dock dock);
    protected override Size ArrangeOverride(Size arrangeSize);
    protected override Size MeasureOverride(Size constraint);

}

DockPanel提供了一个LastChildFill 属性,用来指示最后一个子元素是否填满剩余的空间。其次,它还提供了一个枚举依赖属性,叫Dock。这个属性是附加到子元素身上的,用来指示子元素在DockPanel显示停靠方位,其值分为Left,Right,Top,Bottom。

DockPanel因为继承了FrameworkElement基类,所以您还可以使用FrameworkElement基类的HorizontalAlignment(水平对齐)和VerticalAlignment(垂直对齐)两个属性,用来设置子元素的排列方式。接下来我们将演示它最基本的用法。

一、经典布局

<DockPanel>
    <Button DockPanel.Dock="Left" Content="WPF中文网1" Margin="5" />
    <Button DockPanel.Dock="Top" Content="WPF中文网2" Margin="5" />
    <Button DockPanel.Dock="Right" Content="WPF中文网3" Margin="5" />
    <Button DockPanel.Dock="Bottom" Content="WPF中文网4" Margin="5" />
    <Button  Content="WPF中文网5" Margin="5" />
</DockPanel>

这是DockPanel最经典的布局方式,上下左右都停靠一个控件,中间剩余的空间,全部由最后一个控件填满。

二、水平布局

<DockPanel LastChildFill="False" HorizontalAlignment="Center">
    <Button Content="WPF中文网1" Margin="5" />
    <Button Content="WPF中文网2" Margin="5" />
    <Button Content="WPF中文网3" Margin="5" />
    <Button Content="WPF中文网4" Margin="5" />
    <Button Content="WPF中文网5" Margin="5" />
</DockPanel>

我们只需要设置LastChildFill为False,并设置HorizontalAlignment属性,并再指定子控件的停靠方向,就可以达到上图的效果。

<DockPanel LastChildFill="False" VerticalAlignment="Top">
    <Button Content="WPF中文网1" Margin="5"/>
    <Button Content="WPF中文网2" Margin="5"/>
    <Button Content="WPF中文网3" Margin="5"/>
    <Button Content="WPF中文网4" Margin="5"/>
    <Button Content="WPF中文网5" Margin="5"/>
</DockPanel>

你可以通过摸索HorizontalAlignment(水平对齐)和VerticalAlignment(垂直对齐)两个属性,还可以达到更多意想不到的效果哦。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:010-《DockPanel控件(停靠布局)》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月18日

WrapPanel控件表示将其子控件从左到右的顺序排列,如果第一行显示不了,则自动换至第二行,继续显示剩余的子控件。我们来看看它的结构定义:

public class WrapPanel : Panel
{
    public static readonly DependencyProperty ItemWidthProperty;
    public static readonly DependencyProperty ItemHeightProperty;
    public static readonly DependencyProperty OrientationProperty;

    public WrapPanel();

    public double ItemWidth { get; set; }
    public double ItemHeight { get; set; }
    public Orientation Orientation { get; set; }

    protected override Size ArrangeOverride(Size finalSize);
    protected override Size MeasureOverride(Size constraint);

}

这个控件比较简单,只提供了3个属性,分别是Orientation代表子控件的排列方向,ItemWidth代表子控件的(最大)宽度,ItemHeight代表子控件的(最大)高度。默认的排列方向是水平方向。

一、水平排列

<WrapPanel Orientation="Horizontal">
    <Button Content="WPF中文网1" Margin="5" HorizontalAlignment="Left"/>
    <Button Content="WPF中文网2" Margin="5" HorizontalAlignment="Right"/>
    <Button Content="WPF中文网3" Margin="5" HorizontalAlignment="Center"/>
    <Button Content="WPF中文网4" Margin="5" HorizontalAlignment="Stretch"/>
    <Button Content="WPF中文网5" Margin="5" />
    <Button Content="WPF中文网6" Margin="5" />
    <Button Content="WPF中文网7" Margin="5" />
    <Button Content="WPF中文网8" Margin="5" />
    <Button Content="WPF中文网9" Margin="5" />
    <Button Content="WPF中文网10" Margin="5" />
</WrapPanel>

由上图所示,我们在WrapPanel的Children属性中放了10个Button,分成了两行显示,请注意一个细节,WrapPanel的子元素的高度和宽度都是根据子元素自身内容的尺寸呈现。另外,当WrapPanel处于水平排列时,子元素的HorizontalAlignment是不起作用的。

二、垂直排列

<WrapPanel Orientation="Vertical">
    <Button Content="WPF中文网1" Margin="5" HorizontalAlignment="Left"/>
    <Button Content="WPF中文网2" Margin="5" HorizontalAlignment="Right"/>
    <Button Content="WPF中文网3" Margin="5" HorizontalAlignment="Center"/>
    <Button Content="WPF中文网4" Margin="5" HorizontalAlignment="Stretch"/>
    <Button Content="WPF中文网5" Margin="5" />
    <Button Content="WPF中文网6" Margin="5" />
    <Button Content="WPF中文网7" Margin="5" />
    <Button Content="WPF中文网8" Margin="5" />
    <Button Content="WPF中文网9" Margin="5" />
    <Button Content="WPF中文网10" Margin="5" />
    <Button Content="WPF中文网12" Margin="5" />
    <Button Content="WPF中文网13" Margin="5" />
    <Button Content="WPF中文网14" Margin="5" />
    <Button Content="WPF中文网15" Margin="5" />
    <Button Content="WPF中文网16" Margin="5" />
    <Button Content="WPF中文网17" Margin="5" />
    <Button Content="WPF中文网18" Margin="5" />
    <Button Content="WPF中文网19" Margin="5" />
    <Button Content="WPF中文网20" Margin="5" />

</WrapPanel>

这里我们放了20个button在WrapPanel控件中,并设置Orientation属性为Vertical(垂直排列),此时,请观察前面3个按钮的HorizontalAlignment状态,可以很清晰的看到,第一个按钮居左显示,第二个按钮居右显示,第三个按钮居中显示,说明在Vertical垂直排列下,子元素的水平状态才会生效,反之亦然。

三、指定子元素宽高

<WrapPanel Orientation="Horizontal" ItemWidth="80" ItemHeight="80">
    <Button Content="WPF中文网1" Margin="5" HorizontalAlignment="Left"/>
    <Button Content="WPF中文网2" Margin="5" HorizontalAlignment="Right"/>
    <Button Content="WPF中文网3" Margin="5" HorizontalAlignment="Center"/>
    <Button Content="WPF中文网4" Margin="5" HorizontalAlignment="Stretch"/>
    <Button Content="WPF中文网5" Margin="5" />
    <Button Content="WPF中文网6" Margin="5" />
    <Button Content="WPF中文网7" Margin="5" />
    <Button Content="WPF中文网8" Margin="5" />
    <Button Content="WPF中文网9" Margin="5" />
    <Button Content="WPF中文网10" Margin="5" />
</WrapPanel>

由此可以,我们也可以指定子元素的宽度和高度,以便统一观察。下一节,我们将探讨与WrapPanel非常相似的布局控件DockPanel。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:009-《WrapPanel控件(栈式布局)》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月18日

StackPanel用于水平或垂直堆叠子元素。也就是说,StackPanel同样也有一个Children属性,而Children集合中的元素呈现在界面上时,只能是按水平或垂直方式布局。我们先来看看它的结构定义:

public class StackPanel : Panel, IScrollInfo, IStackMeasure
{
    public static readonly DependencyProperty OrientationProperty;

    public StackPanel();

    public double HorizontalOffset { get; }
    public double ViewportHeight { get; }
    public double ViewportWidth { get; }
    public double ExtentHeight { get; }
    public double ExtentWidth { get; }
    public bool CanVerticallyScroll { get; set; }
    public bool CanHorizontallyScroll { get; set; }
    public Orientation Orientation { get; set; }
    public double VerticalOffset { get; }
    public ScrollViewer ScrollOwner { get; set; }
    protected internal override Orientation LogicalOrientation { get; }
    protected internal override bool HasLogicalOrientation { get; }

    public void LineDown();
    public void LineLeft();
    public void LineRight();
    public void LineUp();
    public Rect MakeVisible(Visual visual, Rect rectangle);
    public void MouseWheelDown();
    public void MouseWheelLeft();
    public void MouseWheelRight();
    public void MouseWheelUp();
    public void PageDown();
    public void PageLeft();
    public void PageRight();
    public void PageUp();
    public void SetHorizontalOffset(double offset);
    public void SetVerticalOffset(double offset);
    protected override Size ArrangeOverride(Size arrangeSize);
    protected override Size MeasureOverride(Size constraint);

}

StackPanel提供了一些属性和方法,最常用的是Orientation枚举属性,用于设置子控件在StackPanel内部的排列方式,分别是水平排列(Horizontal)和垂直排列(Vertical)。默认值是垂直排列(Vertical)。

一、水平排列

<StackPanel Orientation="Vertical">
    <Button Content="WPF中文网1" Margin="20" />
    <Button Content="WPF中文网2" Margin="20" />
    <Button Content="WPF中文网3" Margin="20" />
    <Button Content="WPF中文网4" Margin="20" />
</StackPanel>

请注意,当StackPanel子元素处于垂直排列时,此时子元素的宽度默认与StakcPanel的宽度保持一致,但是子元素的高度是与其自身的高度自适应显示。

二、垂直排列

<StackPanel Orientation="Horizontal">
    <Button Content="WPF中文网1" Margin="20" />
    <Button Content="WPF中文网2" Margin="20" />
    <Button Content="WPF中文网3" Margin="20" />
    <Button Content="WPF中文网4" Margin="20" />
</StackPanel>

请注意,当StackPanel子元素处于水平排列时,此时子元素的高度默认与StakcPanel的高度保持一致,但是子元素的宽度是与其自身的宽度自适应显示。

深入探究StackPanel控件

可以利用子控件的HorizontalAlignment属性或VerticalAlignment来设置子控件在StackPanel内部的显示位置,比如在垂直排列布局模式下,可以设置HorizontalAlignment属性值,Left表示显示在左则,Right显示在右则,Center则居中显示,Stretch表示拉伸填充显示。

需要注意的是,由于WPF的控件布局都是采用自适应计算每个控件的位置,所以在设置了HorizontalAlignment或VerticalAlignment后,子控件的宽度和高度都会重新计算,主要是根据自身内容的尺寸计算。

<ScrollViewer>
    <StackPanel Orientation="Vertical">
        <Button Content="WPF中文网1" Margin="20" HorizontalAlignment="Left"/>
        <Button Content="WPF中文网2" Margin="20" HorizontalAlignment="Right"/>
        <Button Content="WPF中文网3" Margin="20" HorizontalAlignment="Center"/>
        <Button Content="WPF中文网4" Margin="20" HorizontalAlignment="Stretch"/>
        <Button Content="WPF中文网5" Margin="20" />
        <Button Content="WPF中文网6" Margin="20" />
        <Button Content="WPF中文网7" Margin="20" />
        <Button Content="WPF中文网8" Margin="20" />
        <Button Content="WPF中文网9" Margin="20" />
    </StackPanel>
</ScrollViewer>

于是,我们可以看到上图中前三行的按钮都是根据自身内容的宽高自适应绘制的。另外,如果StackPanel内部的子控件太多,则需要配合滚动条容器ScrollViewer控件。

和StackPanel类似的控件,还有两个,分别是WrapPanel和DockPanel。下一节,我们将探讨WrapPanel控件。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:008-《StackPanel控件(栈式布局)》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月17日

UniformGrid和Grid有些相似,只不过UniformGrid的每个单元格面积都是相等的,不管是横向的单元格,或是纵向的单元格,它们会平分整个UniformGrid。我们先看看它的结构定义:

public class UniformGrid : Panel
{
    public static readonly DependencyProperty FirstColumnProperty;
    public static readonly DependencyProperty ColumnsProperty;
    public static readonly DependencyProperty RowsProperty;

    public UniformGrid();

    public int FirstColumn { get; set; }
    public int Columns { get; set; }
    public int Rows { get; set; }

    protected override Size ArrangeOverride(Size arrangeSize);
    protected override Size MeasureOverride(Size constraint);

}

UniformGrid控件提供了3个属性,分别是FirstColumn、Columns 、Rows 。FirstColumn表示第一行要空几个单元格,后面两个属性分别用于设置行数和列数。接下来我们以实际的例子来分析这3个属性的用法。

<UniformGrid>
    <Button Content="WPF中文网1" Margin="2"/>
    <Button Content="WPF中文网2" Margin="2"/>
    <Button Content="WPF中文网3" Margin="2"/>
    <Button Content="WPF中文网4" Margin="2"/>
</UniformGrid>

这是我们没有UniformGrid的属性的效果,它会根据子元素的数量和UniformGrid自身的尺寸来决定行数和列数。

<UniformGrid FirstColumn="1" Rows="3" Columns="3">
    <Button Content="WPF中文网1" Margin="2"/>
    <Button Content="WPF中文网2" Margin="2"/>
    <Button Content="WPF中文网3" Margin="2"/>
    <Button Content="WPF中文网4" Margin="2"/>
</UniformGrid>

我们故意设计了当前UniformGrid为3行3列,同时设置第一行第一个单元格保持空白,于是我们就看到了上图中的效果。

UniformGrid控件使用非常简单方便,通常用于局部的布局。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:011-《UniformGrid控件(均分布局)》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年8月19日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1