Hidden features of WPF and XAML?

狂风中的少年 提交于 2019-12-17 14:59:23

问题


Here is a large number of hidden features discussed for variety of languages. Now I am curious about some hidden features of XAML and WPF?

One I have found is the header click event of a ListView

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

The GridViewColumnHeader.Click property is not listed.

Some of relevant features so far:

  • Multibinding combined with StringFormat

  • TargetNullValue to bindings

  • TextTrimming property

  • Markup extensions

  • Adding Aero effect to Window

  • Advanced "caption" properties

  • XAML Converters

See also:

  1. Hidden features of C#
  2. Hidden features of Python
  3. Hidden features of ASP.NET
  4. Hidden features of Perl
  5. Hidden features of Java
  6. Hidden features of VB.NET
  7. Hidden features of PHP
  8. Hidden features of Ruby
  9. Hidden features of C
  10. And So On........

回答1:


Multibinding (combined with StringFormat):

<TextBlock>
  <TextBlock.Text>
    <MultiBinding StringFormat="{}{0}, {1}">
      <Binding Path="LastName" />
      <Binding Path="FirstName" />
    </MultiBinding>
  </TextBlock.Text>
</TextBlock>



回答2:


There is also PresentationTraceSources.TraceLevel trick to debug what is going on with bindings in any particular scenario. All you have to do is to reference System.Diagnostics namespace in WindowsBase assembly

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

and then add following to the binding expression:

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

Log will be like this:

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:


3.5sp1 introduced TargetNullValue to bindings. This will set the bound property to Null if the value is entered and if your property is Null it will display this value.

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



回答4:


3.5sp1 introduced StringFormat into binding expressions, e.g.

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



回答5:


Sometimes you get string that is too long to show on label. In this case we can make use of TextTrimming property of TextBlock to show Ellipses

<TextBlock 
  Name="sampleTextBlock" 
  TextTrimming="WordEllipsis" 
  TextWrapping="NoWrap"/>

MSDN Link




回答6:


Adding Aero effect to Window

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



回答7:


Generics in XAML with x:TypeArguments

If you want to use an ObservableCollection in XAML you need to create a type that derives from ObservableCollection because you cannot declare it in XAML. With XAML 2009 you can use the x:TypeArguments attribute to define the type of a generic type.

<!-- 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 />



回答8:


Show Tooltip on a disabled control

Wpf allows to show tooltip on a control, if it is in disabled state.

For example

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



回答9:


Use of Non-Default Constructors with x:Arguments

In XAML 2006 objects must have a public default constructor to use them. In XAML 2009 you can pass constructor arguments by using the x:Arguments syntax.

<!-- XAML 2006 -->
<DateTime>00:00:00.0000100</DateTime>

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



回答10:


Not really a hidden feature but with WPF/XAML you get Bea Stollnitz and Josh Smith. Queen and King of WPF/XAML programming.




回答11:


Markup extensions and attached properties are my favorite features, they enable you to extend XAML "vocabulary" in a very elegant way.

Markup extensions

<!-- 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}"/>

Attached properties

<!-- 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">

...

Source for GridViewSort (btw, it uses the GridViewColumnHeader.Click event mentioned by Ortus)




回答12:


You can refer to nested types in XAML using the plus sign (+). For example, if we had this class:

public class SomeClass
{
    public enum SomeEnum
    {
        SomeValue
    };
}

We could refer to SomeValue in XAML using the following syntax:

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

This syntax is not documented on MSDN, and it is not officially supported. Someone asked about it on the MSDN forums, and apparently it breaks VS2010's WPF Designer. It has been reported on Microsoft Connect.




回答13:


Grid size sharing (here's a good example). Long story short you can have grid columns and rows share sizes, even across different grids. This will be invaluable for all the people out there who are using DataGrids without the need to edit the data in place.




回答14:


PriorityBinding. Allows you to use asyn bindings in an "first come first show" order:

<TextBlock.Text>
      <PriorityBinding FallbackValue="defaultvalue">
        <Binding Path="SlowestDP" IsAsync="True"/>
        <Binding Path="SlowerDP" IsAsync="True"/>
        <Binding Path="FastDP" />
      </PriorityBinding>
</TextBlock.Text>



回答15:


Use of Static Factory Methods with x:FactoryMethod

When you have a type that has no public constructor but a static factory method you had to create that type in code in XAML 2006. With XAML 2009 you can use the x:FactoryMethodx:Arguments attribute to pass the argument values.

<!-- XAML 2006 -->
Guid id = Guid.NewGuid();

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



回答16:


Advanced "caption" properties

Another thing that is not very clear is the contents of some properties that we are used to contains only text. If the property of a GUI element is of type Object, it is very likely that you can, instead of just setting the text, add a panel of your need that includes a set of controls.

An example of this is the MenuItem, where the Header property (which normally just contains text) can contain a set of gui elements wrapped in a panel control (or just one gui element if you need just one).

Also note the Icon property on the MenuItem. This normally contains an Image element, but this also can contain anything!

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



回答17:


XAML Converters

The following list shows converters developed by the WPF community to convert different formats to XAML or vice versa.

Adobe Illustrator XAML Export Plugin

Adobe Photoshop to XAML Converter

Blender XAML Export Plugin

Lightwave XAML Export Plugin

Visio XAML Export

3D Studio Max to XAML Converter

Maya to XAML Converter

Flash to XAML Converter

SVG to XAML Converter

WMF/EMF to XAML Converter




回答18:


Built-in Types

If you want to add objects of simple types like string or double to a resource dictionary today you need to map the needed clr-namespaces to an XML namespaces. In XAML 2009 we a lot of simple types that are included in the XAML language.

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

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

The following types are included into the XAML language:

<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/> 



回答19:


Easy Object References with {x:Reference}

If you want to create an object reference today you need to do a databinding and declare the source with an ElementName. In XAML 2009 you can use the new {x:Reference} markup extension

<!-- 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" />



回答20:


System Colors Usage

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



回答21:


Support for Arbitrary Dictionary Keys

In XAML 2006 all explicit x:Key value were treated as strings. In XAML 2009 you can define any type of key you like by writing the key in ElementSyntax.

<!-- 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>



回答22:


Set a ValidationError by Code

A ValidatioRule in a BindingExpression only triggers, when the target side of the binding changes. If you want to set a validation error by code you can use the following snippet.

Set the validation error

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

Clear the validation error

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



回答23:


The Ability to Stuff UIElement(s) into a TextBlock

I don't know how useful (it qualifies as hidden though) this is ... but it sure caught me off-guard when I first ran into it:

<Grid x:Name="LayoutRoot">
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid>
            <Rectangle Fill="AliceBlue" Width="25" Height="25"/>
        </Grid>
    </TextBlock>
</Grid>

You could argue the following xaml could be useful (i.e. putting a graphic at the end of some text):

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

The above xaml renders like the following:




回答24:


Debugging Animations

Common Errors

If you get the following error: Cannot animate '(0).(1)' on an immutable object instance. it could be that you are run into one of the following limitations:

  • You are animating a dependency property without setting a local value
  • You are animating a dependency property who's current value is defined in another assembly that is not merged into the resource dictionary.
  • You are animating a value that is currently databound



回答25:


Binding without INotifyPropertyChanged or DependencyProperties

As discussed here you can bind a plain CLR object property without INotifyPropertyChanged, and it will just work.

Here is the Forumpost i am referring to.

Quote:

[...] 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.[...]

Here is another great and detailed article on the subject.

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.



来源:https://stackoverflow.com/questions/1124769/hidden-features-of-wpf-and-xaml

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!