Задача состоит в перерисовке только той части части страницы, которую затронули изменения модели. Проблема же состоит в том, что мы не можем изменить код модели и внедрить в нее механизм уведомления, структура модели является иерархической, а коллекции могут быть изменены или заменены целиком.
Например мы подписаны на изменение размера всех элементов первого параграфа, а параграф затем заменяется другим. Нам необходимо отписаться от всех элементов "старого" параграфа, и подписаться на все элементы "нового" параграфа, а также инвалидировать области всех элементов как старого, так и нового параграфа.
Итак модель:
class Page:ObjectRoot { private Size _pageSize; private IList<Block> _items; public Size Size { get { return _pageSize; } set { UpdateProperty("Size", ref _pageSize, value); } } public IList<Block> Items { get { return _items; } set { UpdateProperty("Items", ref _items, value); } } } class Block:ObjectRoot { private Rectangle _rect; private IList<TextItem> _items; public Rectangle Rect { get { return _rect; } set { UpdateProperty("Rect", ref _rect, value); } } public IList<TextItem> Items { get { return _items; } set { UpdateProperty("Items", ref _items, value); } } } class TextItem : ObjectRoot { private Rectangle _rect; private string _text; public Rectangle Rect { get { return _rect; } set { UpdateProperty("Rect", ref _rect, value); } } public string Text { get { return _text; } set { UpdateProperty("Text", ref _text, value); } } }
В этой модели ObjectRoot предоставляет обобщенный механизм уведомлений об изменении значений свойств:
class ObjectRoot { public event PropertyChanged PropertyChanged; protected void UpdateProperty<T>(string name, ref T holder, T newValue) { if (Equals(newValue, holder)) return; var oldValue = holder; holder = newValue; RaisePropertyChanged(new PropertyChangedArgs(name, oldValue, newValue)); } protected void RaisePropertyChanged(PropertyChangedArgs args) { OnPropertyChanged(args); } protected virtual void OnPropertyChanged(PropertyChangedArgs args) { var handler = PropertyChanged; if(handler != null) handler(this, args); } } public delegate void PropertyChanged(object sender, PropertyChangedArgs args); public class PropertyChangedArgs : EventArgs { public PropertyChangedArgs(string name, object oldValue, object newValue) { PropertyName = name; OldValue = oldValue; NewValue = newValue; } public string PropertyName { get; private set; } public object OldValue { get; private set; } public object NewValue { get; private set; } }
Наиболее очевидное, "наивное" решение проблемы состоит в создании обертки для каждого класса модели следующего вида:
class NaiveObserver { private readonly Page _page; private readonly Action<Rectangle> _invalidateHandler; public NaiveObserver(Page page, Action<Rectangle> invalidateHandler) { _page = page; _invalidateHandler = invalidateHandler; page.PropertyChanged += PageSizeHandler; } private void PageSizeHandler(object sender, PropertyChangedArgs args) { if (args.PropertyName == "Size") { _invalidateHandler(new Rectangle(Point.Empty, (Size) args.OldValue)); _invalidateHandler(new Rectangle(Point.Empty, (Size) args.NewValue)); } } public void Stop() { _page.PropertyChanged -= PageSizeHandler; } }
Здесь мы отслеживаем изменение размера страницы и инвалидируем всю область страницы до и после изменения. Довольно много кода, а учтено только изменение одного свойства. Если брать во внимание коллекции, изменение состава коллекций, необходимость переподписывания на изменения при замене одного объекта другим, то решение, даже в таком простом случае как наша исходная модель, становится крайне громоздким.
Далее мы определим протокол уведомления об изменении коллекций и приступим к построению решения.
Комментариев нет:
Отправить комментарий