使用TransformToAncestor时出错:“指定的Visual不是此Visual的祖先。”

时间:2010-05-14 20:12:30

标签: wpf

我正在尝试获取控件相对于窗口顶部的偏移量,但是在使用控件的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东西)。

1 个答案:

答案 0 :(得分:8)

在可视树仍在组装时正在调用转换器,因此您的Visual还不是Window的后代。

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

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

假设您的绑定是这样的:

<SomeObject Abc="{Binding Xyz, Converter={x:Static my:Converter.Instance}}" />

创建一个DependencyObject子类“Whatever”,其中包含附加属性“AbcControl”,其PropertyChangedCallback执行转换并修改“Abc”属性:

public class AttachedProperties : DependencyObject
{
  public Control GetAbcControl(...
  public void SetAbcControl(...
  ... AbcControlProperty = RegisterAttached("AbcControl", typeof(Control), typeof(AttachedProperties), new PropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
    {
      var ctrl = (Control)e.NewValue;
      Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
      {
        var win = Window.GetWindow(ctrl);   
        var transform = ctrl.TransformToAncestor(win); // Exception thrown here.   
        var pt = transform.Transform(new Point(0, 0));   
        obj.SetValue(AbcProperty, pt.Y);
      });
    }
  });
}

现在你可以写:

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

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

这个总体思路可能有很多变化。