Wpf 使用TransformToAncestor时出错:“0”;指定的视觉对象不是此视觉对象的祖先。”;

Wpf 使用TransformToAncestor时出错:“0”;指定的视觉对象不是此视觉对象的祖先。”;,wpf,Wpf,我试图获取控件相对于其窗口顶部的偏移量,但在使用控件的TransformToAncestor方法时遇到了问题。注意:此代码位于值转换器中,它将从控件转换为相对于窗口的相对Y位置 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var ctrl = (Control) value; var win = Window.GetWindow(ctrl

我试图获取控件相对于其窗口顶部的偏移量,但在使用控件的TransformToAncestor方法时遇到了问题。注意:此代码位于值转换器中,它将从控件转换为相对于窗口的相对Y位置

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var ctrl = (Control) value;
    var win = Window.GetWindow(ctrl);
    var transform = ctrl.TransformToAncestor(win); // Exception thrown here.
    var pt = transform.Transform(new Point(0, 0));
    return pt.Y;
}
Window.GetWindow
的调用工作正常,并返回控件所在的正确窗口对象

我是否误解了WPF对“祖先”的看法?我认为给定
GetWindow
的结果,该窗口将是控件的祖先。是否有某些筑巢模式会导致祖先的血统在某一点被切断

更新:

看起来这可能是个时间问题。当我尝试在事件处理程序中调用
TransformToAncestor
方法而不是值转换器时,它工作得很好。似乎在祖先关系建立之前,值转换器必须在实例化某些元素时运行


我不知道如何解决这个问题,因为我正在尝试使用MVVM模式(因此我不想使用事件处理程序,也不希望在我的ViewModel中使用System.Windows内容)。

在可视化树仍在组装时调用转换器,因此您的可视化还不是该窗口的后代

您希望在构建可视化树后进行转换。这是通过使用Dispatcher.BeginInvoke(DispatcherPriority.Render,…)注册Dispatcher回调并在回调中执行您的工作来完成的

但是,这不能用于转换器,因为转换器必须立即返回转换后的值。简单的解决方法是使用附加属性而不是转换器。以下是方法:

假设您的绑定是这样的:

<SomeObject Abc="{Binding Xyz, Converter={x:Static my:Converter.Instance}}" />
现在你可以写:

<SomeObject AbcControl="{Binding Xyz}" />

将Abc属性设置为转换后的Y值

在这个总的想法上有许多可能的变化

<SomeObject AbcControl="{Binding Xyz}" />