Обновление относительного времени с помощью WPF DataTemplate - PullRequest
1 голос
/ 16 декабря 2009

Я использую DataTemplate для отображения элементов (из класса через привязку данных) в списке. Этот класс также содержит дату и время, и я использую конвертер для преобразования этой даты / времени в относительное время (xx минут назад), которое затем отображается в TextBlock. Пока все отлично.

Проблема в том, что я не знаю, как обновлять это относительное время (все они привязаны к сгенерированному значению, например, «1 секунда назад»). Я мог бы использовать ListBox.Items.Refresh (), но он также перезапускает анимации, которые я настроил для элементов.

Есть идеи?

Заранее спасибо!

Ответы [ 5 ]

0 голосов
/ 20 апреля 2016

Я решил опубликовать собственное решение, так как считаю, что оно самое простое и требует гораздо меньше кода, чем другие, которые я видел.

//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}}"/>

И для переключения, должен ли объект отмечаться галочкой, вы можете определить дополнительный конструктор или флаг в классе объекта, указывающий, включено ли такое поведение. Таким образом, если ваше приложение поддерживает изменение формата даты, вы можете просто выполнить итерации ваших объектов, проверить каждый флаг и удалить (или поменять местами) существующий конвертер привязки.

0 голосов
/ 15 октября 2010

У меня была такая же проблема только сейчас, и я решил ее, создав 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)

0 голосов
/ 16 декабря 2009

Вам нужно реализовать INotifyPropertyChanged или DependencyProperty для свойства, к которому вы привязываете, чтобы увидеть обновления.

DependencyProperty является предпочтительным методом в WPF, поскольку он приведет к повышению производительности во время выполнения. Вот документация , включая пример того, как ее создать.

0 голосов
/ 17 декабря 2009

Хорошо, это может быть не самое элегантное решение (я в этом уверен), но сейчас я сделал это с приемлемыми результатами, используя событие ScrollChanged в ListBox, чтобы просто добавить 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.
    }
}
0 голосов
/ 16 декабря 2009

Пусть ваша модель реализует INotifyPropertyChanged. Если ваша двусторонняя привязка установлена ​​правильно, вызовите OnPropertyChanged, передав ему имя свойства всякий раз, когда изменяется свойство, которое вы хотите обновить. Это предупредит любого, кто просматривает изменения в этом свойстве (т. Е. Ваше мнение, следовательно, требование двухстороннего связывания), что значение изменилось и его необходимо обновить.

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

Обновление:

Используйте таймер. Я не собираюсь красть источник у Дэйва, так что вот ссылка на его ответ на очень похожий вопрос. В вашем методе timer_Tick проведите расчет относительного времени. Это будет обновлять ваш графический интерфейс каждую секунду.

...