WPF - Граница с OpacityMask / VisualBrush: утечки памяти - PullRequest
1 голос
/ 07 июня 2011

Краткое описание моего приложения:

Приложение, над которым я работаю, - это дизайнер поздравительных открыток. Представьте себе нечто, в котором есть фоновое изображение и неограниченное количество «слоев» (в частности, изображений), которые остаются над фоном и могут быть перемещены, изменены в размере, перемещены вперед и назад и т. Д. ...

Возможно также применить к этим слоям определенные формы, например, звезду, эллипс и т. Д., И после того, как карта сделана, ее можно сохранить в файл jpeg.

Проблема

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

Вот код UserControl каждого слоя:

<UserControl>
.....
    <Grid x:Name="_myGrid"  >
        <Border x:Name="im_the_problem" BorderThickness="0" OpacityMask="{Binding Path=MyMask.Data, Converter={StaticResource MaskConverter}}">
        <!-- My Image... -->
        </Border>
    </Grid>
</UserControl>

, где код MaskConverter следующий:

public class MaskConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter,
       System.Globalization.CultureInfo culture)
    {
        String maskData = value as String;
        if (maskData == null) 
            return null;
        if (maskData == "")
            return null;
        VisualBrush vb = new VisualBrush();
        vb.Visual = XamlReader.Parse(maskData) as Visual;
        return vb;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Параметр «MyMask.Data» - это XAML Path (это фигура, которую я применяю), которую я динамически загружаю из текстового файла, который содержит разные фигуры.

Итак, принцип таков: если у меня есть граница с именем * im_the_problem *, память НЕ освобождается. Если я прокомментирую * im_the_problem * (так что я просто буду иметь прямоугольные слои / картинки без фигур), все будет работать как шарм, без утечек памяти.

Проблема должна быть в OpacityMask + VisualBrush.

Я что-то не так делаю? Или есть известная проблема? Есть ли способ сделать то же самое (применить форму к изображению ..) по-другому?

Спасибо.

Ответы [ 3 ]

0 голосов
/ 21 февраля 2012

Вам нужно заморозить свой VisualBrush;)

0 голосов
/ 18 декабря 2017

У меня была эта проблема в шаблоне столбца DataGrid, где я использовал <Canvas><Path /></Canvas> (в качестве статического ресурса) в VisualBrush (также в качестве статического ресурса) и использовал его в качестве OpacityMask за Rectangle. Всякий раз, когда DataGrid перезагружался, Rectangle не выпускал VisualBrush ссылок на OpacityMask, я использовал инструмент профилирования памяти, чтобы показать, что все объекты VisualBrush используют большую часть памяти.

Я не понимаю, почему или как это произошло, но я рад, что я не одинок (даже если бы у меня была такая же проблема примерно 6,5 лет спустя ...).

Мой XAML был примерно таким:

<DataGrid.Resources>

    <Canvas x:Key="icon" ...>
        <Path ... />
    </Canvas>

    <VisualBrush x:Key="iconBrush" Stretch="Uniform" Visual="{StaticResource icon}" />

</DataGrid.Resources>

<DataGrid.Columns>

    <DataGridTemplateColumn>
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <Rectangle
                    Fill="{Binding Foreground, ElementName=myDataGrid}"
                    Width="14"
                    Height="14"
                    Margin="4"
                    Visibility="{Binding IconVisibility}"
                    OpacityMask="{StaticResource iconBrush}"
                />
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

    ...

</DataGrid.Columns>

Я читал, что настройка IsFrozen = true (выполненная с использованием этой техники: https://www.codeproject.com/Tips/72221/Freeze-brushes-directly-in-the-XAML-to-improve-you) поможет решить проблемы с памятью с помощью кистей, однако, похоже, это никак не отразилось. Weird.

Я думал, что поэкспериментирую, и рассуждал, что, если проблема протекала VisualBrush, я подумал, не имеет ли ее значение StaticResource, связанное с объектными ссылками, поэтому я изменил его на "принадлежащий" объект вот так:

    <DataGridTemplateColumn>
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <Rectangle
                    Fill="{Binding Foreground, ElementName=myDataGrid}"
                    Width="14"
                    Height="14"
                    Margin="4"
                    Visibility="{Binding IconVisibility}"
                >
                    <VisualBrush Stretch="Uniform" Visual="{StaticResource iconBrush}" />
                </Rectangle>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

Это исправило проблему! И я до сих пор не знаю почему - интересно, это ошибка в WPF?

В связи с этим я понял, что использование VisualBrush было излишним, поскольку я рендеринг просто Path - VisualBrush стоит дорого, потому что он отображает весь вид WPF - я также узнал от других документация о том, что Path сама по себе не требуется для рендеринга простых фигур, потому что сама является полными UIElement и FrameworkElement - которые являются "более тяжелыми" типами.

Я изменил свой код для сохранения пути в значении PathGeometry внутри GeometryDrawing статического ресурса, который загружается в DrawingBrush:

<GeometryDrawing x:Key="iconDrawing" Brush="Black" Geometry="..." /> 

<Rectangle
    Fill="{Binding Foreground, ElementName=myDataGrid}"
    Width="14"
    Height="14"
    Margin="4"
    Visibility="{Binding IconVisibility}"
    OpacityMask="{StaticResource iconBrush}"
>
    <DrawingBrush Stretch="Uniform" Drawing="{StaticResource iconDrawing}" />
</Rectangle>

Это также сказалось на использовании памяти и, надеюсь, производительности.

В вашем проекте я вижу, что вы не используете информацию о пути в качестве ресурса, но применяется та же техника: загрузите ваш путь в PathGeometry (точнее, StreamGeometry объект, который еще быстрее и предназначен для неизменной геометрии) и установите это как Drawing для DrawingBrush.

0 голосов
/ 27 июля 2011

Возможно, вы сможете попробовать связать MyMask.Data с фактическим Path.Data и установить Path.Fill для ImageBrush, созданного из изображения?

...