简体   繁体   中英

C# WPF Behavior class, AssociatedObject is null after the UIElement was unloaded due to windows scaling change

I'm new to WPF, and trying to fix a bug with a draggable window.

I'm checking a bug in an app I maintain where we have a Behavior class, with which we implement a dragging mechanism. When I go to windows' display settings and change the scaling (100%/125%/150%/175%), windows seems to dispose the app's window and recreate it.
This is where the AssociatedObject member of the behavior class becomes null, so when the Loaded event is invoked again, I have no AssociatedObject to work with.

How can I get the new AssociatedObject? It seems like expected behavior since the UI element was recreated, but the behavior class is still 'alive' but can't do any work since it isn't familiar with the new UI element (If I understand things correctly, as I mentioned, I'm new to WPF)

Thanks!

Edit: adding a sample class

public class SetWindowSizeBehavior : Behavior<FrameworkElement>
{

    protected override void OnDetaching()
    {
        Console.WriteLine("detaching");
        base.OnDetaching();
    }

    protected override void OnAttached()
    {
        AssociatedObject.Unloaded += AssociatedObject_Unloaded;
        AssociatedObject.Loaded += AssociatedObject_Loaded;

        base.OnAttached();
    }

    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        Console.WriteLine("");
        var a = AssociatedObject;
    }

    private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
    {
        Detach();
    }
}

After I'm changing the scaling settings at the windows display settings menu, this is the call stack:
AssociatedObject_Unloaded
OnDetaching
AssociatedObject_Loaded <- this is where AssociatedObject becomes null

I hope the question is more clear now, and if not, would love to get some comments about missing info...

Can you try overriding the 'OnPropertyChanged' method

public class SetWindowSizeBehavior : Behavior<FrameworkElement>
{

    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
        if (AssociatedObject == null)
        {
            // so, let'save the value and then reuse it when OnAttached() called
            _value = e.NewValue as string;
            return;
        }

        if (e.Property == PasswordProperty)
        {
            if (!_skipUpdate)
            {
                _skipUpdate = true;
                AssociatedObject.Password = e.NewValue as string;
                _skipUpdate = false;
            }
        }
    }
    protected override void OnDetaching()
    {
        Console.WriteLine("detaching");
        base.OnDetaching();
    }

    protected override void OnAttached()
    {
        AssociatedObject.Unloaded += AssociatedObject_Unloaded;
        AssociatedObject.Loaded += AssociatedObject_Loaded;

        base.OnAttached();
    }

    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        Console.WriteLine("");
        var a = AssociatedObject;
    }

    private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
    {
        Detach();
    }
}

From here: https://stackoverflow.com/a/42312799/7779827

Eventually I used @DanielFr's idea and when I detected that the associatedObject is null (cause the base class Behavior sets it to null when the detach method is called) I used the sender object and:

if (AssociatedObject == null)
        {
            //If we got here then we didn't go through onAttached in this iteration
            Attach((FrameworkElement)sender);
        }

When FrameworkElement is my T in my Behavior class

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM