WPF中。。DataGrid 实现时间控件和下拉框控件
DatePicker 和新的 DataGrid 行
用户与 DataGrid 中日期列的交互给我造成了很大的麻烦。 我通过将一个 Data Source 对象拖动到 WPF 窗口上,创建了一个 DataGrid。 设计器的默认行为是为该对象中的每个 DateTime 值创建一个 DatePicker。 例如,下面是为一个 DateScheduled 字段创建的列: <DataGridTemplateColumn x:Name=" dateScheduledColumn"
Header="DateScheduled" Width="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker
SelectedDate="{Binding Path=DateScheduled, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> 这一默认行为对编辑造成不便。 编辑时现有行不会更新。 DatePicker 不会在 DataGrid 中触发编辑,这表示数据绑定功能不会将所做的更改推送至基础对象。 通过向 Binding 元素添加 UpdateSourceTrigger 属性并将属性值设置为 PropertyChanged,可以解决这个特定的问题: <DatePicker
SelectedDate="{Binding Path= DateScheduled, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true,
UpdateSourceTrigger=PropertyChanged}" /> 不过,添加这些新行后,DatePicker 不能触发 DataGrid 编辑模式的问题变得更加严重。 在 DataGrid 中,新行由 NewRowPlaceHolder 表示。 首次编辑新行中的单元格时,编辑模式会在数据源中触发插入(再次说明,不是在数据库中,而是在内存中的基础源中)。 因为 DatePicker 不触发编辑模式,所以这不会发生。 因为我的行中的日期列恰好是第一列,所以我发现了这个问题。 我本来想使用它来触发该行的编辑模式的。 图 所示为一个新行,在其中的第一个可编辑列中输入了日期。 图 在新行占位符中输入日期值 但在编辑下一列中的值之后,前一编辑值丢失,如图 所示。 图 修改新行中 Task 列值之后日期值丢失 第一列中的键值变为 ,刚刚输入的日期值变为 //。 编辑 Task 列最终会使 DataGrid 在数据源中添加一个新实体。 ID 值变为整数(默认值 ),日期值变为 .NET 默认的最小日期 //。 如果我为此类指定过默认日期,则用户输入的日期将变为此类的默认日期,而不是 .NET 的默认日期。 请注意,Date Performed 列中的日期没有更改为其默认值。 这是因为 DatePerformed 是可以为 Null 的属性。 那么,现在用户是不是必须回去重新修复 Scheduled Date? 我相信用户肯定不愿意这样做。 这个问题困扰了我一段时间。 我甚至曾将该列改成 DataTextBoxColumn,但之后我必须处理 DatePicker 起保护作用的验证问题。 最后,WPF 团队的 Varsha Mahadevanset 给我指出了正确的道路。 通过利用 WPF 的组合性质,可以对此列使用两个元素。 DataGridTemplateColumn 不仅有 CellTemplate 元素,还有 CellEditingTemplate。 我没有要求 DatePicker 控件触发编辑模式,而只在已经编辑时使用 DatePicker。 为了在 CellTemplate 中显示日期,我切换到了 TextBlock。 下面是 dateScheduledCoumn 的新 XAML: <DataGridTemplateColumn x:Name="dateScheduledColumn"
Header="Date Scheduled" Width="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path= DateScheduled, StringFormat=\{0:d\}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding Path=DateScheduled, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn> 请注意,我不再需要指定 UpdateSourceTrigger。 我对 DatePerformed 列也进行了同样的更改。 现在,这些日期列一开始是简单文本,在您进入该单元格后才切换到 DatePicker,如图 所示。 图 DateScheduled 列同时使用 TextBlock 和 DatePicker 在新行上面的行中没有 DatePicker 日历图标。 但这还是有点不对。 开始编辑这一行时仍然会得到默认的 .NET 值。 这时,您就可以从在基础类中定义默认值受益。 我修改了 ScheduleItem 类的构造函数,使之将新对象初始化为当天日期。 如果从数据库检索到数据,它将覆盖该默认值。 我的项目使用了实体框架,因此类会自动生成。 不过,生成的类是分部类,这样,我就可以将此构造函数添加到附加的分部类中: public partial class ScheduleItem
{
public ScheduleItem()
{
DateScheduled = DateTime.Today;
}
} 现在,当我通过修改 DateScheduled 列开始在新行占位符中输入数据时,DataGrid 会为我创建一个新的 ScheduleItem,并且在 DatePicker 控件中显示默认值(当天日期)。 现在,当用户继续编辑此行时,输入的值会继续有效。 减少用户在编辑时需要点击的次数
两部分模板的一个弊端是必须点击单元格两次才能触发 DatePicker。 这会对数据输入人员造成不便,特别是对那些习惯于使用键盘输入数据而不使用鼠标的人员。 因为 DatePicker 位于编辑模板中,所以除非触发编辑模式,否则它不会获得焦点(这是默认行为)。 这是针对 TextBox 进行的设计,很适合 TextBox 使用。 但这种设计不太适用于 DatePicker。 可以结合使用 XAML 和代码来强制 DatePicker 在用户切换到该单元格时准备好键入。 首先,需要在 CellEditingTemplate 中添加一个 Grid 容器,使它成为 DatePicker 的容器。 然后,可以使用 WPF FocusManager 强制此 Grid 在用户进入该单元格时成为单元格焦点。 下面是作为 DatePicker 容器的新 Grid 元素: <Grid FocusManager.FocusedElement="{Binding ElementName= dateScheduledPicker}">
<DatePicker x:Name=" dateScheduledPicker"
SelectedDate="{Binding Path=DateScheduled, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true}" />
</Grid> 请注意,我为 DatePicker 控件提供了一个名称,并使用 FocusedElement Binding ElementName 指向了该名称。 现在请将注意力转到包含此 Date-Picker 的 DataGrid,注意,我添加了三个新属性(RowDetailsVisibilityMode、SelectionMode 和 SelectionUnit)和一个新的事件处理程序 (SelectedCellsChanged): <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="12,12,22,31"
Name="scheduleItemsDataGrid"
RowDetailsVisibilityMode="VisibleWhenSelected"
SelectionMode="Extended" SelectionUnit="Cell"
SelectedCellsChanged="scheduleItemsDataGrid_SelectedCellsChanged"> 对 DataGrid 进行的更改会启用当用户选择该 DataGrid 中的新单元格进行通知的功能。 最后,需要确保当用户执行此操作时 DataGrid 确实进入编辑模式,这会在 DatePicker 中向用户提供必要的光标。 scheduleItemsDataGrid_SelectedCellsChanged 方法将提供这最后一部分逻辑: private void scheduleItemsDataGrid_SelectedCellsChanged
(object sender,
System.Windows.Controls.SelectedCellsChangedEventArgs e)
{
if (e.AddedCells.Count == ) return;
var currentCell = e.AddedCells[];
string header = (string)currentCell.Column.Header; var currentCell = e.AddedCells[]; if (currentCell.Column ==
scheduleItemsDataGrid.Columns[DateScheduledColumnIndex])
{
scheduleItemsDataGrid.BeginEdit();
}
} 请注意,在类声明中,我将常量 DateScheduledColumnIndex 定义为 ,即该列在网格中的位置。 完成这些更改后,最终用户会很满意。 我费了很大心思才找出使 DatePicker 在 DataGrid 内出色工作的正确 XAML 和代码元素组合,希望这可以帮助您避免经历同样的困难。 现在,UI 以用户熟悉的方式工作了。 使受限 ComboBox 显示旧值
获取在 DataGridTemplateColumn 中对元素分层的价值之后,我再次考虑了另一个我几乎已经放弃的 DataGrid-ComboBox 列相关问题。 编写这一特定应用程序的目的是为了用旧数据替换旧应用程序。 旧应用程序允许用户不经太多控制输入数据。 在新应用程序中,客户端要求通过下拉列表对一些数据输入进行限制。 通过使用字符串集合很容易提供下拉列表的内容。 难点在于仍要显示旧数据,即使此数据不包含在新的限制列表中也是如此。 我首先尝试使用 DataGridComboBoxColumn: <DataGridComboBoxColumn x:Name="frequencyCombo"
MinWidth="" Header="Frequency"
ItemsSource="{Binding Source={StaticResource frequencyViewSource}}"
SelectedValueBinding=
"{Binding Path=Frequency, UpdateSourceTrigger=PropertyChanged}">
</DataGridComboBoxColumn> 在代码隐藏文件中定义源项: private void PopulateTrueFrequencyList()
{
_frequencyList =
new List<String>{"",
"Initial","2 Weeks",
"1 Month", "2 Months",
"3 Months", "4 Months",
"5 Months", "6 Months",
"7 Months", "8 Months",
"9 Months", "10 Months",
"11 Months", "12 Months"
};
} 此 _frequencyList 绑定到另一方法中的 frequencyViewSource.Source。 在无数种可能的 DataGridCombo-BoxColumn 配置中,我找不到任何办法来显示可能已经存储在数据库表的 Frequency 字段中的不同值。 我就不一一列举我试过的所有解决方法了,其中一个是将这些额外的值动态添加到 _frequencyList 底部,然后根据需要删除它们。 我并不喜欢这个解决方法,但恐怕我不得不接受它。 我知道编写 UI 的 WPF 分层方法必须为此提供一种机制,并且已经解决了 Date-Picker 问题,因此我意识到可以对 ComboBox 使用相似的方法。 这个技巧的第一部分是避免使用华而不实的 DataGridComboBoxColumn,而是使用更经典的将 ComboBox 嵌入 DataGridTemplateColumn 内部的方法。 然后,利用 WPF 的组合性质,可以像使用 DateScheduled 列一样对此列使用两个元素。 第一个元素是 TextBlock,用来显示值;第二个元素是 ComboBox,用于编辑目的。 图 显示了同时使用这两个元素的方式。 图 组合使用显示值的 TextBlock 和用于编辑的 ComboBox <DataGridTemplateColumn x:Name="taskColumnFaster"
Header="Task" Width="" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Task}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid FocusManager.FocusedElement=
"{Binding ElementName= taskCombo}" >
<ComboBox x:Name="taskCombo"
ItemsSource="{Binding Source={StaticResource taskViewSource}}"
SelectedItem ="{Binding Path=Task}"
IsSynchronizedWithCurrentItem="False"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn> TextBlock 与限制列表不存在依赖关系,因此能够显示数据库中存储的任何值。 不过,在编辑时就会用到 ComboBox,输入将限制为 frequencyViewSource 中的值。 允许用户在单元格获得焦点时编辑 ComboBox
同样,因为 ComboBox 在用户在单元格中单击两次后方可使用,因此,请注意我将 ComboBox 封装在一个 Grid 中以利用 FocusManager。 考虑到用户可能通过单击 Task 单元格而不是移至第一列的方式开始新行数据输入,我修改了 SelectedCellsChanged 方法。 唯一的更改是代码还检查当前单元格是否位于 Task 列中: private void scheduleItemsDataGrid_SelectedCellsChanged(object sender,
System.Windows.Controls.SelectedCellsChangedEventArgs e)
{
if (e.AddedCells.Count == ) return;
var currentCell = e.AddedCells[];
string header = (string)currentCell.Column.Header; if (currentCell.Column ==
scheduleItemsDataGrid.Columns[DateScheduledColumnIndex]
|| currentCell.Column == scheduleItemsDataGrid.Columns[TaskColumnIndex])
{
scheduleItemsDataGrid.BeginEdit();
}
}
WPF中。。DataGrid 实现时间控件和下拉框控件的更多相关文章
- FineReport——JS二次开发(隐藏下拉框控件的倒三角)
在对FR控件进行二次开发的过程中,需要自定义样式,比如下拉框控件带有自动检索的功能,但是又希望它的显示样式如同文本框一样,这时就需要隐藏多余的部分. 在对在线文档的查阅中可以发现很多选择器适用于多种控 ...
- DevExpress的下拉框控件ComboxBoxEdit怎样绑定键值对选项
场景 DevExpress的下拉框控件ComboBoxEdit控件的使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1028 ...
- DevExpress的下拉框控件ComboBoxEdit控件的使用
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- DevExpress的下拉框控件LookUpEdit的使用、添加item选项值、修改默认显示值
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- 几种常用的控件(下拉框 可选框 起止日期 在HTML页面直接读取当前时间)
下拉框 <div class="form-group"> <label class="col-xs-3 c ...
- 用MVC的辅助方法自定义了两个控件:“可编辑的下拉框控件”和“文本框日历控件”
接触MVC也没多长时间,一开始学的时候绝得MVC结构比较清晰.后来入了门具体操作下来感觉MVC控件怎么这么少还不可以像ASP.net form那样拖拽.这样设计界面来,想我种以前没学过JS,Jquer ...
- DropShadowEffect导致下拉框控件抖动
<!--<Border.Effect> <DropShadowEffect Direction="180" BlurRadius="1" ...
- android 开发-spinner下拉框控件的实现
Android提供实现下拉框功能的非常实用的控件Spinner. spinner控件需要向xml资源文件中添加spinner标签,如下: <Spinner android:id="@+ ...
- [原创]自己动手实现React-Native下拉框控件
因项目需要,自己动手实现了一个下拉框组件,最近得空将控件独立出来开源上传到了Github和npm. Github地址(求Star 求Star 求Star
随机推荐
- Java并发案例05---Master-Worker模式
Master-Worker 模式是常用的并行计算模式.它的核心思想是系统由两类进程协同工作,Master和Worker进程.Master负责接收和分配任务,Worker负责处理子任务.当各个Worke ...
- python中执行shell命令
查看输出结果 import os output = os.popen('cat 6018_gap_5_predict/solusion2/solusion2_0-1.txt | wc -l') pri ...
- 淡说Linux 的发展史
♦ 1 Linux的简单介绍 Linux与Windows一样都是一套OS(操作系统),Windows界面美观 ,普通用户很容易上手,点点鼠标就能搞定许多操作,而Linux生下来就是为程序员的,故精通 ...
- css实现等高布局 两栏自适应布局 三栏自适应布局
等高布局: HTML结构如下: <div class="wrapper"> <div class="box"> <h1>.. ...
- R中的apply族函数和多线程计算
一.apply族函数 1.apply 应用于矩阵和数组 # apply # 1代表行,2代表列 # create a matrix of 10 rows x 2 columns m <- ma ...
- Spark读写HBase时出现的问题--RpcRetryingCaller: Call exception
问题描述 Exception in thread "main" org.apache.hadoop.hbase.client.RetriesExhaustedException: ...
- ThinkPHP5入门(三)----模型篇
一.操作数据库 1.数据库连接配置 数据库默认的相关配置在项目的application\database.php中已经定义好. 只需要在模块的数据库配置文件中配置好当前模块需要连接的数据库的配置参数即 ...
- 错误的另一个常见原因是默认的安全组规则。default security group默认情况下不允许ICMP(ping命令使用的协议)
可以在openstack horizon界面中添加ICMP和ssh(TCP)规则,也可以通过命令行.命令行方式给默认安全组添加规则的方法如下: $ nova secgroup-add-rule def ...
- unordered_map 遇到 vector subscript out of range 的错误提示
错误类型 当调用unordered_map的函数的时候,会出现如下问题: 使用linux运行则会提示 float exeption(core dump) 原因 遇到vector subscript o ...
- Spring Boot 构建一个 RESTful Web Service
1 项目目标: 构建一个 web service,接收get 请求 http://localhost:8080/greeting 响应一个json 结果: {"id":1,&qu ...