代码改变世界

WPF的两棵树与绑定

2010-12-02 10:55  Clingingboy  阅读(1539)  评论(0编辑  收藏  举报

 

先建立测试基类

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();
    }
}

效果

image

视觉树绑定测试

//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) }
});

测试均通过
imageimage

使用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

结果错误

image

设置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将一片空白,但绑定成功

image

重写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);
}

测试结果

image

这样就实现了可以在不同父元素的绑定,通过这个例子也可以看到逻辑树与视觉树的不同之处