原文:WPF的两棵树与绑定
先建立测试基类
public class VisualPanel : FrameworkElement
{
protected VisualCollection Children { get; set; } public VisualPanel()
{
Children = new VisualCollection(this);
} protected override int VisualChildrenCount
{
get
{
return Children.Count;
}
} protected override Visual GetVisualChild(int index)
{
return Children[index];
} protected override Size ArrangeOverride(Size finalSize)
{
if (VisualChildrenCount>0)
{
(Children[0] as FrameworkElement).Arrange(new Rect(0, 0, 100, 25));
}
return base.ArrangeOverride(finalSize);
}
}
添加元素,并将加入到Window窗体中
public class VisualTest : VisualPanel
{
public TextBlock textblock; private void TestVisual()
{
textblock = new TextBlock() { Text = "Hello", Background = Brushes.Red };
this.Children.Add(textblock);
} public VisualTest()
{
TestVisual();
}
}
效果
视觉树绑定测试
//test1
textblock.SetBinding(TextBlock.TextProperty, new Binding("Title")
{
RelativeSource =
new RelativeSource() { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(Window) }
});
//test2
this.Tag = "Test";
textblock.SetBinding(TextBlock.TextProperty, new Binding("Tag")
{
RelativeSource =
new RelativeSource() { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(VisualTest) }
});
测试均通过
使用ElementName绑定
//test3
this.Name = "VisualTest"; textblock.SetBinding(TextBlock.TextProperty, new Binding("Tag")
{
ElementName = this.Name
});
可以参考这里
http://www.cnblogs.com/Clingingboy/archive/2010/11/29/1891253.html
结果错误
设置NameScope
this.Name = "VisualTest";
NameScope ns = new NameScope();
NameScope.SetNameScope(this, ns);
this.RegisterName(this.Name, this);
textblock.SetBinding(TextBlock.TextProperty, new Binding("Tag")
{
ElementName = this.Name
});
测试再次未通过
将元素添加到逻辑树当中
this.Name = "VisualTest";
NameScope ns = new NameScope();
NameScope.SetNameScope(this, ns);
this.RegisterName(this.Name, this);
AddLogicalChild(textblock);
textblock.SetBinding(TextBlock.TextProperty, new Binding("Tag")
{
ElementName = this.Name
});
测试通过
去除视觉树只添加逻辑树的情况
private void TestLogic()
{
textblock = new TextBlock() { Text = "Hello", Background = Brushes.Red };
this.Tag = "Test";
this.Name = "VisualTest";
NameScope ns = new NameScope();
NameScope.SetNameScope(this, ns);
this.RegisterName(this.Name, this);
AddLogicalChild(textblock);
textblock.SetBinding(TextBlock.TextProperty, new Binding("Tag")
{
ElementName = this.Name
});
}
现在UI将一片空白,但绑定成功
重写ArrangeOverride方法
protected override Size ArrangeOverride(Size finalSize)
{
textblock.Arrange(new Rect(0, 0, 100, 25));
return base.ArrangeOverride(finalSize);
}
即使重写也无效,wpf依赖于VisualChildrenCount和GetVisualChild方法.
总结
绑定的ElementName依赖于NameScope和逻辑树,
FindAncestor的查找方式则依赖于视觉树
测试的父元素逻辑树与视觉树不一致的情况
private void TestTwoTree()
{
var visual = new VisualTest(string.Empty);
visual.Name = "InternalPanel";
textblock = new TextBlock() { Text = "Hello", Background = Brushes.Red };
visual.AddLogicalChild(textblock);
this.Children.Add(textblock);
}
现在TextBlock有两个父元素一个是逻辑父元素InternalPanel,一个是外部的VisualTest.
绑定逻辑父元素
private void TestTwoTree()
{
var visual = new VisualTest(string.Empty);
visual.Name = "InernalPanel";
NameScope ns = new NameScope();
NameScope.SetNameScope(visual, ns);
visual.RegisterName(visual.Name, visual);
textblock = new TextBlock() { Text = "Hello", Background = Brushes.Red };
textblock.SetBinding(TextBlock.TextProperty, new Binding("Name")
{
ElementName = visual.Name
});
visual.AddLogicalChild(textblock);
this.Children.Add(textblock);
}
测试结果
这样就实现了可以在不同父元素的绑定,通过这个例子也可以看到逻辑树与视觉树的不同之处