【问题标题】:Updating relative time with WPF DataTemplate使用 WPF DataTemplate 更新相对时间
【发布时间】:2010-12-27 00:19:27
【问题描述】:

我正在使用 DataTemplate 在列表框中显示项目(来自通过数据绑定的类)。此类还包含日期和时间,我正在使用转换器将此日期/时间转换为相对时间(xx 分钟前),然后显示在 TextBlock 中。到目前为止一切都很好。

问题是我不知道如何更新这个相对时间(它们都停留在生成的值上,例如“1 秒前”)。我可以使用 ListBox.Items.Refresh(),但这也会重新运行我为项目设置的动画。

有什么想法吗?

提前致谢!

【问题讨论】:

  • 好吧,这 INotifyPropertyChanged 和 DependencyProperty 的东西太让我头疼了,所以我稍微改变了 UI 来解决这个问题。可能会在稍后的某个时间回到这个。谢谢:)

标签: c# .net wpf .net-3.5 datatemplate


【解决方案1】:

您需要为要绑定的属性实现INotifyPropertyChangedDependencyProperty 才能看到更新。

DependencyProperty 是 WPF 中的首选方法,因为它会在运行时带来更好的性能。 Here is the documentation,包括一个如何创建的示例。

【讨论】:

    【解决方案2】:

    让你的模型实现INotifyPropertyChanged。如果您的双向绑定设置正确,请在您想要更新的属性更改时调用OnPropertyChanged 将属性名称传递给它。这将提醒正在查看该属性更改的任何人(即您的视图,因此双向绑定要求)该值已更改并且需要更新。

    public string Name
    {
        get { return m_Name; }
        set 
        {
            OnPropertyChanged(Name);
            m_Name = value; 
        }
    }
    

    更新:

    使用计时器。我不会从 Dave 那里窃取源代码,所以这里有一个指向他的answer 的链接,指向一个非常相似的问题。在您的 timer_Tick 方法中进行相对时间计算。这将每秒更新您的 GUI。

    【讨论】:

    • 但是如果每个项目的时间不变,OnPropertyChanged 会起作用吗? (这是该物品在服务器上的创建时间)
    • 所以模型上的时间属性不会改变。我假设您使用 DateTime.Now 进行相对计算,并且您只想主动更新计算,对吗?
    • 关于计时器:这么快的计时器不会对应用程序的资源使用造成巨大损失吗?无论如何,使用虚拟化 ScrollChanged 的​​技术(每次滚动或添加可见项目时都会调用)对我来说已经足够好了。感谢您的帮助:)
    • 这不会是一个“消耗”,它每秒只调用一次。而且,由于您似乎是新来的,请您将建议的答案之一标记为您接受的答案。它可以帮助其他人知道问题何时结束,并为那些在未来关注问题的人提供良好的指导,了解哪些有效,哪些无效。谢谢和欢迎=D
    【解决方案3】:

    好吧,这可能不是最优雅的解决方案(我很确定),但现在我已经使用 ListBox 的 ScrollChanged 事件简单地为每个可见项目的时间增加 1 毫秒,从而获得了可接受的结果,这导致更新的相对时间;)每次我向列表框添加内容时都会调用代码,并且只影响当前可见的项目(有点像 VirtualizingStackPanel):)

    int VO = 0; // I think that this was protection for when a load of items are added at the beginning. Maybe you can do fine without it.
    private void HomeList_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        int v = (int)e.VerticalOffset;
        if (HomeList.Items.Count > 0 && v != VO) // Maybe you can do fine without VO.
        {
            for (int i = 0; i < e.ViewportHeight; i++)
            {
                // Add 1 millisecond to the item's time here
            }
            VO = v; // Maybe you can do fine without VO.
        }
    }
    

    【讨论】:

    • 您根本不应该更改 DateTime 值。你需要的是一个转换器。
    【解决方案4】:

    我刚才遇到了同样的问题,我通过为这些项目创建一个 ViewModel 来解决它。

    public class MyItem
    {
        public DateTime { get; set; }
    }
    
    public class MyItemViewModel : INotifyPropertyChanged
    {
        private string relativeTime;
        public string RelativeTime
        {
            get { return relativeTime; }
            set
            {
                relativeTime = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("RelativeTime"));
            }
        }
    
        public DateTime Date { get; set; }
    
        public static implicit operator MyItemViewModel(MyItem item)
        {
            return new MyItemViewModel { Date = item.Date }
        }
    }
    

    然后使用计时器更新它们。

    updateRelativeTimeString = new Timer(s =>
        Items.ForEach(
             item => item.RelativeTime = item.Date.ToRelativeTime()),
        null,
        0,
        5000);
    

    使用两种扩展方法(IEnumerable.ForEach 和 DateTime.ToRelativeTime)

    【讨论】:

    • 使用计时器来引发属性更改事件并使用绑定到该值的转换器不是更有意义吗?只要计时器引发 PropertyChanged 事件,转换器就会自动选择何时转换并输出新值。
    【解决方案5】:

    我决定发布我自己的解决方案,因为我觉得它是最简单的,并且需要的代码比我见过的其他解决方案少得多。

    //AbstractViewModel implements INotifyPropertyChanged
    public class MyObject : AbstractViewModel
    {
        private DateTime date = DateTime.UtcNow;
        public DateTime Date
        {
            get
            {
                return date;
            }
            set
            {
                date = value;
                OnPropertyChanged("Date");
            }
        }
    
        public MyObject()
        {
            Timer t = new Timer();
            t.Interval = 1000; //Update every second
            t.Elapsed += T_Elapsed;
            t.Enabled = true;
        }
    
        private void T_Elapsed(object sender, ElapsedEventArgs e)
        {
            OnPropertyChanged("Date");
        }
    }
    

    然后您将在转换器内部执行相对时间操作:

    using System;
    using System.Globalization;
    using System.Windows.Data;
    
    namespace MyConverters
    {
        [ValueConversion(typeof(DateTime), typeof(string))]
        public class RelativeTimeConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                DateTime Date = (DateTime)value;
                if (Date == null) return "never";
                return Utility.RelativeTime(Date);
            }
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return null;
            }
        }
    }
    

    并将转换器添加到绑定:

    <Run Text="{Binding Date, Converter={StaticResource RelativeTimeConverter}}"/>
    

    要切换对象是否应该打勾,您可以在对象类中定义一个额外的构造函数或标志,指示是否启用了此类行为。这样,如果您的应用程序支持更改日期格式,您可以简单地迭代您的对象,检查每个标志,并删除(或交换)绑定的现有转换器。

    【讨论】:

      猜你喜欢
      • 2015-07-15
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 1970-01-01
      • 2019-06-14
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      相关资源
      最近更新 更多