简体   繁体   中英

WPF TransformToAncestor error

I have the following code

public partial class MyControl : UserControl
{
    ...
    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        if (DataContext is MyViewModel)
        {
            var vm = DataContext as CameraViewModel;
            var p = Parent as FrameworkElement;
            while (!(p is Window))
                p = p.Parent as FrameworkElement;

            vm.GetTransform = () => TransformToAncestor(p);
        }
    }

}

And in my viewmodel I have a command handler code, executes when a button in my user control being clicked.

public Func<GeneralTransform> GetTransform { private get; set; }
private void OnMyCommand(object parameter){
    var p = GetTransform().Transform(new Point(0,0));
}

But the above code does not work, as the GetTransform() call says

The specified Visual is not an ancestor of this Visual.

I think my code above is already ensured that p is the window ancestor of the control, but why I still get this error?

Thanks to @Brian Reichle's comment, I figured out the reason and resolved it.

It turn out to be the case that I have the same control in different tabs of a tab control.

As you should know, there is only one tab can be active at all times. Those controls belong to tabs that currently inactive are belong to different visual trees that don't have a window ancestor.

In my case, this is what happened: I track back the logical tree to figure out the Window object, but the this object is belong to some invisible tabs and don't have that Window as its visual ancestor.

Once realized this, the solution is then not to attach Loaded event, but attach IsVisibleChanged event.

    private void UserControl_IsVisibleChanged(object sender, 
        DependencyPropertyChangedEventArgs e)
    {
        if (!(bool)e.NewValue)
            return;
        if (DataContext is CameraViewModel)
        {
            SaveTransform();
        }
        else Dispatcher.InvokeAsync(SaveTransform);
    }

    private void SaveTransform()
    {
        var vm = DataContext as CameraViewModel;
        var w = VisualTreeHelper.GetParent(this) as FrameworkElement;
        while (w != null && !(w is Window))
            w = VisualTreeHelper.GetParent(w) as FrameworkElement;
        if (w != null)
        {
            vm.GetTransform = () => TransformToAncestor(w);
        }
    }

Note: in the first fire of IsVisibleChanged event the data context might not being set yet. So the above code have to postpond the SaveTransform code later.

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