-->

How to freeze freezable objects that cannot be fro

2019-06-20 15:12发布

问题:

In my scenario I want to freeze a non changing BitmapCacheBrush before I want to render it in a background task. Unfortunately I receive the error "This Freezable cannot be frozen". Is there any workaround or hacky way freeze also not freezable objects? Maybe it is possible to set the right properties via reflection to reach this goal? Thank you guys in advance.

Edit: (My sample code as requested)

 public static class ext
{
    public static async Task<BitmapSource> RenderAsync(this Visual visual)
    {
        var bounds = VisualTreeHelper.GetDescendantBounds(visual);

        var bitmapCacheBrush = new BitmapCacheBrush(visual);
        bitmapCacheBrush.BitmapCache = new BitmapCache();

        // We need to disconnect the visual here to make the freezable freezable :). Of course this will make our rendering blank
        //bitmapCacheBrush.Target = null;

        bitmapCacheBrush.Freeze();

        var bitmapSource = await Task.Run(() =>
        {
            var renderBitmap = new RenderTargetBitmap((int)bounds.Width,
                                                         (int)bounds.Height, 96, 96, PixelFormats.Pbgra32);

            var dVisual = new DrawingVisual();
            using (DrawingContext context = dVisual.RenderOpen())
            {

                context.DrawRectangle(bitmapCacheBrush,
                                      null,
                                      new Rect(new Point(), new Size(bounds.Width, bounds.Height)));
            }

            renderBitmap.Render(dVisual);
            renderBitmap.Freeze();
            return renderBitmap;
        });

        return bitmapSource;
    }

}

回答1:

First, you need to figure out WHY you can't freeze it. Get into the registry and set ManagedTracing to 1 (if you have to make it, it's a REG_DWORD type). I suggest you add it to your Favorites in regedit so you can quickly get to it when you need to turn it on/off.

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Tracing\WPF\ManagedTracing

When you try to freeze the BitmapCacheBrush or check the bool property BitmapCacheBrush.CanFreeze, you will get a warning in the Output tab in visual studio telling you what the problem is.

I made a test case based off code from https://blogs.msdn.microsoft.com/llobo/2009/11/10/new-wpf-features-cached-composition/

And the warning it gave me was:

System.Windows.Freezable Warning: 2 : CanFreeze is returning false because a DependencyProperty on the Freezable has a value that is a DispatcherObject with thread affinity; Freezable='System.Windows.Media.BitmapCacheBrush'; Freezable.HashCode='29320365'; Freezable.Type='System.Windows.Media.BitmapCacheBrush'; DP='Target'; DpOwnerType='System.Windows.Media.BitmapCacheBrush'; Value='System.Windows.Controls.Image'; Value.HashCode='11233554'; Value.Type='System.Windows.Controls.Image'

BitmapCacheBrush.Target is of type Visual and all Visuals are derived from DependencyObject which is derived from DispatcherObject. And according to https://msdn.microsoft.com/en-us/library/ms750441(v=vs.100).aspx#System_Threading_DispatcherObject

By deriving from DispatcherObject, you create a CLR object that has STA behavior, and will be given a pointer to a dispatcher at creation time.

So, all Visuals are STA which means you can't freeze BitmapCacheBrush unless you set it's Target to null.