dotnet/wpf · error · InvalidOperationException

SR.BitmapCacheBrush_RelativeTransformChanged

Error message

SR.BitmapCacheBrush_RelativeTransformChanged

What it means

BitmapCacheBrush only supports its default (null/Identity) RelativeTransform; any attempt to set a non-default transform is coerced back and throws InvalidOperationException. The library intentionally disallows changing RelativeTransform because bitmap-cache rendering cannot apply an arbitrary relative transform on the MilCore side.

Solutions

  1. Remove the RelativeTransform assignment and instead apply the transform to the Visual being cached or to the element's RenderTransform/LayoutTransform.
  2. Use RenderTransform on the BitmapCacheBrush itself or its container instead of RelativeTransform.
  3. If a transform is required relative to the output area, wrap the content in a decorator element that applies the transform.

Example fix

// before
var brush = new BitmapCacheBrush(visual);
brush.RelativeTransform = new RotateTransform(45);

// after
var brush = new BitmapCacheBrush(visual);
brush.RenderTransform = new RotateTransform(45);
Defensive patterns

Strategy: validation

Validate before calling

static bool CanSetRelativeTransform(Transform t) => t == null || t == Transform.Identity;

Type guard

static bool IsDefaultTransform(Transform t) => t is null || t == Transform.Identity;

Prevention

When it happens

Trigger: Assigning a Transform to BitmapCacheBrush.RelativeTransform (directly, via binding, animation, or style setter) to any value other than the default (null/Transform.Identity).

Common situations: Data-binding or animating RelativeTransform by copying XAML patterns from other brushes (VisualBrush, ImageBrush); applying a RenderTransform-style resource to a BitmapCacheBrush in a template.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/a00f4fc12dba75e3. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/BitmapCacheBrush.cs:371

                throw new InvalidOperationException(SR.BitmapCacheBrush_OpacityChanged);
            }
            return 1.0;
        }

        private static object CoerceTransform(DependencyObject d, object value)
        {
            if ((Transform)value != (Transform)TransformProperty.GetDefaultValue(typeof(BitmapCacheBrush)))
            {
                throw new InvalidOperationException(SR.BitmapCacheBrush_TransformChanged);
            }
            return null;
        }

        private static object CoerceRelativeTransform(DependencyObject d, object value)
        {
            if ((Transform)value != (Transform)RelativeTransformProperty.GetDefaultValue(typeof(BitmapCacheBrush)))
            {
                throw new InvalidOperationException(SR.BitmapCacheBrush_RelativeTransformChanged);
            }
            return null;
        }

        private static void StaticInitialize(Type typeofThis)
        {             
            OpacityProperty.OverrideMetadata(typeofThis, new IndependentlyAnimatedPropertyMetadata(1.0, propertyChangedCallback: null, CoerceOpacity));
            TransformProperty.OverrideMetadata(typeofThis, new UIPropertyMetadata(null, propertyChangedCallback: null, CoerceTransform));
            RelativeTransformProperty.OverrideMetadata(typeofThis, new UIPropertyMetadata(null, propertyChangedCallback: null, CoerceRelativeTransform));
        }

        private ContainerVisual _dummyVisual;
        
        private DispatcherOperation _DispatcherLayoutResult;
        private bool _pendingLayout;        
        private bool _reentrancyFlag;

        private bool _isAsyncRenderRegistered = false;

View on GitHub (pinned to 81131a70a4)