WPF和XAML的隐藏功能?

Hidden features of WPF and XAML?

下面是为各种语言讨论的大量隐藏特性。现在我对XAML和WPF的一些隐藏特性很好奇?

我发现了一个listview的header click事件

1
2
3
<ListView x:Name='lv'
      Height="150"
      GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler">

未列出GridViewColumnHeader.Click属性。

目前的一些相关功能:

  • 结合StringFormat的多绑定

  • 绑定的TargetNullValue

  • 文本剪裁属性

  • 标记扩展

  • 向窗口添加Aero效果

  • 高级"标题"属性

  • XAML变换器

参见:

  • C的隐藏特征#
  • Python的隐藏特征
  • ASP.NET的隐藏功能
  • Perl的隐藏特性
  • Java的隐藏特性
  • vb.net的隐藏功能
  • PHP的隐藏功能
  • 红宝石的隐藏特征
  • C的隐藏特征
  • 等等……

  • 多重绑定(与StringFormat结合使用):

    1
    2
    3
    4
    5
    6
    7
    8
    <TextBlock>
      <TextBlock.Text>
        <MultiBinding StringFormat="{}{0}, {1}">
          <Binding Path="LastName" />
          <Binding Path="FirstName" />
        </MultiBinding>
      </TextBlock.Text>
    </TextBlock>


    还有PresentationTraceSources.TraceLevel技巧,用于调试任何特定场景中绑定的执行情况。您所要做的就是引用WindowsBase程序集中的System.Diagnostics命名空间

    1
    xmlns:sd="clr-namespace:System.Diagnostics;assembly=WindowsBase"

    然后将以下内容添加到绑定表达式中:

    1
    <TextBlock Text="{Binding Message, sd:PresentationTraceSources.TraceLevel=High}"  />

    日志如下:

    1
    2
    3
    4
    5
    6
    System.Windows.Data Warning: 52 : Created BindingExpression (hash=5923895) for Binding (hash=7588182)
    System.Windows.Data Warning: 54 :   Path: 'Message'
    System.Windows.Data Warning: 56 : BindingExpression (hash=5923895): Default mode resolved to OneWay
    System.Windows.Data Warning: 57 : BindingExpression (hash=5923895): Default update trigger resolved to PropertyChanged
    System.Windows.Data Warning: 58 : BindingExpression (hash=5923895): Attach to System.Windows.Controls.TextBlock.Text (hash=65248697)
    System.Windows.Data Warning: 63 : BindingExpression (hash=5923895): Resolving source


    3.5SP1为绑定引入了targetNullValue。如果输入值,这会将绑定属性设置为空;如果属性为空,则显示此值。

    1
    <TextBox Text="{Binding Total, TargetNullValue=$0.00}" />

    3.5SP1在绑定表达式中引入了StringFormat,例如

    1
    <TextBox Text="{Binding Date, StringFormat='{}{0:MM/dd/yyyy}'}" />


    有时您得到的字符串太长,无法在标签上显示。在这种情况下,我们可以利用TextBlockTextTrimming属性来显示椭圆。

    1
    2
    3
    4
    <TextBlock
      Name="sampleTextBlock"
      TextTrimming="WordEllipsis"
      TextWrapping="NoWrap"/>

    MSDN链路


    向窗口添加Aero效果

    1
    2
    3
    4
      <Window.Resources>
        <ResourceDictionary Source="/PresentationFramework.Aero, Version=3.0.0.0, Culture=neutral,
            PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=MSIL;component/themes/aero.normalcolor.xaml" />
    </Window.Resources>


    具有x:typearguments的XAML中的泛型

    如果要在XAML中使用ObservableCollection,则需要创建从ObservableCollection派生的类型,因为不能在XAML中声明它。使用XAML2009,可以使用x:typearguments属性定义泛型类型的类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!-- XAML 2006 -->
    class EmployeeCollection : ObservableCollection<Employee>
    {
    }

    <l:EmployeeCollection>
        <l:Employee FirstName="John" Name="Doe" />
        <l:Employee FirstName="Tim" Name="Smith" />
    </lEmployeeCollection>

    <!-- XAML 2009 -->
    <ObservableCollection x:TypeArguments="Employee">
        <l:Employee FirstName="John" Name="Doe" />
        <l:Employee FirstName="Tim" Name="Smith" />
    </ObservableCollection />


    在禁用的控件上显示工具提示

    WPF允许在处于禁用状态的控件上显示工具提示。

    例如

    1
    <Button Content="Disabled Button" ToolTipService.ShowOnDisabled="True" IsEnabled="False" ToolTip="This is a disabled button"/>

    使用带有x:参数的非默认构造函数

    在XAML2006中,对象必须具有公共的默认构造函数才能使用它们。在XAML2009中,可以使用x:arguments语法传递构造函数参数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!-- XAML 2006 -->
    <DateTime>00:00:00.0000100</DateTime>

    <!-- XAML 2009 -->
    <DateTime>
        <x:Arguments>
            <x:Int64>100</x:Int64>
        </x:Arguments>
    </DateTime>


    不是一个隐藏的特性,但是有了wpf/xaml,你可以得到bea stollnitz和josh smith。WPF/XAML编程之王。


    标记扩展和附加属性是我最喜欢的特性,它们使您能够以非常优雅的方式扩展XAML"词汇表"。

    标记扩展

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!-- Binding to app settings -->
    <CheckBox IsChecked="{my:SettingBinding MinimizeToTray}">Close to tray</CheckBox>

    <!-- Fill ItemsControl with the values of an enum -->
    <ComboBox ItemsSource="{my:EnumValues sys:DaysOfWeek}"/>

    <!-- Localization -->
    <TextBlock Text="{my:Localize HelloWorld.Text}"/>

    <!-- Switch on the result of a binding -->
    <TextBlock Text="{my:Switch Path=IsGood, ValueIfTrue=Good, ValueIfFalse=Bad}"/>

    附加属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <!-- Sort GridView automatically -->
    <ListView ItemsSource="{Binding Persons}"
          IsSynchronizedWithCurrentItem="True"
          util:GridViewSort.AutoSort="True">
        <ListView.View>
            <GridView>
                <GridView.Columns>
                    <GridViewColumn Header="Name"
                                    DisplayMemberBinding="{Binding Name}"
                                    util:GridViewSort.PropertyName="Name"/>
                    <GridViewColumn Header="First name"
                                    DisplayMemberBinding="{Binding FirstName}"
                                    util:GridViewSort.PropertyName="FirstName"/>
                    <GridViewColumn Header="Date of birth"
                                    DisplayMemberBinding="{Binding DateOfBirth}"
                                    util:GridViewSort.PropertyName="DateOfBirth"/>
                </GridView.Columns>
            </GridView>
        </ListView.View>
    </ListView>


    <!-- Vista Glass effect -->
    <Window x:Class="WpfApplication1.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:my="clr-namespace:WpfApplication1"
            Title="Window1"
            my:WinUtil.EnableAeroGlass="True">

    ...

    GridViewSort的源(btw,它使用Ortus提到的GridViewColumnHeader.Click事件)


    可以使用加号(+来引用XAML中的嵌套类型。例如,如果我们有这个类:

    1
    2
    3
    4
    5
    6
    7
    public class SomeClass
    {
        public enum SomeEnum
        {
            SomeValue
        };
    }

    我们可以使用以下语法在XAML中引用SomeValue

    1
    {x:Static local:SomeClass+SomeEnum.SomeValue}

    此语法没有在msdn上记录,也没有正式支持。有人在msdn论坛上问到了这个问题,很明显它打破了vs2010的wpf设计。已在Microsoft Connect上报告。


    网格大小共享(这里是一个很好的例子)。长话短说,您可以让网格列和行共享大小,甚至跨不同的网格。对于所有使用数据报而无需就地编辑数据的人来说,这将是非常宝贵的。


    优先级绑定。允许您按"先到先显示"顺序使用asyn绑定:

    1
    2
    3
    4
    5
    6
    7
    <TextBlock.Text>
          <PriorityBinding FallbackValue="defaultvalue">
            <Binding Path="SlowestDP" IsAsync="True"/>
            <Binding Path="SlowerDP" IsAsync="True"/>
            <Binding Path="FastDP" />
          </PriorityBinding>
    </TextBlock.Text>


    使用静态工厂方法与x:FactoryMethod

    当您有一个没有公共构造函数但静态工厂方法的类型时,您必须在XAML2006中的代码中创建该类型。使用XAML 2009,可以使用x:FactoryMethodx:Arguments属性传递参数值。

    1
    2
    3
    4
    5
    <!-- XAML 2006 -->
    Guid id = Guid.NewGuid();

    <!-- XAML 2009 -->
    <Guid x:FactoryMethod="Guid.NewGuid" />

    XAML变换器

    下面的列表显示了由WPF社区开发的转换器,用于将不同格式转换为XAML,反之亦然。

    Adobe Illustrator XAML导出插件

    Adobe Photoshop到XAML转换器

    Blender XAML导出插件

    LightWave XAML导出插件

    Visio XAML导出

    3D Studio最大到XAML转换器

    maya到xaml转换器

    闪存到XAML转换器

    SVG到XAML转换器

    WMF/EMF到XAML转换器


    高级"标题"属性

    另一件不太清楚的事情是一些属性的内容,我们习惯于只包含文本。如果gui元素的属性是object类型,那么很可能您可以添加一个包含一组控件的面板,而不只是设置文本。

    例如,menuitem,其中Header属性(通常只包含文本)可以包含一组封装在面板控件中的GUI元素(如果只需要一个GUI元素,则仅包含一个)。

    还要注意menuitem上的Icon属性。这通常包含一个图像元素,但也可以包含任何内容!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <MenuItem Name="MyMenuItem" Click="MyMenuItem_Click">
      <MenuItem.Icon>
        <Button Click="Button1_Click">i</Button>
      </MenuItem.Icon>
      <MenuItem.Header>
         <StackPanel Orientation="Horizontal">
            <Label>My text</Label>
            <Button Click="Button2_Click">ClickMe!</Button>
         </StackPanel>
      </MenuItem.Header>
    </MenuItem>

    内置类型

    如果今天要将字符串或double等简单类型的对象添加到资源字典,则需要将所需的clr命名空间映射到XML命名空间。在XAML 2009中,我们有许多包含在XAML语言中的简单类型。

    1
    2
    3
    4
    5
    <!-- XAML 2006 -->
    <sys:String xmlns:sys="clr-namespace:System;assembly=mscorlib >Test</sys:String>

    <!-- XAML 2009 -->
    <x:String>Test</x:String>

    以下类型包含在XAML语言中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <x:Object/>
    <x:Boolean/>
    <x:Char/>
    <x:String/>
    <x:Decimal/>
    <x:Single/>
    <x:Double/>
    <x:Int16/>
    <x:Int32/>
    <x:Int64/>
    <x:TimeSpan/>
    <x:Uri/>
    <x:Byte/>
    <x:Array/>
    <x:List/>
    <x:Dictionary/>


    使用x:reference_的简单对象引用

    如果您今天想要创建一个对象引用,您需要做一个数据绑定并用elementname声明源。在XAML 2009中,可以使用新的x:引用标记扩展

    1
    2
    3
    4
    5
    6
    7
    <!-- XAML 2006 -->
    <Label Target="{Binding ElementName=firstName}">FirstName</Label>
    <TextBox x:Name="firstName" />

    <!-- XAML 2009 -->
    <Label Target="{x:Reference firstName}">FirstName</Label>
    <TextBox x:Name="firstName" />


    系统颜色使用

    1
    <Border Background="{DynamicResource {x:Static SystemColors.InactiveBorderBrushKey}}"/>


    支持任意字典键

    在XAML2006中,所有显式x:key值都被视为字符串。在XAML2009中,您可以通过用elementSyntax编写键来定义任何类型的键。

    1
    2
    3
    4
    5
    6
    7
    <!-- XAML 2006 -->
    <StreamGeometry x:Key="CheckGeometry">M 0 0 L 12 8 l 9 12 z</StreamGeometry>

    <!-- XAML 2009 -->
    <StreamGeometry>M 0 0 L 12 8 l 9 12 z
        <x:Key><x:Double>10.0</x:Double></x:Key>
    </StreamGeometry>

    按代码设置验证错误

    bindingExpression中的validationRule仅在绑定的目标端更改时触发。如果您想通过代码设置验证错误,可以使用以下代码段。

    设置验证错误

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ValidationError validationError =
        new ValidationError(regexValidationRule,
        textBox.GetBindingExpression(TextBox.TextProperty));

    validationError.ErrorContent ="This is not a valid e-mail address";

    Validation.MarkInvalid(
        textBox.GetBindingExpression(TextBox.TextProperty),
        validationError);

    清除验证错误

    1
    Validation.ClearInvalid(textBox.GetBindingExpression(TextBox.TextProperty));

    将ui元素填充到文本块中的能力

    我不知道这有多有用(尽管它被认为是隐藏的)这是…但当我第一次碰到它时,它确实让我措手不及:

    1
    2
    3
    4
    5
    6
    7
    <Grid x:Name="LayoutRoot">
        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
            <Grid>
                <Rectangle Fill="AliceBlue" Width="25" Height="25"/>
            </Grid>
        </TextBlock>
    </Grid>

    您可能会认为以下XAML可能有用(即在某些文本的末尾放置图形):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <Grid>
        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="Hello World">
            <TextBlock.Resources>
                <DrawingBrush x:Key="exclamationPoint" Stretch="Uniform">
                    <DrawingBrush.Drawing>
                        <DrawingGroup>
                            <DrawingGroup.Children>
                                <GeometryDrawing Brush="#FF375CE2" Geometry="F1 M 7.968,58.164L 0,58.164L 1.914,49.921L 9.882,49.921L 7.968,58.164 Z M 21.796,0L 11.054,42.148L 4.403,42.148L 13.049,0L 21.796,0 Z"/>
                            </DrawingGroup.Children>
                        </DrawingGroup>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </TextBlock.Resources>
            <Grid>
                <Rectangle Width="100" Height="100" Fill="{StaticResource exclamationPoint}"/>
            </Grid>
        </TextBlock>
    </Grid>

    上面的XAML呈现如下:

    Hello World


    绑定时不带InotifyPropertiesChanged或DependencyProperties

    正如这里讨论的,您可以绑定一个普通的clr对象属性,而不使用inotifyPropertiesChanged,它只会起作用。

    这是我所指的裁决。

    报价:

    [...] WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.

    And if you are implementing INotifyPropertyChanged, you are fully responsible to implement the change notification in every setter of the properties which needs to be data bound to the UI. Otherwise, the change will be not synchronized as you'd expect.[...]

    这是另一篇关于这个主题的伟大而详细的文章。

    Note this only works when using binding. If you update the values from code, the change won't be notified. [...]

    Implementing INotifyPropertyChanged can be a fair bit of tedious development work. However, you'll need to weigh that work against the runtime footprint (memory and CPU) of your WPF application. Implementing INPC yourself will save runtime CPU and memory.


    调试动画

    常见错误

    如果出现以下错误:无法在不可变的对象实例上动画"(0)。(1)"。可能是您遇到了以下限制之一:

    • 您正在设置依赖项属性的动画,而不设置本地值
    • 您正在为依赖项属性设置动画,该属性的当前值是在另一个未合并到资源字典中的程序集中定义的。
    • 您正在为当前数据绑定的值设置动画