01-XAML与布局
发布时间:2026/9/30 23:43:56来源:尧图网络
XAML 与布局从基础到熟练上位机主窗口要同时放状态、曲线、配方、报警。这篇只解决「这些区域怎么摆」。控件里面填什么、数据怎么绑是后面几篇。熟练的标准不用拖拽设计器能用一个Grid把四块区域分成可拉伸的骨架并且说得清某一块为什么用Auto、*或固定高度。1. XAML 在写什么XAML 是界面的声明。一个标签是一个对象属性写成特性子标签是它的内容。Windowx:ClassDemo.MainWindowxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlTitle单工位检测Height640Width960GridTextBlockText在线//Grid/Window默认命名空间指向 WPF 的控件。Grid、TextBlock、Button都从这里来。xmlns:x是 XAML 语言自己的标记例如x:Name、x:Class。x:Class把这个 XAML 和同名的 C# 分部类连在一起。窗口的构造函数在MainWindow.xaml.cs里里面要有InitializeComponent()。没有x:Name的控件code-behind 里就点不到它。MVVM 里多数控件靠绑定不需要名字。窗口的内容只能有一个子元素。所以根上放一个Grid或StackPanel区域都放进这个面板里而不是把多个控件直接写在Window下面。2. 基础两种面板StackPanel沿一个方向排StackPanelOrientationHorizontalMargin12TextBlockText状态VerticalAlignmentCenter/TextBlockText在线Margin12,0,0,0/ButtonContent启动Margin12,0,0,0//StackPanelOrientationHorizontal从左到右。默认是从上到下。适合状态栏里短的一排标签、数值、按钮。它不会把多余空间分给子元素。窗口拉高时竖着的 StackPanel 下面是空的子元素高度仍是自己的内容高度。所以主窗口不要只用一个 StackPanel 装着曲线和报警。Grid按行和列分主界面用 Grid。GridGrid.RowDefinitionsRowDefinitionHeightAuto/RowDefinitionHeight*/RowDefinitionHeight180//Grid.RowDefinitionsGrid.ColumnDefinitionsColumnDefinitionWidth2*/ColumnDefinitionWidth*//Grid.ColumnDefinitions/Grid三种尺寸写法含义上位机里放什么Auto行高或列宽由内容决定顶上的状态栏里面就几个文本和按钮*吃掉剩余空间。2*和*按 2:1 分曲线区比配方区宽180固定像素底部报警列表不希望它被曲线挤没子元素用附加属性声明自己落在哪一格。Grid.Row、Grid.Column、Grid.ColumnSpan定义在Grid上写在子元素标签上用来告诉 Grid 怎么摆这个子元素。它们不是 TextBlock、Border 自己的属性。XAML 里写成Grid.Row1即可。C# 里没有element.Grid.Row这种写法用静态方法Grid.SetRow(element, 1)。登记一份新的附加属性见10.1-附加属性.md。TextBlockGrid.Row0Grid.Column0Text状态栏/BorderGrid.Row1Grid.Column0Background#111/BorderGrid.Row1Grid.Column1Background#222/BorderGrid.Row2Grid.Column0Grid.ColumnSpan2Background#333/不写Grid.Row和Grid.Column时默认是第 0 行第 0 列。多个元素会叠在同一格后写的盖住先写的看起来像控件丢了。每一格只放一个直接子元素里面再套面板。Grid.ColumnSpan2横跨两列。底部报警条用它从左到右通栏。Grid.RowSpan是跨行。行和列都从 0 数。测量和排列面板摆孩子分两步。Measure父元素问每个子元素理想要多大子元素回报DesiredSize。Arrange父元素按测量结果分给子元素一块实际矩形。竖直的 StackPanel 沿垂直方向排列。测量子元素时这一向给出的可用高度是无穷大double.PositiveInfinity子元素就按自己的内容回报高度。另一向仍是父元素给的真实宽度文字和按钮不会在水平方向拿到无限空间。格子被拉高之后多出来的高度不会再分给这些子元素所以曲线区不会跟着变高。水平 StackPanel 则相反宽度方向无穷大高度方向是真实可用高度。Grid 先扣掉Auto和固定尺寸再把剩下的分给带*的行和列。主窗口骨架用 Grid就是为了这一步。XAML 里写出来的这层嵌套是逻辑树。按钮模板展开之后的边框和圆点在视觉树上这篇还不涉及。差别见14-视觉树与逻辑树.md。另外三种面板主骨架不用它们。知道差别面试问到能对上场景。面板特点上位机里什么时候用不要用在哪DockPanel子元素靠上、下、左、右。LastChildFill默认 true最后一个子元素铺满剩下的区域顶上一栏、底下一栏这种简单停靠状态、曲线、配方、报警同时存在的主界面WrapPanel一行或一列排不下就换行一组数量不固定的小按钮主窗口骨架Canvas用Canvas.Left、Canvas.Top绝对定位。窗口拉大时子元素不跟着重新分空间在固定画布上画简单图形会随窗口缩放的业务界面LastChildFillFalse时最后一个子元素也只停靠在自己的边上不再铺满剩余区域。3. 熟练对齐、边距、格子里再套一层对齐格子比内容大时靠HorizontalAlignment和VerticalAlignment决定内容贴哪边。默认是Stretch内容被拉满这一格。控件写了明确的Width时水平方向的Stretch不再起作用。宽已经是 200就不会再被格子拉宽。垂直方向没写Height时VerticalAlignmentStretch仍然有效。Height写死则只影响垂直这一向。某一向的Stretch生效前提是这一向没有写死尺寸。格子分到的矩形小于子控件想要的尺寸时子控件会被排进这块更小的区域显示不下的部分留在区域外面。ClipToBounds默认是 false格子不会替你把画出界的图形裁掉曲线有时会叠到旁边的配方区上。要把绘制限制在这一格里给这格的Border或面板设ClipToBoundsTrue。状态栏里的文字不要拉伸用Center或LeftTextBlockGrid.Row0Text在线HorizontalAlignmentLeftVerticalAlignmentCenterMargin12,8/Margin12,8是左右 12、上下 8。Margin12,8,12,8按左、上、右、下。面板和面板之间留白用 Margin不要用空的行来充数也不要用很大的 Margin 把控件硬推到某个坐标。窗口一缩放硬凑的位置就会错。Margin是控件外面的空白用来推开旁边的控件。Padding是控件里面的空白边框到内容的距离。Border、Button有Padding。StackPanel没有Padding它里面的空隙仍用子元素的Margin。配方区那层Border的Padding12是内边距外面的Margin才是和曲线区之间的缝。格子里再放面板Grid 的一格里仍然只能直接放一个子元素。这一格里要横排多个按钮时放一个 StackPanelStackPanelGrid.Row0Grid.ColumnSpan2OrientationHorizontalMargin12,8TextBlockText状态VerticalAlignmentCenter/TextBlockText待机Margin16,0,0,0VerticalAlignmentCenter/ButtonContent启动Margin16,0,0,0/ButtonContent停止Margin8,0,0,0//StackPanel曲线那一格先放一个Border占位后面换成 OxyPlot。配方那一格先放一个竖排 StackPanel。报警那一格先放一个ListBox后面用数据模板画每一行。剩余空间不够时*行会被Auto和固定高度先扣掉。窗口高度 640状态栏 Auto 约 40报警 180曲线区大约是 640 减去这两段再减去窗口边框。把窗口拉小到比 40180 还小时*会被挤到最小曲线先消失。报警用固定高度就是为了保住列表。若希望报警也可以被压缩把它改成*另外给MinHeight。行和列自己就能限制范围而且先于子控件生效RowDefinitionHeight*MinHeight120/ColumnDefinitionWidth*MinWidth240MaxWidth420/MinHeight120表示这一行再分剩余空间窗口缩小时至少留下 120。若*算出来的高度已经小于MinHeight这一行会抬到最小高度去挤其他*行。多行都设了最小高度加起来超过窗口时排不下的部分会被窗口裁掉而不是各行继续按比例缩小。MaxWidth防止某一列在超宽屏幕上拉得过宽。写在RowDefinition、ColumnDefinition上比只写在子控件上更稳。两张互不相干的 Grid 要对齐同一列的宽度时用SharedSizeGroup。外层设Grid.IsSharedSizeScopeTrue两列的ColumnDefinition写同一个SharedSizeGroup名字。上位机里两张表要列对齐时才用主骨架用不上。需要操作员拖动曲线和配方的分界时在两列之间放GridSplitter。它占自己的格子或贴在某一列的边缘。拖的是列宽不是用 Margin 模拟出来的位置。分割列时宽度写小例如Width4并且VerticalAlignmentStretch分割条才会占满这一行的高度。只写HorizontalAlignmentRight而不拉满垂直方向分割条会缩成内容那么高不好拖。分割行则相反写Height并且HorizontalAlignmentStretch。主骨架示例不必先加。做法放在练习里。4. 后期应用单工位检测的骨架下面这一份可以直接当主窗口的第一版。没有绑定没有命令只证明四块区域的位置。数字和按钮文字是写死的下一篇再换成控件细节Binding 那篇再接数据。Windowx:ClassDemo.MainWindowxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlTitle单工位检测Height640Width960MinHeight480MinWidth720GridGrid.RowDefinitionsRowDefinitionHeightAuto/RowDefinitionHeight*/RowDefinitionHeight180//Grid.RowDefinitionsGrid.ColumnDefinitionsColumnDefinitionWidth2*/ColumnDefinitionWidth*MinWidth240//Grid.ColumnDefinitionsStackPanelGrid.Row0Grid.ColumnSpan2OrientationHorizontalMargin12,8TextBlockText离线VerticalAlignmentCenter/TextBlockText待机Margin16,0,0,0VerticalAlignmentCenter/TextBlockText25.0Margin16,0,0,0VerticalAlignmentCenter/ButtonContent启动Margin16,0,0,0/ButtonContent停止Margin8,0,0,0//StackPanelBorderGrid.Row1Grid.Column0Margin12,0,6,8Background#1E1E1ETextBlockText曲线ForegroundWhiteMargin8//BorderBorderGrid.Row1Grid.Column1Margin6,0,12,8Padding12StackPanelTextBlockText配方/TextBlockText上限Margin0,12,0,4/TextBoxText30/TextBlockText下限Margin0,12,0,4/TextBoxText10/ButtonContent保存Margin0,12,0,0HorizontalAlignmentLeft//StackPanel/BorderBorderGrid.Row2Grid.ColumnSpan2Margin12,0,12,12TextBlockText报警列表Margin8//Border/Grid/Window拉窗口时的预期状态栏高度不变按钮始终在一行。曲线和配方一起变高。配方列有MinWidth240不会被拉成一条缝。报警区高度保持约 180并且横跨两列。这就是后期一直沿用的骨架。后面换曲线控件、换报警ListBox只替换某一格里的子元素行和列不用推倒。5. 自测为什么Window下面不能并列写两个Button状态栏为什么用Auto曲线区为什么用*不写Grid.Row时元素出现在哪一格报警条要同时占左右两列写哪个附加属性配方区里有标签、输入框、保存按钮这一格的直接子元素是什么Margin和Padding有什么差别控件写了Width200又写HorizontalAlignmentStretch。它会被格子拉宽吗参考答案窗口内容只能有一个子元素。多个控件要放进 Grid 或 StackPanel。状态栏高度随文字和按钮不参与瓜分剩余空间。曲线区吃掉扣掉状态栏和报警区之后剩下的高度。第 0 行第 0 列。不写行和列的多个元素会叠在这里。Grid.ColumnSpan2。行已经是第 2 行时再写Grid.Row2。一个竖排的 StackPanel。Grid 的一格只有一个直接子元素里面再排多个控件。Margin在控件外面推开周围控件。Padding在控件里面是边框到内容的距离。Panel没有Padding所以StackPanel、Grid都没有。Border和Button这类Control才有。不会。这一向已经有明确宽度Stretch不起作用。常见错误Window下面直接放多个控件没有套 Grid。用竖直 StackPanel 做主界面指望窗口拉高时曲线跟着变高。同一格里放了多个直接子元素控件叠在一起。用很大的 Margin 把控件推到某个位置窗口一缩放就错位。写了固定Width还指望HorizontalAlignmentStretch把控件拉满。把外边距和内边距当成同一个属性。配方区和曲线区之间的缝是Margin配方区边框到输入框的距离是Padding。6. 练习新建 WPF 项目用第 4 节替换主窗口运行后拖动窗口四边对照上面三条预期。把报警区改成Height*再拖矮窗口看曲线和报警谁被挤扁。然后改回180。在状态栏右边再加一个「保存配方」按钮不新开一行。做不到时检查是不是还在用竖排 StackPanel。在曲线和配方两列之间加一条GridSplitter拖动时只有这两列的宽度变化状态栏和报警条的行高不变。分割条落在曲线那一格的右缘Grid.Row1、Grid.Column0、HorizontalAlignmentRight、VerticalAlignmentStretch、Width4。漏掉VerticalAlignmentStretch时分割条高度不够不好拖。主骨架示例可以先不加。面试可能提问主界面为什么用 Grid而不是一个 StackPanel 把曲线和报警从上往下排答StackPanel 不把剩余高度分给中间区域。窗口拉高时曲线区不会跟着变高。Grid 用*把扣掉状态栏和报警区之后的高度给曲线。Auto、*和固定像素各在什么时候用答状态栏用Auto高度由内容决定。曲线用*吃剩余空间。报警列表用固定高度避免被曲线挤没。Grid.Row写在子控件上它是谁的属性答是 Grid 的附加属性用来声明这个子元素落在第几行。不写时默认第 0 行第 0 列多个元素会叠在一起。窗口下面为什么不能直接放两个按钮答Window的内容只能有一个子元素。多个控件要放进 Grid 或 StackPanel。某一格里还要再排多个控件时这一格的直接子元素仍然只有一个面板。DockPanel的LastChildFill做什么什么时候用GridSplitter答LastChildFill默认是 true最后一个子元素铺满剩下的区域。设为 false 时它只停靠在自己的边上。操作员要拖动曲线和配方的分界时用GridSplitter改列宽。主界面的四块区域仍然先用 Grid 的行和列不用停靠面板来代替。控件不撑开、不收缩先看布局的哪一步答先看 Measure 和 Arrange。竖直 StackPanel 在排列方向上用无穷可用高度测量子元素按内容要高度多出来的高度不会分给它们水平方向仍是真实宽度。Grid 才会把剩余空间分给*行。写死Width只让水平Stretch失效没写Height时垂直方向仍可拉伸。
网站建设高端定制企业官网