In my user control I want to get aware of rotations & co. made in the WPF designer. So in my user control I did:
private void Test_Loaded(object sender, RoutedEventArgs e)
{
this.RenderTransform.Changed += this.RenderTransform_Changed;
}
private void RenderTransform_Changed(object sender, EventArgs e)
{
// do anything
}
It seems this.RenderTransform
is never null. However unless my user control has the exact XAML structure like the designer makes when i.e. rotating my control, it will fail.
Example: When I open a XAML file with the following content:
<my:Test>
<my:Test.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</my:Test.RenderTransform>
</my:Test>
the RenderTransform_Changed
will be called when I rotate my control.
But when the XAML is:
<my:Test>
<my:Test.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<TranslateTransform/>
</TransformGroup>
</my:Test.RenderTransform>
</my:Test>
or
<my:Test>
<my:Test.RenderTransform>
<RotateTransform/>
</my:Test.RenderTransform>
</my:Test>
or
<my:Test/>
it won't call RenderTransform_Changed
when rotating my control.
I assume, this happens, because the designer is recreating this.RotateTransform
when it does not follow the exact same structure as the designer wants. Thus, the subscription will be lost, entirely.
To solve the problem I tried to provide the same structure to this.RenderTransform
in the Loaded
event before subscribing to the Changed
event:
private void Test_Loaded(object sender, RoutedEventArgs e)
{
ScaleTransform scale = this.RenderTransform is ScaleTransform
? (ScaleTransform)this.RenderTransform
: new ScaleTransform();
SkewTransform skew = this.RenderTransform is SkewTransform
? (SkewTransform)this.RenderTransform
: new SkewTransform();
RotateTransform rotate = this.RenderTransform is RotateTransform
? (RotateTransform)this.RenderTransform
: new RotateTransform();
TranslateTransform translate = this.RenderTransform is TranslateTransform
? (TranslateTransform)this.RenderTransform
: new TranslateTransform();
if (this.RenderTransform is TransformGroup)
{
TransformCollection tc = ((TransformGroup)this.RenderTransform).Children;
foreach (Transform t in tc)
{
if (t is ScaleTransform) scale = (ScaleTransform)t;
if (t is SkewTransform) skew = (SkewTransform)t;
if (t is RotateTransform) rotate = (RotateTransform)t;
if (t is TranslateTransform) translate = (TranslateTransform)t;
}
if (!tc.Any(x => x is ScaleTransform)) tc.Add(scale);
if (!tc.Any(x => x is SkewTransform)) tc.Add(skew);
if (!tc.Any(x => x is RotateTransform)) tc.Add(rotate);
if (!tc.Any(x => x is TranslateTransform)) tc.Add(translate);
}
else
{
this.RenderTransform = new TransformGroup()
{
Children =
{
scale,
skew,
rotate,
translate,
},
};
}
foreach (Transform t in ((TransformGroup)this.RenderTransform).Children)
{
t.Changed += this.RenderTransform_Changed;
}
this.RenderTransform.Changed += this.RenderTransform_Changed;
}
private void RenderTransform_Changed(object sender, EventArgs e)
{
// do anything
}
With that I tried to be ready for any given structure of RenderTransform
on initial time. But it seems the designer does not care what's actually in the RenderTransform
object, it simply rewrites it, because the XAML still does not fulfill the structure it wants.
What can I do to get rid of this problem?
Grx70's solution gave me the right direction, however I was overwriting the
PropertyMetadata
to get rid of the reset ofRenderTransform
.So I did
in the static constructor of my control. So then I could do
Unless I'm wrong in my diagnosis of the problem, here's an answer:
The problem
Whenever you register a handler with
this.RenderTransform.Changed += ...
you subscribe to an object which is the value ofthis.RenderTransform
. When the property is then changed, it no longer holds the object to which you subscribed (and that object is no longer in effect), yet you're still subscribed to that exact object, and not the new value of the property.The solution
In order to keep track of the actual value of the
RenderTransform
proprety you need to subscribe to it's owner (usually aWindow
or aControl
) for a "ValueChanged" event, and registerRenderTransform.Changed
handler upon each occurrence of that event. SinceRenderTransform
is a "shortcut property" for aDependencyProperty
calledRenderTransformProperty
, you need to do it like this:where
DependencyPropertyDescriptor
is inSystem.ComponendModel
namespace.