2010-04-05 1 views
7

У меня есть сложный элемент управления WPF, который рисует много примитивов в OnRender (это похоже на карту). Когда небольшая часть его изменяется, я бы хотел только повторно выдать команды рендеринга для затронутых элементов, вместо того, чтобы запускать весь OnRender. Хотя я отлично справляюсь со своей функцией функции OnRender при изменении размера или что-то еще, это недостаточно быстро для подсветки примитивов на основе мыши.Возможно ли InvalidateVisual() для данной области вместо всего элемента управления WPF?

В настоящее время единственным способом, которым я знаю, как заставить обновление экрана, является вызов InvalidateVisual(). Невозможно отправить в грязную область прямоугольника, чтобы сделать недействительным.

Является ли самая низкая гранулярность композиции экрана WPF элементом пользовательского интерфейса? Нужно ли мне делать рендеринг примитивов в промежуточную цель, а затем использовать это свойство InvalidateVisual() для обновления на экране?

+0

ли вы переопределить UIElement.OnRender рисовать все внутри UIElement, или же элемент по-прежнему имеют нормальные зрительные дети? –

ответ

0

WPF не работает совсем так, поэтому вы не можете аннулировать регионы. Тем не менее, есть некоторые оптимизации, которые могут быть сделаны. Есть Мера, Упорядочить, а затем Пропустить. Если элемент управления перемещается, но то, что на самом деле визуализирует, не изменяется, вы можете сказать WPF сделать только переход на аранжировку. Вы можете отключить эти отклонения от изменений значения свойств зависимостей с помощью FrameworkPropertyMetadata и FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx).

3

Если вы хотите написать пользовательские/составные элементы управления WPF, вы должны стараться избегать переопределения OnRender, насколько это возможно, особенно если вы планируете аннулировать его части. Это гораздо проще в использовании AddVisualChild + переопределить VisualChildrenCount + переопределить GetVisualChild + переопределения Меру & Устройте как это (псевдокод с 2 детьми):

private void BuildMyControls() 
{ 
    AddVisualChild(subControl1); 
    AddVisualChild(subControl2); 
} 

protected override int VisualChildrenCount 
{ 
    get 
    { 
    return 2; 
    } 
} 

protected override Visual GetVisualChild(int index) 
{ 
    if (index == 0) return subControl1; 
    if (index == 1) return subControl2; 
    return null; // should never be called in fact... 
} 

protected override Size MeasureCore(Size availableSize) 
{ 
    base.Measure... 
    BuildMyControls(); 
    .. measure them, probably call subControlX.Measure(...); 
} 

protected override void ArrangeCore(Rect finalRect) 
{ 
    base.ArrangeCore(finalRect); 
    ... arrange them, probably call subControlX.Arrange 
} 

С такого кода, вы можете аннулировать только одну часть, с чем-то вроде subControlX.InvalidateXXX();

+0

'MeasureCore' и' ArrangeCore' не являются допустимыми. Может быть, вы хотели сказать «MeasureOverride» и «ArrangeOverride» вместо этого? – dotNET

+0

@dotnet - Это зависит от того, произошли ли вы из UIElement или FrameworkElement –

0

Вы не должны использовать InvalidateVisual(), если размер вашего элемента управления не изменится, так как он вызывает довольно дорогостоящую переустановку вашего пользовательского интерфейса.

WPF является сохранен системы рисования. То есть OnRender() лучше назвать AccumulateDrawingObjects(). Это фактически накапливает дерево живых объектов рисования, которые должны выполняться только один раз для макета. Затем он использует эти объекты для рисования вашего пользовательского интерфейса, когда это необходимо. Чтобы изменить, как выглядит часть пользовательского интерфейса без повторной компоновки, некоторые объекты (например, DrawingGroup, RenderTargetBitmap и WriteableBitmap) могут быть обновлены после OnRender() в любое удобное для вас время.

Чтобы обновить часть вашего UI позже, обернуть эти команды в DrawingGroup и положить что объект в DrawingContext. Затем вы можете Open() и обновлять его, когда захотите, и WPF автоматически перекрасит эту часть пользовательского интерфейса.

Это то, что он выглядит следующим образом:

DrawingGroup backingStore = new DrawingGroup(); 

protected override void OnRender(DrawingContext drawingContext) {  
    base.OnRender(drawingContext);    

    Render(); // put content into our backingStore 
    drawingContext.DrawDrawing(backingStore); 
} 

// I can call this anytime, and it'll update my visual drawing 
// without ever triggering layout or OnRender() 
private void Render() {    
    var drawingContext = backingStore.Open(); 
    Render(drawingContext); 
    drawingContext.Close();    
}