先建立测试基类
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); }
测试结果
这样就实现了可以在不同父元素的绑定,通过这个例子也可以看到逻辑树与视觉树的不同之处