
常用控件从基础到熟练上一篇把窗口分成了状态、曲线、配方、报警。这一篇把占位换成真正的控件。数据先写死在 XAML 里。Binding 是下一篇Command 是第 5 篇。熟练的标准配方区能选配方、改上下限报警区是一张只能看、不能改的表说得清显示用TextBlock输入用TextBox一组记录用ListBox还是DataGrid。1. 基础文本、输入、按钮TextBlock只显示状态、温度、标签都用它。操作员不能在上面打字。TextBlockText25.0FontSize20VerticalAlignmentCenter/要显示的是Text。不要为了显示一个数去用只读TextBox只读输入框仍然像能编辑。TextBox给操作员改的值配方上下限用它。TextBoxText30Width120HorizontalAlignmentLeft/不设Width且放在会拉伸的格子里时输入框会拉满整列。配方区这样是合适的。状态栏里不要放会拉满的输入框。上限、下限是单行数值。AcceptsReturn默认就是 false回车不会在框里换行。不要为了配方输入去打开它。报警备注那种需要多行的文本才设AcceptsReturnTrue并加上TextWrappingWrap。数字是否合法留给转换器这一篇先允许框里出现任意字符。Text在这一篇是写死的字。下一篇改成{Binding HighLimit}之后界面和数据才连得上。Button点一下做一件事ButtonContent启动Padding12,4/按钮上的字是Content不是Text。Content也可以是别的元素例如一个圆点加文字。现在先写字符串。Button、ListBoxItem、ComboBoxItem都是ContentControl所以内容都放在Content上可以是文字也可以是更复杂的界面。模板那篇再讲这块内部怎么画。Click能进 code-behind。本篇若要确认按钮是活的可以临时在Click里改某个TextBlock的Text。启动设备、写寄存器不要写在Click里那是 Command 那篇的事。三个控件的属性不要混控件字写在哪谁来改TextBlockText程序TextBoxText操作员稍后由绑定写回ButtonContent一般不改2. 熟练选择一项和列出很多行ComboBox多套配方里选一套ComboBoxSelectedIndex0HorizontalAlignmentStretchComboBoxItemContent默认配方/ComboBoxItemContent高温/ComboBoxItemContent低温//ComboBoxSelectedIndex0是第一项。操作员展开后只能选中一项。上位机里它代表「当前编辑的是哪套配方」不是用来显示报警的。IsEditable默认是 false操作员不能在框里打出列表中没有的名字。配方选择保持默认。设成 true 时框变成可输入容易写出一套不存在的配方。项很多、或项是后面从 SQLite 读出来的就不要在 XAML 里逐条写ComboBoxItem。下一篇起改为ItemsSource绑定到集合。ListBox一列记录突出当前选中适合先把报警「列出来」。每项这一篇先是纯文本。ListBoxListBoxItemContent12:01 温度超上限 31.2/ListBoxItemContent12:04 已恢复//ListBox点某一行它成为选中项。SelectionMode默认是Single报警表保持单选即可。ListBox自己带滚动不必再包一层ScrollViewer。前提是父布局给了它一块有限高度例如 Grid 里固定 180 的那一行。放进竖直的 StackPanel 时可用高度是无穷的列表会按全部内容一直变高滚动条不会出现。这和布局篇里 StackPanel 不把剩余空间分给子元素是同一件事。ListBox的一项后期会换成时间、内容、数值三列那是数据模板那篇。这一篇用一行字就能练习选中。DataGrid已经知道列的表报警最终更适合表因为列是固定的时间、内容、当时的值。DataGridAutoGenerateColumnsFalseIsReadOnlyTrueCanUserAddRowsFalseHeadersVisibilityColumnGridLinesVisibilityHorizontalDataGrid.ColumnsDataGridTextColumnHeader时间Binding{Binding Time}Width160/DataGridTextColumnHeader内容Binding{Binding Message}Width*/DataGridTextColumnHeader值Binding{Binding Value}Width80//DataGrid.Columns/DataGrid这一篇先认识这几个开关。列上的Binding只是列定义。没有ItemsSource时不会生成行所以表现在是空的。行数据下一篇再接。ListBox和DataGrid都自带滚动同样要求父布局限制高度。报警表放在 Grid 的固定高度行里。不要放进竖直 StackPanel否则表会无限变高内部滚动不会出现。HeadersVisibilityColumn去掉最左边的行头有的主题下单元格左侧仍会留一条细缝不影响列内容。属性作用AutoGenerateColumnsFalse列由自己写不按对象属性自动长出一堆列IsReadOnlyTrue报警是记录不允许在格子里改字CanUserAddRowsFalse底部不留一个空白新行HeadersVisibilityColumn只要列表头不要最左边的行头Width*这一列吃掉剩余宽度和 Grid 的*同一思路怎么选配方名称这种「从几项里挑一个」用ComboBox。先看选中效果、一项就一句话用ListBox。时间、内容、数值这种多列记录用DataGrid并且只读。不要用DataGrid做主界面布局。它是表不是面板。选中项ListBox、ComboBox、DataGrid都有SelectedIndex和SelectedItem。SelectedIndex是第几项从 0 开始。没有选中时是-1。SelectedItem是那一项本身。静态 XAML 里它是ListBoxItem或ComboBoxItem。绑到集合之后它是集合里的那个对象例如一条报警。本篇在设计器或运行界面里点一行能看到该项高亮就说明选中在工作。下一篇把SelectedItem绑到 ViewModel用来删除这一条。多选删除报警偶尔要一次删掉几条。SelectionMode还有Multiple和Extended。Extended可以用 Ctrl 和 Shift 连续选和资源管理器一样报警表用这个。默认Single保持不变直到真的要多选。DataGridSelectionModeExtended/SelectedItem仍然只表示最后点中的那一条。多选的集合是SelectedItems。它不是依赖属性不能写{Binding SelectedItems}。删除前在SelectionChanged里把当前选中抄进 ViewModel 的一份列表命令删的是这份列表不是控件上的属性。privatevoidOnAlarmSelectionChanged(objectsender,SelectionChangedEventArgse){if(DataContextisnotDeviceViewModelvm)return;vm.ReplaceSelection(((DataGrid)sender).SelectedItems.CastAlarmRow());}没有选中时列表为空删除命令直接返回。不要用SelectedIndex ! -1代替这份列表。数字过滤和红框校验见08-值转换器.md。表的行虚拟化以及行样式和数据模板怎么分工见07-数据模板.md和06-资源与样式.md。在线、运行那种状态灯不是TextBlock也不是Ellipse堆在窗口上从10-依赖属性.md的StatusLamp做起。3. 后期应用放回四块骨架曲线区仍用Border占位。其余三块换成控件。状态栏的「启动」「停止」先只是按钮不写Click。StackPanelGrid.Row0Grid.ColumnSpan2OrientationHorizontalMargin12,8TextBlockText离线VerticalAlignmentCenter/TextBlockText待机Margin16,0,0,0VerticalAlignmentCenter/TextBlockText25.0Margin16,0,0,0FontSize20VerticalAlignmentCenter/ButtonContent启动Margin16,0,0,0Padding12,4/ButtonContent停止Margin8,0,0,0Padding12,4//StackPanelBorderGrid.Row1Grid.Column1Margin6,0,12,8Padding12StackPanelTextBlockText配方FontSize16/ComboBoxMargin0,12,0,0SelectedIndex0ComboBoxItemContent默认配方/ComboBoxItemContent高温/ComboBoxItemContent低温//ComboBoxTextBlockText上限Margin0,12,0,4/TextBoxText30/TextBlockText下限Margin0,12,0,4/TextBoxText10/ButtonContent保存Margin0,12,0,0Padding12,4HorizontalAlignmentLeft//StackPanel/BorderDataGridGrid.Row2Grid.ColumnSpan2Margin12,0,12,12AutoGenerateColumnsFalseIsReadOnlyTrueCanUserAddRowsFalseHeadersVisibilityColumnDataGrid.ColumnsDataGridTextColumnHeader时间Binding{Binding Time}Width160/DataGridTextColumnHeader内容Binding{Binding Message}Width*/DataGridTextColumnHeader值Binding{Binding Value}Width80//DataGrid.Columns/DataGrid运行后应看到状态下拉不动、温度是一块文本配方能切换三项、上下限能打字报警表只有表头没有空行可编辑。表是空的是对的行要等变更通知那篇往集合里加。若想在这一篇就看到报警文字同一格先改用ListBox加两条ListBoxItem。换成DataGrid时删掉这些静态项。按钮变灰这一篇可以写IsEnabledFalse看效果然后删掉。真正的灰亮跟着待机、运行、离线走用 Command 的CanExecute不要在界面上写死。4. 自测温度数值为什么用TextBlock上限为什么用TextBoxButton上的字写在Text还是Content三套配方切换用ListBox、ComboBox还是DataGrid报警表为什么要IsReadOnly和CanUserAddRowsFalse没有任何选中时SelectedIndex是多少参考答案温度是程序显示给操作员看的。上限是操作员要修改并保存的设定。Content。ComboBox。它表示当前这一套不占一大块列表也不需要多列。报警是历史记录。只读避免改字关掉新增行避免底部多出一个空行。-1。5. 练习用第 3 节替换主窗口里的状态栏、配方区和报警区。运行切换配方在上下限里打字。把报警区临时换成带两条ListBoxItem的ListBox点其中一行。再换回DataGrid。给「启动」加上IsEnabledFalse确认它变灰且点不到。确认完删掉这个属性。面试可能提问温度用 TextBlock上限用 TextBox依据是什么答温度是程序显示给操作员的。上限是操作员要改、并且会写回配方的。只读的数不要放进 TextBox。报警记录为什么用只读 DataGrid而不让操作员在格子里改答报警是已经发生的记录。IsReadOnly禁止改字CanUserAddRowsFalse去掉底部空行。列用AutoGenerateColumnsFalse自己定避免按对象属性长出多余列。三套配方的切换为什么用 ComboBox答操作员只需要选定当前这一套。ListBox 占一块列表DataGrid 适合多列记录都不适合「从几项里挑一个」。SelectedIndex为 -1 表示什么删除当前报警前要先看什么答表示没有选中项。删除前看SelectedItem是不是 null。不要只判断SelectedIndex ! -1就去取第 0 项。两种判断大多同步业务代码以SelectedItem这份对象为准没有选中就是 null。