Как я могу вручную сообщить о управлении WPF, нарисованным с владельцем, чтобы обновить/перерисоваться без выполнения меры или организовать проходы?

StackOverflow https://stackoverflow.com/questions/7801680

Вопрос

Мы делаем пользовательский рисунок в подклассе управления OnRender. Анкет Этот код рисунка основан на внешнем триггере и данных. Таким образом, всякий раз, когда запускается триггер, нам нужно повторно рендерировать контроль на основе этих данных. То, что мы пытаемся сделать, это выяснить, как заставить контроль повторно рендеринг, но не проходя через весь проход макета.

Как указано выше, большинство ответов, которые я видел Visual что лишает недействительного макета, которая принуждает новую меру и организует проходы, что очень дорого, особенно для очень сложных визуальных деревьев, как и наши. Но опять же, макет делает нет Изменения, а также визуализация. Единственное, что делает, это внешние данные, которые оказываются по -разному. Таким образом, это строго чистая проблема рендеринга.

Опять же, мы просто ищем простой способ сказать управление, что ему нужно повторно обрести OnRender. Анкет Я видел один «взлом», в котором вы создаете новый DependencyProperty и зарегистрируйте его на «AfficeSrender», который вы только что придаете в некоторую ценность, когда хотите обновить контроль, но меня больше интересует то, что происходит в реализации по умолчанию для этих свойств: что они призывают влиять на это поведение.


Обновлять:

Ну, похоже, нет такого звонка, как даже AffectsRender Флаг по-прежнему вызывает проход с проходом (согласно ответу ниже), но я опубликовал второй ответ, который показывает встроенное поведение, а также обходной флаг. Смотри ниже.

Это было полезно?

Решение 2

Хорошо, я отвечаю на это, чтобы показать людям, почему ответ Codenaked верен, но с звездочкой, если хотите, а также для обеспечения работы. Но в хорошем таблице я все еще отмечаю его, так как его ответ привел меня сюда.

ОБНОВЛЕНИЕ: Я с тех пор перенес принятый ответ здесь по двум причинам. Во -первых, я хочу, чтобы люди знали там является Решение этого (большинство людей читают только принятый ответ и движутся дальше) и два, учитывая, что у него есть представитель 25K, я не думаю, что он не возражает, если бы я верну его! :)

Вот что я сделал. Чтобы проверить это, я создал этот подкласс ...

public class TestPanel : DockPanel
{
    protected override Size MeasureOverride(Size constraint)
    {
        System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
        return base.MeasureOverride(constraint);
    }

    protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
    {
        System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
        return base.ArrangeOverride(arrangeSize);
    }

    protected override void OnRender(System.Windows.Media.DrawingContext dc)
    {
        System.Console.WriteLine("OnRender called for " + this.Name + ".");
        base.OnRender(dc);
    }

}

... что я изложил так (обратите внимание, что они вложены):

<l:TestPanel x:Name="MainTestPanel" Background="Yellow">

    <Button Content="Test" Click="Button_Click" DockPanel.Dock="Top" HorizontalAlignment="Left" />

    <l:TestPanel x:Name="InnerPanel" Background="Red" Margin="16" />

</l:TestPanel>

Когда я изменил размер окна, я получил это ...

MeasureOverride called for MainTestPanel.
MeasureOverride called for InnerPanel.
ArrangeOverride called for MainTestPanel.
ArrangeOverride called for InnerPanel.
OnRender called for InnerPanel.
OnRender called for MainTestPanel.

Но когда я позвонил InvalidateVisual На 'BeartestPanel' (в событии кнопки «нажимать») я получил это вместо этого ...

ArrangeOverride called for MainTestPanel.
OnRender called for MainTestPanel.

Обратите внимание, как ни одно из измерений не было вызвано, и был вызван только ArrangeOverride для внешнего контроля.

Это не идеально, как будто у вас есть очень тяжелый расчет внутри ArrangeOverride В вашем подклассе (который, к сожалению, мы делаем), который все еще выполняется (пере), но, по крайней мере, дети не падают на ту же участь.

Однако, если вы знаете, что ни у одного из контролей над ребенком нет свойства с набором битов AmffectSparentArrange (опять же, что мы делаем), вы можете пойти на один лучше и использовать Nullable Size В качестве флага, чтобы подавить логику ArrangeOverride от повторного входа, за исключением случаев, как это ...

public class TestPanel : DockPanel
{
    Size? arrangeResult;

    protected override Size MeasureOverride(Size constraint)
    {
        arrangeResult = null;
        System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
        return base.MeasureOverride(constraint);
    }

    protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
    {
        if(!arrangeResult.HasValue)
        {
            System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
            // Do your arrange work here
            arrangeResult = base.ArrangeOverride(arrangeSize);
        }

        return arrangeResult.Value;
    }

    protected override void OnRender(System.Windows.Media.DrawingContext dc)
    {
        System.Console.WriteLine("OnRender called for " + this.Name + ".");
        base.OnRender(dc);
    }

}

Теперь, если что-то конкретно не нужно повторно рассказать о логике соглашения (как это делает вызов для измерения), вы получаете только поставку, и если вы хотите явно заставить логику аранжировки, просто учесть размер, вызовите Invalidatevisual, а Боб-ваш дядя! :)

Надеюсь это поможет!

Другие советы

К сожалению, вы должны позвонить Недействителен, который вызывает InvalidateArrange внутренне. А OnRender Метод называется как часть фазы расположения, поэтому вам необходимо указать WPF перестроить элемент управления (что делает InvalidateArrange) и что ему необходимо перерисовать (что делает недействительнымвизуальным).

А FrameworkPropertyMetadata.AffectsRender опция просто говорит WPF позвонить InvalidateVisual Когда связанное свойство меняется.

Если у вас есть контроль (давайте назовем этот MainControl), который переопределяет на выставке и содержит несколько элементов управления потоками, то вызов недействительным Vistical может потребовать, чтобы элементы управления потомка были перестановлены или даже переоценили. Но я считаю, что у WPF есть оптимизация, чтобы предотвратить перегруппировку управления потоками, если их доступное пространство не изменилось.

Возможно, вы сможете обойти это, перемещая свою логику рендеринга в отдельный контроль (скажем, indestControl), что было бы визуальным ребенком MainControl. MainControl может добавить это в качестве визуального ребенка автоматически или как часть его ControlTemplate, но он должен быть самым низким ребенком в Z-порядке. Тогда вы можете разоблачить InvalidateNestedControl Метод типа на MainControl, который бы назвал InvalidateVisual на IntedControl.

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

Эффективно обновить изображение управления без изменения его размера. Использовать DrawingGroup. Анкет Вы создаете группу рисунков и вкладываете ее в DrawingContext в течение OnRender() а затем в любое время после этого вы можете Open() а DrawingGroup Чтобы изменить его визуальные команды рисования, и WPF будет автоматически и эффективно Повторно возместить эту часть пользовательского интерфейса. (Вы также можете использовать эту технику с RenderTargetBitmap Если вы предпочитаете иметь растровую карту, в которую вы можете вносить постепенные изменения, а не перерисовываться каждый раз)

Вот как это выглядит:

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();            
}

private void Render(DrawingContext drawingContext) {
    // put your render code here
}

Вот еще один взлом:http://geekswithblogs.net/newthingsilearndned/archive/2008/08/25/refresh-update-wpf-controls.aspx

Короче говоря, вы называете какой -нибудь фиктивного делегата на приоритетном диспетчерском диспетке.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top