Search code examples
c#storyboardexpression-blendblend

Blend Interaction Behaviour gives "points to immutable instance" error


I have a UserControl that is a base class for other user controls, that are shown in a "modal view".
I want to have all user controls fading in when shown and fading out when closed. I also want the user to be able to move the controls around. My constructor looks like this:

var tg = new TransformGroup();
tg.Children.Add(new ScaleTransform());
RenderTransform = tg;
var behaviors = Interaction.GetBehaviors(this);
behaviors.Add(new TranslateZoomRotateBehavior());  

Loaded += ModalDialogBase_Loaded;

And the ModalDialogBase_Loaded method looks like this:

private void ModalDialogBase_Loaded(object sender, RoutedEventArgs e)
{
    var fadeInStoryboard = (Storyboard)TryFindResource("modalDialogFadeIn");
    fadeInStoryboard.Begin(this);
}

When I press a Close-button on the control this method is called:

protected virtual void Close()
{
    var fadeOutStoryboard = (Storyboard)TryFindResource("modalDialogFadeOut");  
    fadeOutStoryboard = fadeOutStoryboard.Clone();
    fadeOutStoryboard.Completed += delegate
    {
        RaiseEvent(new RoutedEventArgs(ClosedEvent));
    };
    fadeOutStoryboard.Begin(this);
}

The storyboard for fading out look like this:

<Storyboard x:Key="modalDialogFadeOut">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="{x:Null}">
        <EasingDoubleKeyFrame KeyTime="0" Value="1">
            <EasingDoubleKeyFrame.EasingFunction>
                <BackEase EasingMode="EaseIn" Amplitude="0.3" />
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
        <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0">
            <EasingDoubleKeyFrame.EasingFunction>
                <BackEase EasingMode="EaseIn" Amplitude="0.3" />
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="{x:Null}">
        <EasingDoubleKeyFrame KeyTime="0" Value="1">
            <EasingDoubleKeyFrame.EasingFunction>
                <BackEase EasingMode="EaseIn" Amplitude="0.3" />
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
        <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0">
            <EasingDoubleKeyFrame.EasingFunction>
                <BackEase EasingMode="EaseIn" Amplitude="0.3" />
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="{x:Null}">
        <EasingDoubleKeyFrame KeyTime="0" Value="1" />
        <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0" />
        <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0" />
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

If the user control is shown, and the user does NOT move it around on the screen, everything works fine. However, if the user DOES move the control around, I get the following error when the modalDialogFadeOut storyboard is started:

'Children' property value in the path '(0).(1)[0].(2)' points to immutable instance of 'System.Windows.Media.TransformCollection'.

How can I fix this?


Solution

  • The issue is that the TranslateZoomRotateBehavior is replacing your ScaleTransform with a MatrixTransform, causing the first two animations in your Storyboard to target a property that no longer exists.

    Since you can't animate the values of a Matrix to get the fadeout effect, I would use an additional container control. The outermost for the behavior and then an inner one to fade out visually.