Как уведомить, что представление должно получить новое значение вычисляемого поля - PullRequest
0 голосов
/ 02 января 2012

Я работаю над приложением WP7, которое отображается несколько раз на одной странице. У меня есть код, который имеет ObservableCollection объектов. Каждый объект имеет вычисляемое свойство, которое использует DateTime.Now для определения времени, отображаемого на странице. Я не могу понять, как «уведомить», что свойство изменилось, так как свойство не изменяется, меняется текущее время (только один раз в секунду). Есть идеи? Вот суть того, что у меня есть:

//my business object
public class Widget
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    private DateTime? _start;
    public DateTime? Start
    {
        get { return _start; }
        set { _start = value; }
    }

    public TimeSpan? TimeSinceStart
    {
        get { return Start.HasValue ? DateTime.Now - Start.Value : default(TimeSpan); }
    }
}

//my viewmodel
public class WidgetDisplayerViewModel : BaseViewModel
{
    public WidgetDisplayerViewModel()
    {
        TimeUpdateTimer.Tick += new EventHandler(TimeUpdateTimer_Tick);
        TimeUpdateTimer.Interval = new TimeSpan(0, 0, 1);
        TimeUpdateTimer.Start();
    }

    public WidgetDisplayerViewModel(string selectedCategory) : this()
    {
        Category = MockDataService.GetCategory(selectedCategory);
        Category.Widgets = MockDataService.GetWidgets(selectedCategory).ToObservableCollection();
    }

    public DispatcherTimer TimeUpdateTimer = new DispatcherTimer();

    private DateTime _currentTime;
    public DateTime CurrentTime
    {
        get { return _currentTime; }
        set {
            _currentTime = value;
            NotifyPropertyChanged("CurrentTime");
        }
    }


    public Category Category { get; set; }

    void TimeUpdateTimer_Tick(object sender, EventArgs e)
    {
        CurrentTime = DateTime.Now;
    }
}

И затем представление очень простое, и ему просто нужно отобразить CurrentTime, а затем для каждого виджета в коллекции, которое требуется для отображения TimeSinceStart. Текущее время обновляется таймером каждую секунду, и это распространяется на представление. Это легко, потому что таймер устанавливает его, и поэтому у меня есть шанс вызвать NotifyPropertyChanged ("CurrentTime"), но как бы я "уведомил", что все получатели TimeSinceStart должны быть вызваны, чтобы обновить вычисленное значение для каждого виджета, так как Я их не настраиваю?

Спасибо!

Ответы [ 4 ]

1 голос
/ 02 января 2012

Вам придется вручную обновить свойство так или иначе.Я вижу, у вас уже есть таймер, тикающий каждую секунду.Поэтому я могу предложить вам два решения:

1 / Определить метод "UpdateTime" в объекте Widget.В этом методе вызовите NotifyPropertyChanged("TimeSinceStart").Когда таймер тикает, перечислите список виджетов и вызовите метод UpdateTime для каждого.

2 / Создайте глобальный объект, реализующий интерфейс INotifyPropertyChanged и содержащий значение CurrentTime.Сделайте так, чтобы каждый из ваших объектов Widget подписывался на событие PropertyChanged этого глобального класса, чтобы знать, когда обновляется время.Затем, когда событие срабатывает, позвоните NotifyPropertyChanged("TimeSinceStart").

1 голос
/ 03 января 2012

Это может быть сложно, и может очень быстро запутаться.

Я бы посоветовал вам придерживаться вашего текущего подхода, предусматривающего использование только одного таймера, который инициализируется в основной модели представления. Затем вам нужно задать себе вопрос - относится ли возраст (TimeSinceStart) виджета к виджету или он предназначен исключительно для демонстрации / в информационных целях? Является ли это основной информацией, которую каждый виджет должен хранить в течение своей жизни?

Мне кажется, это только для демонстрации. Поэтому я предлагаю следующее: как только вы позвоните GetWidgets, вы можете перечислять каждый виджет и оборачивать его в отдельную тонкую модель представления. Конструктор для этой модели представления принимает два параметра - таймер из основной модели представления и виджет. Затем вы подписываетесь на событие таймера Tick и после этого уведомляете, что свойство TimeSinceStart изменилось.

public class WidgetWrapper : INotifyPropertyChanged
{
    public WidgetWrapper(DispatcherTimer timer, Widget widget)
    {
        _widget = widget;
        timer.Tick += TimerTick;
    }

    private void TimerTick(object sender, EventArgs e)
    {
        OnPropertyChanged("TimeSinceStart");
    }

    public Widget Widget { get { return _widget; } }

    public TimeSpan? TimeSinceStart
    {
        get { return _widget.Start.HasValue ? DateTime.Now - _widget.Start.Value : default(TimeSpan); }
    } 

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private readonly Widget _widget;
}


public class WidgetDisplayerViewModel : BaseViewModel
{

    public WidgetDisplayerViewModel(string selectedCategory) : this()
    {
        Category = MockDataService.GetCategory(selectedCategory);
        var wrappedWidgets = new ObservableCollection<WidgetWrapper>();
        MockDataService.GetWidgets(selectedCategory).ForEach(widget => wrappedWidgets.Add(new WidgetWrapper(TimeUpdateTimer, widget)));
        Category.Widgets = wrappedWidgets;
    }
}

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

0 голосов
/ 03 января 2012

Подписаться на все виджеты на CurrentTime PropertyChanged событие в Widget конструкторе

 private Widget()
 {
      App.ViewModel.PropertyChanged += (s, e) =>
      {
           if (e.PropertyName.Equals("CurrentTime")
           {
                NotifyPropertyChanged("TimeSinceStart");
           }
      };
 }
0 голосов
/ 02 января 2012

Вызовите метод NotifyPropertyChanged для указанного свойства.

public DateTime CurrentTime
{
    get { return _currentTime; }
    set {
        _currentTime = value;
        NotifyPropertyChanged("CurrentTime");
        NotifyPropertyChanged("TimeSinceStart");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...