WPF - 带OpacityMask / VisualBrush的边框:内存泄漏

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF - 带OpacityMask / VisualBrush的边框:内存泄漏相关的知识,希望对你有一定的参考价值。

关于我的应用的简短说明:

我正在做的应用就是这样一个贺卡设计师。想象一下,有一个背景图像,以及无限数量的“图层”(特别是图片),它们可以在背景中移动,可以移动,调整大小,前后移动等等......

也可以将特定形状应用于这些层,如星形,椭圆形等。制作卡片后,可以保存为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路径(我正在申请的形状),我从包含不同形状的文本文件中加载。

所以,原则是如果我有名为* im_the_problem *的边框,则不释放内存。如果我评论* im_the_problem *(所以我只有矩形图层/图片没有形状)一切都像魅力一样,没有内存泄漏。

问题应该在OpacityMask + VisualBrush中。

难道我做错了什么?或者是否存在已知问题?有没有办法以不同的方式做同样的事情(将形状应用于图片......)?

谢谢。

答案

您可以尝试将MyMask.Data绑定到实际的Path.Data,并将Path.Fill设置为从图像创建的ImageBrush?

另一答案

你需要冻结你的VisualBrush;)

另一答案

我在DataGrid的列模板中遇到了这个问题,我将<Canvas><Path /></Canvas>(作为静态资源)用于VisualBrush(也是静态资源),并将其用作OpacityMaskRectangle。每当重新加载DataGrid时,Rectangle都不会释放VisualBrushOpacityMask的引用,我使用内存分析器工具来揭示所有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)将有助于Brushes的内存问题,但是这看起来根本没有任何影响。奇怪的。

我以为我会进行实验而且我推断如果问题是泄漏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本身不是必需的渲染简单的形状,因为它本身是一个完整的UIElementFrameworkElement - 这是“更重”的类型。

我更改了我的代码,将路径存储在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对象,它更快,并且用于不可变几何)并设置作为DrawingDrawingBrush

以上是关于WPF - 带OpacityMask / VisualBrush的边框:内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

WPF实现渐变淡入淡出的动画效果

如何在WPF控件上应用简单的褪色透明效果?

在WPF中使用另一个控件作为不透明蒙板?

WPF和Expression Blend开发实例:Loading动画

如何使用qt-component OpacityMask

在 WPF 中使用另一个控件作为不透明蒙版?