• Learning WPFUnleashedBook chapter 3Important new concepts in WPF


    最近读了WPFUnleashed这本书的第三章,主要介绍了一下几个方面:

    1. Logical and Visual Trees(逻辑树和视觉树)
    2. Dependency Properties(依赖属性)
    3. Routed Events
    4. Commands
    5. WPF 类的层次概述

    下面分别是我觉得比较重要的地方的笔记。
    1. 逻辑树和视觉树
    逻辑树就是我们实际看到的节点间的树的结构。视觉树可以看作是对逻辑树的扩展,它把每一个元素还原
    成它的真实面目,我们可以看到每一个元素由那些visual components组成的。例如一个ListBox,在逻辑
    树中它就是一个元素,但在视觉树中我们可以看到它包含一个Border,两个Scrollbar,等等组成的。

    注意在XamlPad中,只能显示Page的Visual Tree,对于Window,我们可以把根节点改成Page来查看它的
    Visual Tree。

    逻辑树在构造函数中就可以打印出来查看了,但是视觉树要等到整个元素(例如Window)被layout至少一次
    以后才可以形成的。所以我们可以在Override void OnContentRendered(EventArgs e)中查看视觉树。

    2. Dependency Properties
    WPF用Dependency Properties来实现styling,动态data binding,animation,change notification等等功能。
    为什么叫Dependency Properties呢,因为一个Dependency property在任何时刻都能及时的根据许多的
    Providers来决定它的值,例如这个providers可能是从父元素那里继承来的属性值、由动画连续改变它的值,
    自己定义的缺省值等等。

    Property value inheritance(属性值的继承机制)

    注意:不是所有的dependency property都能被继承的,在Dependency.Register中我们通过
    FrameworkPropertyMetadataOptions.Inherits来指定这个属性可以被继承。

    因为有多个Providers,它们是通过下面的顺序来决定dependency property的最终值的:

    Determine Base Value -----> Evaluate -----> Apply animations -----> Coerce -----> Validate

    Step(1) Determine Base Value

    Base value 有以下八个提供者,其中1为最高的优先级:

    1. Local value
    2. Style triggers
    3. Template triggers
    4. Style setters
    5. Theme Style triggers
    6. Theme Style setters
    7. Property value inheritance
    8. Default value
    其中Local value是指直接对某个元素设置的值,它会调用或者我们直接调用DependencyObject.SetValue
    方法。Default value是Register是设置的值。

    Step(2) Evaluate

    如果第一步得到的值是一个expression(derives from System.Windows.Expression),WPF则根据这个
    expression来计算值,这个expression可能来自于使用了DynamicResource或者是data binding。

    Step(3) Apply animations

    如果有动画,动画会改变上面得到的值。

    Step(4) Coerce

    将上面得到的值给CoerceValueCallback代理,判断是否需要返回一个强制的value。

    Step(5) Validate

    最后,将上面得到的值给ValidateValueCallback代理,这个代理会返回true如果上面的值是合法的,
    false说明不合法,会取消整个过程,或者抛一个异常。

    Tooltip:在debug的时候,如果不能确定我们最终得到的值来自哪里,我们可以用如下的方法,
    例如有一个StatusBar,名称是statusBar:
    ValueSource vs = DependencyPropertyHelper.GetValueSource(statusBar, FontSizeProperty);
    vs 的第一个属性值是BaseValueSource,它是一个枚举,对应了Step(1)中的base value
    的来源,其他的值是IsAnimated,IsCoerced,IsExpression等,对应其它几步的来源。
    假设有下面的xaml:

    <StackPanel TextElement.FontSize="20">
          <Button>click</Button>
          <StatusBar>You have successfully registered this product.</StatusBar>
    </StackPanel>

    我们会发现Button的字体继承了父亲的FontSize属性,大小是20。但StatusBar的字体很小,明显不是
    20,为什么呢?
    如果我们用上面的方法得到的BaseValueSource的值是DefaultStyle,DefaultStyle表示这个base value
    来自当前系统的theme style setter的设置,排在base value的第6位,而property inheritance value排在
    第7位,所以优先用系统的字体设置了。类似的还有Menu、ToolTip等等。

    Clearing a local value(清除一个本地设置的值)

    例如有一个Button名称为b,我们在MouseEnter trigger中设置了Foreground属性为Red,但是当Mouse
    Leave的时候如何回到最初的default值呢,这时只要调用b.ClearValue(b, ForegroundProperty)就可以了。

    如果我们想要清除所有的已经设定了的LocalValue,可以调用DependencyObject的
    GetLocalValueEnumerator()方法来获得所有已经设定了的LocalValue的枚举:

    LocalValueEnumerator locallySetProperties = uie.GetLocalValueEnumerator();
    while (locallySetProperties.MoveNext())
    {
    DependencyProperty propertyToClear = (DependencyProperty)locallySetProperties.Current.Property;

    if (!propertyToClear.ReadOnly) { uie.ClearValue(propertyToClear); }

    }

    Attached Properties(附加属性)

    Attached Property是一种特殊的Dependency property,可以看作是在子元素中设置父元素的某些属性。
    注意在上面的xaml片段中,因为StackPanel本身是没有FontSize属性的,这里是用到了TextElement的
    FontSize这个Attached property,为什么能够这样做呢?
    我们看这个属性的注册方式:

    TextElement.FontSizeProperty = DependencyProperty.RegisterAttached(
    “FontSize”, typeof(double), typeof(TextElement), new FrameworkPropertyMetadata(
    SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits |
    FrameworkPropertyMetadataOptions.AffectsRender |
    FrameworkPropertyMetadataOptions.AffectsMeasure),
    new ValidateValueCallback(TextElement.IsValidFontSize));

    然后在Control类里用AddOwner方法把Control类也当作是这个已经注册了的Attached property的Owner:

    Control.FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
    typeof(Control), new FrameworkPropertyMetadata(SystemFonts.MessageFontSize,FrameworkPropertyMetadataOptions.Inherits));

    这样就相当于Control类借用了这个属性了。而Control类又是Window类的基类,所以就实现了。
    当这个根元素是Page的时候,也可以这样做,是因为在Page类中

    FontSizeProperty = TextElement.FontSizeProperty.AddOwner(typeof(Page));

    Attached Properties的扩展用法

    例如GeometryModel3D类本身没有Tag属性,但是我们可以借用FrameworkElement的Tag属性,达到
    类似于该类有这个属性的效果:

    GeometryModel3D model = new GeometryModel3D();
    model.SetValue(FrameworkElement.TagProperty, my custom data);

    这样接着我们就可以通过GetValue得到这个customized data了:
    object obj = model.GetValue(FrameworkElement.TagProperty);







     











  • 相关阅读:
    PyCharm2019激活
    实时统计每天pv,uv的sparkStreaming结合redis结果存入mysql供前端展示
    Python批量删除mysql中千万级大量数据
    SpringBoot + thymeleaf 实现分页
    适合用设计模式解决的问题场景
    重新理解面向过程和面向对象编程
    优化前端单页面应用性能指南
    使用ts开发vue项目知识点1
    职场人的核心竞争力应该是什么
    重新理解原型链
  • 原文地址:https://www.cnblogs.com/bear831204/p/1317619.html
Copyright © 2020-2023  润新知