Задача состоит в перерисовке только той части части страницы, которую затронули изменения модели. Проблема же состоит в том, что мы не можем изменить код модели и внедрить в нее механизм уведомления, структура модели является иерархической, а коллекции могут быть изменены или заменены целиком.
Например мы подписаны на изменение размера всех элементов первого параграфа, а параграф затем заменяется другим. Нам необходимо отписаться от всех элементов "старого" параграфа, и подписаться на все элементы "нового" параграфа, а также инвалидировать области всех элементов как старого, так и нового параграфа.
Итак модель:
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;
}
}
Здесь мы отслеживаем изменение размера страницы и инвалидируем всю область страницы до и после изменения. Довольно много кода, а учтено только изменение одного свойства. Если брать во внимание коллекции, изменение состава коллекций, необходимость переподписывания на изменения при замене одного объекта другим, то решение, даже в таком простом случае как наша исходная модель, становится крайне громоздким.
Далее мы определим протокол уведомления об изменении коллекций и приступим к построению решения.
Комментариев нет:
Отправить комментарий