布局和常用panel学习

一、简介

所有的wpf布局容器都派生自system.windows.controls.panel。panel继承自frameworkelement。 在panel中有一个比较重要的属性是uielementcollection 类型的children属性,uielementcollection是一个有序的集合。我们可以使用继承自panel的类来重写measureoverride(),arrangeoverride()实现自定义面板。

常用的布局容器:
border不是布局面板,但是经过几个大佬的提醒,用好他真的很重要,所以我就放在第一个了。
stackpanel: 堆栈面板,水平或垂直放置元素。
wrappanel:可换行的行中放置元素,在水平方向上从左向右放置元素,换行后也是从左向右。在垂直方向上,从上到下放置元素,在切换列后也是从上到下。
dockpanel: 根据容器的整个边界调整元素。
grid:在行列表格中排列元素。
uniformgrid:强制所有单元格具有相同尺寸。
canvas:使用固定坐标绝对定位元素。
scrollviewer:通过添加滚动条可以使当前过长布局内的内容纵向或者横向滚动。再有限的区域内可以通过滚动呈现更多的内容。

二、代码案例

1.border

border不是布局面板,但是经常与布局类的面板一起配合使用,所以先介绍border。border的主要作用是给元素添加边框,这个元素可以理解为一个布局面板,一个控件等等。他包含设置边框的2个属性,borderbrush用来设置颜色,borderthickness用来设置宽度。cornerradius设置圆角。而padding和margin一个设置边框和里面元素的间距,一个设置边框和其他临近元素的间距。而background则是设置border所有内容的颜色。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="800">
    <stackpanel>
        <!--borderbrush用来设置颜色-->
        <!--borderthickness用来设置宽度-->
        <!--cornerradius设置圆角-->
        <!--padding设置边框和里面元素的间距-->
        <!--margin设置边框和其他临近元素的间距-->
        <!--background则是设置border所有内容的颜色-->
        <border background="bisque" borderbrush="coral"  borderthickness="3">
            <button content="button a" width="120"/>
        </border>
        <border borderbrush="red" borderthickness="3" margin="5">
            <button content="button b"/>
        </border>
        <border borderbrush="darkred" borderthickness="3" background="red" padding="5">
            <button content="button c"/>
        </border>
    </stackpanel>
    <!--<grid>
        
    </grid>-->
</window>

运行效果:

2.stackpanel

stackpanel面板可以在单行或者单列以堆栈的形式排列子元素。默认情况下stackpanel面板按从上到下的顺序排列元素。如果不指定宽度、则默认宽度和stackpanel面板宽度一致,如果stackpanel宽度发生变化,则按钮也会拉伸以适应变化。而如果设置了宽度、就不会跟面板宽度变更发生变化。但是想要设计出自己想要的好看布局,还需要更多的元素,先看一个基本的例子。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="800">
    <stackpanel x:name="root_spanel" >
        <button content="点我切换方向" click="orientationtranslator_click"/>
        <button content="点我添加元素到面板中" click="addelementtopanel_click"/>
        <button x:name="btn_fixedwidth" content="点我手动设置宽度为120" click="setcurrentwidth_click"/>
        <button content="大家一定要努力学习c#!!!"/>
    </stackpanel>
</window>

后台逻辑代码:

       private void orientationtranslator_click(object sender, routedeventargs e)
        {
            root_spanel.orientation = root_spanel.orientation == orientation.horizontal ? orientation.vertical : orientation.horizontal;
        }

        private void addelementtopanel_click(object sender, routedeventargs e)
        {
            button btn = new button();
            btn.content = "我是新添加的button";
            root_spanel.children.add(btn);
        }

        private void setcurrentwidth_click(object sender, routedeventargs e)
        {
            btn_fixedwidth.width = 120;
        }

运行效果:

3.wrappanel

wrappanel面板可以一次排列一行或一列然后再换行继续排列,和stackpanel一样,可以通过设置orientation属性来设置当前是以水平还是垂直来排列子元素。因为是根据窗体变化演示布局排列,这个只有xaml。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="800">
    <wrappanel>
        <button content="《老黄牛》" width="120"/>
        <button content="臧克家"  width="120"/>
        <button content="块块荒田水和泥,"  width="120"/>
        <button content="深翻细作走东西。"  width="120"/>
        <button content="老牛亦解韶光贵,"  width="120"/>
        <button content="不待扬鞭自奋蹄。"  width="120"/>
    </wrappanel>
</window>

运行效果:

4.dockpanel

dockpanel面板与stackpanel面板类似,但是dockpanel可以通过设置dock附加属性设置子元素停靠的边。dock的值为left、right、top、bottom。通过设置子元素再dockpanel面板中的dock属性。可以修改子元素再dockpanel面板内的位置。可以通过lastchildfill设置为true来告诉dockpanel面板使最后一个元素占满剩余控件。而设置的停靠顺序会影响布局结果。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="900">
    <dockpanel lastchildfill="true">
        <button dockpanel.dock="top" content="今天学习了吗?"/>
        <button dockpanel.dock="left" content="今天写代码了?"/>
        <button dockpanel.dock="right" content="随便放点东西"/>
        <button dockpanel.dock="right" verticalalignment="center" content="真的理解了吗?要不要再多敲几遍。"/>
        <button dockpanel.dock="bottom" content="程序员不学习写代码,还能干什么呢?"/>
        <button dockpanel.dock="bottom" content="程序员不学习写代码,还能干什么呢?"/>

    </dockpanel>
</window>

运行效果:

5.grid

grid面板是把显示内容分割到不可见的行列网格中。通过设置行列和对应的宽高占比。来进行网格布局。grid布局再平时使用的比较多。大部分都是用来做布局的嵌套,设计外框各个部分的比例,然后在子元素中嵌套其他布局控件。来实现区域的划分。
在使用grid面板时,需要用到一个叫做附加依赖项属性的参数。在布局相关的内容里不会去讲什么是附加依赖项属性,这个会在依赖项属性中去讲解,这里只有了解就行。因为这个针对于grid布局来说是固定写法。
我们添加一个三行三列的grid面板。grid.rowdefinitions和grid.columndefinitions里面的内容是我们设置的gird的行列数。各有3个,代表我们是一个三行三列的网格。我们没有设置宽高。就会默认为是等分的。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="900"> 
    <grid>
        <grid.rowdefinitions>
            <rowdefinition/>
            <rowdefinition/>
            <rowdefinition />
        </grid.rowdefinitions>
        <grid.columndefinitions>
            <!--各列平分宽度-->
            <columndefinition/>
            <columndefinition />
            <columndefinition />
            
            <!--要求左边一列宽度固定,右边一列以文本内容宽度适配,剩下的宽度区域都给中间的列。为了提高代码可读性,不建议省略width="*"虽然都是一样的。-->
            <!--<columndefinition width="140"/>
            <columndefinition width="*"/>
            <columndefinition width="auto"/>-->
        </grid.columndefinitions>
        <!--网格布局设计好之后。我们需要往里面放置内容。我们要使用grid.column、grid.row这2个附加依赖项属性来实现把button放置到不同的网格中-->
        <button grid.column="0" grid.row="0" content="button row 1 column 1"/>
        <button grid.column="1" grid.row="0" content="button row 1 column 2"/>
        <button grid.column="2" grid.row="0" content="button row 1 column 3"/>
        <button grid.column="0" grid.row="1" content="button row 2 column 1"/>
        <button grid.column="1" grid.row="1" content="button row 2 column 2"/>
        <button grid.column="2" grid.row="1" content="button row 2 column 3"/>
        <button grid.column="0" grid.row="2" content="button row 3 column 1"/>
        <button grid.column="1" grid.row="2" content="button row 3 column 2"/>
        <button grid.column="2" grid.row="2" content="button row 3 column 3"/>
    </grid>
</window>

运行效果:

我们尝试运行代码。就会得到9个一样大小的button。我们尝试拖动窗体大小。不管怎么拖动,应该都是等量变化的。

6.uniformgrid

uniformgrid面板的特点是每个单元格始终保持一致的大小。通过设置行列数量来分割布局。元素通过放入的前后顺序被分配到不同的位置。这个再某些特定场景配合数据虚拟化和列表虚拟化使用的还是比较多的。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="900">
    <uniformgrid columns="2" rows="2">
        <!--按照先来后到的顺序,先行后列的放入到行列单元格-->
        <button content="button a"/>
        <button content="button b"/>
        <button content="button c"/>
        <button content="button d"/>
    </uniformgrid>
</window>

运行效果:

7.canvas

canvas是一个基于坐标的布局面板。他主要用于构建图形工具的绘图、canvas知识再指定的位置放置子元素。并且子元素要提供精确的尺寸。再canvas中需要设置canvas.left和canvas.top属性。用来设置相对于原点的left和top。
也可以使用canvas.right和canvas.bottom。但是canvas.left和right不能同时使用,canvas.top和canvas.bottom也不能同时使用。使用panel.zindex来设置子元素的层级关系。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="900">
    <canvas>
        <!--panel.zindex="1"设置子元素的层级关系,哪个数字大,哪个在上面-->
        <button content="button a" canvas.left="255" canvas.top="70"  panel.zindex="2" width="80px" height="30px"/>
        <button content="button b" canvas.left="110" canvas.top="100"  width="80px" height="30px"/>
        <button content="button c" canvas.left="295" canvas.top="81" panel.zindex="1"  width="80px" height="30px"/>
    </canvas>
</window>

运行效果:

button a和button c的重叠关系使用panel.zindex来设置。

8.scrollviewer

如果要再一个比较小的区域内显示特别多的内容,就需要使用scrollviewer来进行横向或纵向滚动了,但是再实际使用过程中往往配合数据虚拟化和列表虚拟化来实现支持更多内容的滚动效果。不然如果内容一旦特别多,scrollviewer下的内容又特别长,每次滚动都会触发布局的重新测量和排列。如果不使用虚拟化,会全部重新计算所有的布局元素,会特别卡,导致使用困难。

案例xaml代码:

<window x:class="wpf_panel.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:wpf_panel"
        mc:ignorable="d"
        title="mainwindow" height="500" width="900">
    <grid>
        <stackpanel>
            <scrollviewer name="scroll" width="480"  height="350" horizontalscrollbarvisibility="auto" verticalscrollbarvisibility="visible" >
                <textblock    name="txtshowarticle"   foreground="gray" margin="20,10" loaded="txtshowarticle_loaded" />
            </scrollviewer>
        </stackpanel>
    </grid>
</window>

后台逻辑代码:

  string content = @"中国青年网6月22日电 据“健康广东”微信公众号消息,6月21日0-24时,全省新增2例本土确诊病例,深圳报告1例,东莞报告1例。
全省新增境外输入确诊病例5例,广州报告2例,分别来自法国和阿曼;深圳报告1例,来自印度尼西亚;珠海报告1例,来自孟加拉国;东莞报告1例,来自阿联酋。新增境外输入无症状感染者7例,广州报告3例,2例来自柬埔寨,1例来自阿联酋;佛山报告1例,来自柬埔寨;中山报告1例,来自加蓬;肇庆报告2例,均来自印度尼西亚。新增出院16例。
截至6月21日24时,全省累计报告新冠肺炎确诊病例2706例(境外输入1140例)。目前在院221例。
(来源:中国青年网)";
        private void showarticle()
        {
            //获取私信信息           
            stringbuilder strmessage = new stringbuilder();
            strmessage.append("标题:" + "广东昨日新增2例本土确诊病例,深圳、东莞各1例" + "\r\n");
            strmessage.append("来源:" + "中国青年网" + "\r\n");
            strmessage.append("发送时间:" + "2021-06-22 15:31:32" + "\r\n");
            strmessage.append("发送内容:" + content + "\r\n\n");
            txtshowarticle.text = strmessage.tostring();
        }
       
        private void txtshowarticle_loaded(object sender, routedeventargs e)
        {
            showarticle();
        }

运行效果: