WPF拖拽选时控件咨询:工时记录应用时间块拖拽实现
嘿,作为有C#和MVC背景转WPF的开发者,我完全懂你想要快速搞定这个工时记录界面的心情!针对你的场景,我整理了两种靠谱的解决方案:
现成可用的控件选项
如果你想省事儿,直接用成熟的第三方控件是最快的:
- Telerik RadScheduleView:这款商业控件完美支持自定义15分钟时间刻度,自带拖拽选择时间块的功能,还能轻松绑定数据源。你只需要把字节数组转成对应的时间区间模型,绑定到它的Appointments集合就行。唯一的小缺点是需要授权,但能帮你省掉大量自定义逻辑的工作量。
- DevExpress SchedulerControl:同样是商业控件,它的时间线视图可以直接配置15分钟刻度,拖拽选择、编辑时间块的逻辑都很成熟。数据绑定也很灵活,适配你把字节数组转模型的需求完全没问题。
- WPF Toolkit 扩展组件:如果想找免费的,这个开源工具包的Calendar相关组件可以作为基础,但原生功能不够完善,需要自己扩展刻度和拖拽逻辑,适合愿意花点时间折腾的情况。
自定义实现方案(不想用第三方控件的话)
其实自己动手实现也没那么难,毕竟你有C#基础,WPF的布局和事件处理和MVC前端逻辑有不少相通之处:
1. 先搭好网格布局
用ItemsControl结合UniformGrid来构建96个时间块(24小时×4个15分钟段):
- 计算好每个块的尺寸,比如把窗口高度均分96份,宽度可以固定或者自适应。
- 给每个时间块绑定对应的时间区间(比如00:00-00:15),同时绑定字节数组里的状态(0=未记录,1=已记录)来控制背景色。
2. 实现拖拽选择逻辑
这部分核心是处理鼠标事件:
- 监听
MouseDown:记录起始的时间块索引,标记进入选择状态。 - 监听
MouseMove:鼠标移动时计算当前所在的时间块索引,更新选中的区间,同时实时修改字节数组对应的位置(比如把选中索引对应的位设为1)。 - 监听
MouseUp:结束选择状态,触发数据保存的逻辑。
3. 优化数据绑定
把字节数组封装成一个ViewModel,比如DailyTimeRecordViewModel,里面暴露一个TimeBlocks集合,每个TimeBlock包含StartTime、EndTime、IsSelected属性。然后把字节数组的每一位映射到IsSelected,这样UI和数据就能双向绑定,不用手动操作UI元素。
举个简单的映射逻辑示例:
public ObservableCollection<TimeBlock> TimeBlocks { get; set; } = new ObservableCollection<TimeBlock>(); public void LoadFromByteArray(byte[] dailyData) { TimeBlocks.Clear(); for (int i = 0; i < 96; i++) { var startTime = TimeSpan.FromMinutes(i * 15); var endTime = startTime.Add(TimeSpan.FromMinutes(15)); // 字节数组每一位对应一个15分钟块 var isSelected = (dailyData[i / 8] & (1 << (i % 8))) != 0; TimeBlocks.Add(new TimeBlock { StartTime = startTime, EndTime = endTime, IsSelected = isSelected }); } }
4. 美化界面样式
用WPF的Style和Trigger来设置选中状态、鼠标悬停的效果,让界面更友好:
<Style TargetType="Border" x:Key="TimeBlockStyle"> <Setter Property="Background" Value="#F5F5F5"/> <Setter Property="BorderBrush" Value="#E0E0E0"/> <Setter Property="BorderThickness" Value="0.5"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#E8E8E8"/> </Trigger> <DataTrigger Binding="{Binding IsSelected}" Value="True"> <Setter Property="Background" Value="#2196F3"/> </DataTrigger> </Style.Triggers> </Style>
内容的提问来源于stack exchange,提问作者Guy
相关产品推荐
相关产品推荐

