WPF中如何绑定DateTime属性并显示其与当前时间的差值
WPF中如何绑定DateTime属性并显示其与当前时间的差值
看起来你想要在WPF列表里绑定DateTime类型的When属性,但显示它和当前时间的相对差值(比如“5分钟前”“2小时前”这种)对吧?我给你几个实用的方案,一步步来:
方案一:使用值转换器(IValueConverter)
这是WPF里处理“数据格式转换”场景最常规的做法,不需要修改你的核心数据模型,只需要新增一个转换器类:
1. 创建时间差值转换器
先写一个实现IValueConverter的类,负责把DateTime转换成友好的时间差字符串:
using System; using System.Globalization; using System.Windows.Data; public class TimeAgoConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is DateTime dateTime) { var timeSpan = DateTime.Now - dateTime; // 根据时间差返回不同的友好文本 if (timeSpan.TotalSeconds < 60) return "刚刚"; if (timeSpan.TotalMinutes < 60) return $"{(int)timeSpan.TotalMinutes}分钟前"; if (timeSpan.TotalHours < 24) return $"{(int)timeSpan.TotalHours}小时前"; if (timeSpan.TotalDays < 30) return $"{(int)timeSpan.TotalDays}天前"; // 超过1个月的话显示标准日期格式 return dateTime.ToString("yyyy-MM-dd HH:mm"); } return string.Empty; } // 反向转换用不到,直接返回null public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } }
2. 在XAML中引用并使用转换器
首先在Window的资源里注册这个转换器(注意要在XAML顶部添加local命名空间,指向你的转换器所在的项目命名空间):
<Window.Resources> <local:TimeAgoConverter x:Key="TimeAgoConverter"/> </Window.Resources>
然后在ListBox的ItemTemplate里绑定When属性时,指定这个转换器:
<ListBox x:Name="ltvInfos"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding When, Converter={StaticResource TimeAgoConverter}}" Margin="0 0 10 0"/> <TextBlock Text="{Binding What}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
补充:实现实时更新
上面的转换器只会在第一次绑定或When属性变化时计算一次,如果想要时间差自动刷新(比如过了一分钟自动从“刚刚”变成“1分钟前”),可以在MainWindow里加一个定时器:
private DispatcherTimer _updateTimer; public MainWindow() { InitializeComponent(); Informations = new ObservableCollection<Information> { new Information() { When = DateTime.Now, What = "A first information" }, new Information() { When = DateTime.Now, What = "Another information" }, new Information() { When = DateTime.Now, What = "An other information" } }; ltvInfos.ItemsSource = Informations; // 初始化定时器,每分钟触发一次绑定刷新 _updateTimer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(1) }; _updateTimer.Tick += (s, e) => { // 通知每个Information对象的When属性“变化”,触发转换器重新计算 foreach (var info in Informations) { info.OnPropertyChanged(nameof(Information.When)); } }; _updateTimer.Start(); }
(需要确保你的Information类的OnPropertyChanged方法是可访问的,比如改成public或者添加专门的刷新方法)
方案二:在数据模型中添加计算属性
如果你觉得转换器的实时刷新逻辑有点繁琐,也可以把时间差计算逻辑封装在Information类里:
1. 修改Information类
新增一个TimeAgo计算属性,同时通过定时器触发实时更新:
using System; using System.Runtime.CompilerServices; using System.Windows; public class Information : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private DateTime _when; public DateTime When { get => _when; set { _when = value; OnPropertyChanged(); // 当When变化时,同步通知TimeAgo更新 OnPropertyChanged(nameof(TimeAgo)); } } public string What { get; set; } // 计算属性:返回时间差友好文本 public string TimeAgo { get { var timeSpan = DateTime.Now - When; if (timeSpan.TotalSeconds < 60) return "刚刚"; if (timeSpan.TotalMinutes < 60) return $"{(int)timeSpan.TotalMinutes}分钟前"; if (timeSpan.TotalHours < 24) return $"{(int)timeSpan.TotalHours}小时前"; if (timeSpan.TotalDays < 30) return $"{(int)timeSpan.TotalDays}天前"; return When.ToString("yyyy-MM-dd HH:mm"); } } // 静态定时器,所有Information实例共用 private static DispatcherTimer _updateTimer; static Information() { _updateTimer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(1) }; _updateTimer.Tick += (s, e) => { // 这里可以维护一个实例列表来触发所有实例的TimeAgo更新,或者在MainWindow里遍历触发 }; _updateTimer.Start(); } }
然后在XAML里直接绑定TimeAgo属性即可:
<TextBlock Text="{Binding TimeAgo}" Margin="0 0 10 0"/>
备注:内容来源于stack exchange,提问作者R. Douan
相关产品推荐
相关产品推荐

