Ленивый рендеринг рисунка Визуальный
Я пытаюсь изменить пользовательский элемент WPF для использования визуального дерева, чтобы можно было изменять фоновые слои без необходимости отбрасывать весь оставшийся рендер. В то же время, на слой может повлиять более чем одно свойство, поэтому я хотел бы лениво повторить рендеринг, если несколько свойств будут изменены в одном цикле обновления экрана. Вот что я сделал.
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
private readonly DrawingVisual textLayer = new DrawingVisual();
bool textLayerReady;
protected override Visual GetVisualChild(int index)
{
switch (index)
{
case 0:
if (!textLayerReady)
{
using (var textContext = textLayer.RenderOpen())
RenderTextLayer(textContext);
}
return textLayer;
default:
throw new ArgumentOutOfRangeException("index");
}
}
Кажется, работает правильно, но в конструкторе я получаю:
InvalidOperationException: невозможно вызвать этот API во время обратного вызова OnRender. Во время OnRender могут выполняться только те операции рисования, которые рисуют содержимое визуала.
Я полагаю, что во время выполнения процесс макета вызывает GetVisualChild
до фактического рендера, а дизайн холста работает по-другому?
Это разумная попытка? Как я должен запустить рендеринг детского рисунка, чтобы убедиться, что это происходит в законное время?
2 ответа
Сами визуалы генерируют исключения, если они изменяются во время цикла рендеринга, а дизайнеры выбирают визуалы во время цикла рендеринга без предварительной обработки аранжировки.
Тем не менее, совершенно законно изменять содержимое DrawingGroup
во время рендера, даже если это DrawingGroup
является членом Visual
,
Нужно просто добавить DrawingGroup
к DrawingVisual
один раз в конструкторе:
DrawingGroup textLayer = new DrawingGroup();
DrawingVisual textVisual = new DrawingVisual();
using (DrawingContext textContext = textVisual.RenderOpen())
textContext.DrawDrawing(textLayer);
Затем позже можно лениво заменять контент, когда он действительно необходим для рендеринга, при этом сохраняя преимущества сохраненного режима (не восстанавливая "списки инструкций векторной графики", как его называет WPF, для неизмененных слоев).
protected override Visual GetVisualChild(int index)
{
switch (index)
{
case 0:
if (!textLayerReady)
{
using (var textContext = textLayer.Open())
RenderTextLayer(textContext);
}
return textVisual;
default:
throw new ArgumentOutOfRangeException("index");
}
}
Вы можете предотвратить рендеринг во время разработки, обнаружив его соответствующим образом
так что, если я немного изменю ваш код, он должен вести себя нормально и в дизайнере
private readonly DrawingVisual textLayer = new DrawingVisual();
bool textLayerReady;
protected override Visual GetVisualChild(int index)
{
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
return; //do not perform custom logic during design time
}
switch (index)
{
case 0:
if (!textLayerReady)
{
using (var textContext = textLayer.RenderOpen())
RenderTextLayer(textContext);
}
return textLayer;
default:
throw new ArgumentOutOfRangeException("index");
}
}