Daniel Cazzulino's Blog :

Subscriptions

News

Source code published in this blog is public domain unless otherwise specified.

 

kzu in LinkedIn

  Microsoft MVP Profile

 Contact

Post Categories

WPF (RSS)

WPF
For those that think Microsoft Connect is useless

A while back I reported both through my weblog and Microsoft Connect what I thought was a serious flaw in the WPF validation infrastructure for ValidationRule and Binding. The issue, in short was:

The validation check is invoked during any attempt to update the underlying data ... before a value converter is called (if present)...

Take special note that this was not some undocumented, strange behavior, but rather something that was explicitly explained on MSDN where the validation process was explained as part of the design.

But based on my feedback and the 13 other people that voted to fix the bug, it looks like they just did so (at least that's what the bug status shows).

Pretty significant IMO, especially since in my experience these kind of "breaking changes even if the existing behavior was already broken" issues rarely get fixed, no matter how obvious the error might be.

Continue blogging and bitching about stuff that bothers you, but don't forget to report it formally: it might make a difference and we may end up getting a better product from Microsoft (it may take years, but hey, it's a learning process!).

posted Friday, April 18, 2008 1:12 AM by kzu with 1 Comments

NavigationWindow, WinFormsHost and TextBoxes: backspace bug

WPF NavigationWindow is a very handy class that allows you to easily build Vista-like wizards, or any kind of window that resembles the browser-style navigation:

NavWindow   

The NavigationWindow will automatically handle the Backspace, Back/Next keys and perform the appropriate navigation on the window pages.

If you're combining this class with the WindowsFormsHost to integrate WinForms controls, you may hit a very annoying bug: if there are textboxes in your WinForm control, hitting Backspace while editing the input on it will cause the navigation window to navigate back, instead of deleting the text on the TextBox. Yikes!

Being WPF the new kid on the block, I was immediately tempted to blame it. Turns out it's a bug in the WinForms TextBoxBase :S.

You see, before WPF gets a chance to handle an input message, WindowsFormsHost tries to determine whether any contained control is interested in handling the message first and gives it the option to do that right away. This is done by calling the WinForms Control.PreProcessControlMessage() which ultimately calls the virtual Control.IsInputKey which controls use to determine a particular key is part of their input processing. The problem is that TextBoxBase does not say it's interested in handling the Backspace key (but it does so for Esc, Tab, Home, End, etc., and inherits the text keys from the base Control class implementation of IsInputKey).

Therefore, the WindowsFormsHost doesn't do anything further with the message, and the message enters the WPF input system, where it induces events tunneled and bubbled across the element tree. Eventually, NavigationWindow handles the key thanks to the built-in CommandBinding for navigating back. Bummer :(.

Fortunately, there are a couple workarounds:

  1. Create your own TextBox-derived control that fixes the IsInputKey implementation and use that in your controls:
    public class TextBox2 : TextBox 
    {
      protected override bool IsInputKey(Keys keyData)
      {
        return base.IsInputKey(keyData) || keyData == Keys.Back;
      }
    };
  2. Cancel NavigationWindow's CommandBinding for the backspace key by doing this in the constructor of your NavigationWindow-derived class:
    InputBindings.Add(new KeyBinding(ApplicationCommands.NotACommand, new KeyGesture(Key.Back))); 

     

I prefer the latter, as it doesn't require you to modify your existing WinForms controls. On the other hand, it removes the Backspace gesture for navigating back, but that's not too bad as I doubt users are actually going to miss it (how many people navigate back in the browser by pressing Backspace anyway?)

I created a small repro and reported this bug through Connect. Feel free to vote for it ;)

posted Wednesday, December 26, 2007 10:20 AM by kzu with 1 Comments

How to compile and run ALL WPF samples in the Windows SDK with a single command

Just issue the following PowerShell one-liner, and you'll compile and run each sample in the SDK (enter all in one line; broken here for readability):

PS C:\WinFx Samples\WPFSamples\AppModel\> 
gci -i *.csproj -r |
foreach
{
pushd ([System.IO.Path]::GetDirectoryName($_.FullName));
msbuild $_.fullname;
(gci -i *.exe -r |
%{ &$_.fullname; read-host; });
popd;
$_;
}

 

"WinFx Samples" is the folder where you installed the samples. It also works from a subdirectory, as you can see, so that you only run the subset of samples you're interested in. Executables may launch twice (from obj and bin outputs) and in some cases a dependent project might not get built before the main one, but hey, it's just a quick trick ;). The process will pause after executing each sample.

Very useful if you're just playing with the technologies and want to get a quick overview through samples of what's possible.

posted Thursday, November 15, 2007 8:13 PM by kzu with 2 Comments

Automatic generation of data-binding interfaces for data context objects

From the new Clarius Labs release:

XamlBinding Custom Tool

This custom tool will generate a partial class file implementing INotifyPropertyChange as well as a strongly-typed event for each property declared in the file it's applied to.
This is very useful when creating models or entities that are used in data-binding scenarios, and where an associated presenter needs to perform certain logic when a given property changes.


Communicating through these strong-typed events prevents tight coupling between the presenter and the UI, which now communicate exclusively through the model/data context.
A must-have for anyone doing "Binding Oriented Programming" ;)


The code generator solves the problem of having the presenter attach to the generic INotifyPropertyChanged.PropertyChanged event and checking the property by name, which must be kept in sync with the class definition and doesn't participate in refactorings.


It will also solve the initialization sequence and validation (post deserialization and afterwards) problem by implementing ISupportInitialize, ISupportInitializeNotification and IChangeTracking, which allows it to track changes automatically on its members and nested values. The public interface of your object remains uncluttered by implementing most of these privately. The data context class can simply call the provided EnsureValid at the beginning of each method call to make sure that the class has been properly initialized, and that if it's dirty, it's re-validated.


A DoValidate method is added to the main class to implement the actual property validation (where you could use the Standalone Validation Block for example), as well as RaiseXXChanged method calls on each property setter.


Finally, the generated code doesn't depend on anything other than System.ComponentModel, so WPF is not even required to use the codegen for other data-binding scenarios (WinForms should work fine).


Installation: run the MSI, or compile the included solution.
Usage: assign the value XamlBinding to any code file in VS. A corresponding file ending in ".XamlBinding.cs" (or .vb) will be generated automatically everytime the main file changes.

This release supports both VB and C#.

Enjoy!

posted Thursday, September 27, 2007 10:44 PM by kzu with 2 Comments

Evolving community guidelines on XAML and WPF

I just found the following excelent guidelines (especially the ones on resources organization) at Paul Stovell's blog.

I think it would be great to have community-developed guidelines on this topic. I've proposed to Paul to use Jottit, which is brain dead simple and uses the increasingly popular Markdown format.

Would be nice to point everyone to http://wpf.jottit.com/ and http://xaml.jottit.com/ for this kind of advice :)

posted Wednesday, September 26, 2007 9:30 PM by kzu with 1 Comments

Automatic input validation in WPF with data binding and Enterprise Library Validation Application Block

WPF provides validation infrastructure for databinding scenarios through ValidationRule objects. Only one built-in rule is provided, and you're expected to write most of these yourself. The way they work is by assigning one or more explicit rules to a binding:

<TextBox ...>
  <TextBox.Text>
    <Binding .>
      <Binding.ValidationRules>
        <local:JpgValidationRule/>
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

[Example from Adam Nathan's excelent book on WPF]

Note that the syntax is much more involved than the simple non-validating binding:

<TextBox Text="{Binding ...}">
</TextBox>

Another consequence of the provided support is that even if you reuse the data context (i.e. you provide more than one screen that binds to a given object type, say "Customer" object) in more than one editing scenario, you will need to duplicate the validators and make sure you keep them in sync. Also, you will typically be performing the same validation again in the object itself (i.e. the above binding probably sets a "Photo" string property), so you either take a dependency on WPF from your entities project just to call the same validation rules, or you duplicate the code to perform the validation (or you make your rules just expose a WPF interface over the same objects). In any case, there's no straight-forward way to reuse the logic, and maintaining the XAML and entities internal validation rules in sync is far from ideal.

The Microsoft Enterprise Library Validation Block (EntLib VAB) solves this problem by providing attribute (or configuration) driven rules that you can validate on the UI as well as on your entities. For example:

				public
				class
				Customer
				
				{
  privatestring firstName;

  [StringLengthValidator(1, RangeBoundaryType.Inclusive, 0, RangeBoundaryType.Ignore)]
  [NotNullValidator]
  publicstring FirstName

The code above specifies that the FirstName property must be a non-null, non-empty string. You could very easily create a custom validation rule such as NotNullOrEmptyValidator ;), but you get the idea.

EntLib VAB provides integration with WinForms and ASP.NET validation architectures, but not for WPF. Up to now, that is ;).

I've been working for the past few days on getting a kick-ass integration with WPF. It wasn't easy, and I found a couple not-so-nice things in the way, but I finally got the following working flawlessly (and without any hacks):

<Window ... xmlns:v="http://www.codeplex.com/entlibcontrib">
  <Grid>
    ...
    <Label Content="First Name:"/>
    <TextBox Text="{v:ValidationBinding FirstName}" .../>
    ...
    <TextBox Text="{v:ValidationBinding Address.USState}" .../>
    ...
  </Grid>
</Window>

This sample markup and the class above are part of a sample available with the download.

You can now do a simple search&replace of Binding with v:ValidationBinding and that's it. If your data context objects (or entities) provide validation attributes, they will get automatically applied to the binding, and all the WPF-specific handling on the UI for showing the errors and styling the elements with errors will be available to you for free.

 

The ValidationBinding is nothing more than a MarkupExtension that acts as a decorator for a Binding. Hence, the full data binding behavior is preserved, as I'm basically returning from my extension the result of evaluating the binding I'm decorating (prior to attaching my validation rule). From that point on, it's plain built-in validation rules and a few creative tricks to get around EntLib VAB's a bit weird "integration" model. It may be interesting to look for someone looking to implement their own markup extensions. In order to allow for the drop-in replacement scenario with the Binding class, I also expose all of the public properties of a binding, so that if you're using any of them, you get full fidelity behavior as they are all just pass-through getters/setters to the inner instance of the binding I'm constructing.

 

I've contributed this code to the EntLib Contrib project as part the Standalone Validation Application Block, which is a version of the VAB that doesn't depend on configuration at all. I've written about why XAML makes System.Configuration and Enterprise Library Configuration obsolete and why we need an EntLib Standalone Validation Application Block before. I may integrate this with the official EntLib 3.1, but it's not a high priority for me as I don't believe we need all the System.Configuration/ObjectBuilder baggage just for validation. If there's enough demand for it through the project discussions, though, I'm sure someone (maybe myself?) will get it done eventually.

 

Go get the release, and enjoy developing apps with full input validation and data binding in (almost) pure markup :

posted Wednesday, September 26, 2007 8:25 PM by kzu with 1 Comments

Serious flaw in WPF validation infrastructure for ValidationRule and Binding

Adam Nathan's excelent book on WPF states:

The validation check is invoked during any attempt to update the underlying data ... before a value converter is called (if present)...

This is further reinforced on MSDN where the validation process is explained:

image 

I can't understand how this design decision was made (it seems to be very much a rational decision, given how well documented it is).

Think about this scenario: you want to validate an integer value for a property (say, its range). There is a built-in converter for integers (but this could be a custom value converter, if it was your own type) that will ultimately assign a value of the right type to the property.

The validation routine should deal only with checking the range of an integer, not about how to convert it from whichever value it's passed-in by the binding target (i.e. the textbox control), which is already a responsibility of the value converter. If it has to perform conversion of the value prior to validating it, then a double conversion will happen if the value turns out to be valid.

What's worse, the validation rules have no way of accessing the value converter associated with the binding, so you'll end up either hardcoding which value converter to use inside the validator, or having to sync both manually (make validator code and XAML match).

 

Consider the alternative: first, the value converter tries to convert. Say It fails. It's like a validation error, but it's a precondition to validation that failed. With this approach, the validator can rely on the incoming value to always be of the appropriate type. A much better approach IMO.

 

I'm still enjoying XAML/WPF very much, but this is the second "not-cool" moment I had in a single week :(.

It's probably useless, but if you wanna vote to get this changed, go ahead. Thanks.

posted Tuesday, September 25, 2007 8:21 PM by kzu with 3 Comments

Why we need an EntLib Standalone Validation Application Block

In my previous post about XAML and its future as a domain model/DSL serialization format, I've argued that System.Configuration as well as Enterprise Library Configuration (part of the core of EntLib) will soon become irrelevant.

The caveat is that now you need to design your classes a bit differently. No more constructors where you validate the required input. Now everything is a property get/set, and validation needs to be done at a separate point in time (ISupportInitialize.EndInit), as well as on every property set (if you only want to allow the setter to be called before initialization is completed) and before every method (if you depend on the properties being initialized).

This sounds much worse than it actually is. With a fairly trivial base class (say, SupportInitialize), you can code your properties as follows:

public int Timeout
{
   get { return timeout; }
   set { EnsureNotInitialized(); /* validate value here, finally save value */ timeout = value; }   
}

public void DoSomething()
{
  EnsureInitialized();
  // do work
}

Property validation can become an annoying issue now, with validation scattered throughout your class on every property setter. Moreover, you may need to validate property values that depend on other properties, meaning the validation will have to be performed on the EndInit method forcedly.

It's pretty evident that you need something better for validation. Enter the SVAB (Enterprise Library Standalone Validation Application Block). It's basically everything you like about the Validation Application Block without any of the "legacy" dependencies on System.Configuration (you only need attribute-driven validation) and ObjectBuilder (which doesn't really fit on the XAML picture). The idea is that you can simply rely on the base class to perform the validation based on the properties metadata:

[RangeValidator(1, RangeBoundaryType.Inclusive, 60, RangeBoundaryType.Inclusive)]
public int Timeout

By inheriting from the base class, you gain automatic validation. The next step is a seamless integration with WPF (it's getting tricky). The existing solution is not ideal as it requires the use of an additional wrapper element in the markup, and incurs quite some overhead in its current implementation.

I'll follow up with the alternative I'm working on right now ;)

posted Monday, September 24, 2007 9:58 PM by kzu with 1 Comments

Why XAML makes System.Configuration and Enterprise Library Configuration obsolete

It may seem unrelated, but if you haven't read Fowler's article on DSLs (from Google cache if it's down like now), please do so now. It will help you understand why XAML goes far beyond WPF and presentation.

Configuration is typically nothing more than a DSL (external DSL in Fowler's terms) that serves to setup the variable parts of a certain runtime library or framework. For example, while a logging framework has many "static" or invariant components (i.e. logging "pipeline" execution, interaction between components, registration of variable components, etc.), there are variable parts which determine the runtime behavior, such as concrete trace listeners attached to given trace sources, the specific file a file listener will write to, and so on.

With careful API design, manipulating the actual runtime classes wouldn't be significantly different than manipulating its corresponding configuration classes or files. Your API is already a sort of DSL for your domain. For example, system.diagnostics can contain the definition of trace sources:

<source name="TraceTest" switchName="SourceSwitch" switchType="System.Diagnostics.SourceSwitch" >
  <listeners>
    <add name="console" />
    <remove name ="Default" />
  </listeners>
</source>

To achieve the same using the corresponding runtime classes, you would write:

				TraceSource source = newTraceSource("TraceTest");
source.Switch = newSourceSwitch("SourceSwitch");
source.Listeners.Add(newConsoleTraceListener());
source.Listeners.Remove("Default");

Note how close the two are. All you need is a good object (de)serialization format and you can go straight from the external DSL ("config" file) to the runtime objects without any intermediate representation. This wasn't possible before with .NET 1.x and 2.0 due to the lack of extensibility of the XmlSerializer and its excesive (IMO wrong) focus as a generic XML Schema <-> object converter (pretty much an unattainable goal if you want full fidelity both ways) and a webservices-only feature.

XAML, on the other hand, was thought from the beginning as a fully extensible object serialization format. With it, it's quite easy to achieve what's shown above, straight from the domain/runtime classes. One possible XAML syntax for the TraceSource example could be:

<TraceSource Name="TraceTest">
  <TraceSource.Switch>
    <SourceSwitch Name="SourceSwitch"/>
  </TraceSource.Switch>
  <TraceSource.Listeners>
    <ConsoleTraceListener />
  </TraceSource.Listeners>
</TraceSource>

(this wouldn't work with current diagnostics classes)

You end up having executable models :)

The only requirement to enable XAML serialization on your classes is to provide a public parameterless constructor, and public read/write properties for the stuff you want to get serialized. Validation can be done after deserialization by implementing System.ComponentModel.ISupportInitialize.

So, why would you want to develop and maintain a DSL/configuration model for your domain classes ever again?

And XAML is a v1 release, so I can only expect it to get better, more flexible and powerful over time. 

posted Monday, September 24, 2007 9:00 PM by kzu with 3 Comments

Forget about extending WPF data binding support

Databinding in WPF is implemented in the Binding class, which in principle looks like just a MarkupExtension. A MarkupExtension is a core piece of XAML markup "magic". It's rather simple, though:

public abstract class MarkupExtension
{
    // Methods
    protected MarkupExtension();
    public abstract object ProvideValue(IServiceProvider serviceProvider);
}

You're supposed to return a value to assign to the property that is using the markup extension, such as:

<TextBox Text="{l:MyMarkup}" />

A binding markup extension does a LOT more, though. The trick is that the binding ProvideValue implementation returns a BindingExpression object, not the straight value resulting from the evaluation (which is typically not even available at "markup resolution" time, when the file is being deserialized, which is the time that the XAML reader will call your markup extensions).

Now, so far, it doesn't look like there are many scenarios where you'd want to extend this infrastructure. After all, this is doesn't seem like much more than the old DataBinder.Eval.

Enter data validation and ValidationRules. This functionality is also baked-in the binding, and is key for showing input errors on WPF apps. Now this does look like an area where I might want some extensibility points. WPF gives you ValidationRule, which also looks straightforward enough:

public abstract class ValidationRule
{
    // Methods
    protected ValidationRule();
    public abstract ValidationResult Validate(object value, CultureInfo cultureInfo);
}  

It's so straightforward that it falls short in many real-world scenarios: the value of one property depends on the value of another, or on the (data) context. Moreover, I can totally see myself using some attribute-driven validation library to avoid writing a ton of validation rules myself. So, it would be very cool to be able to just attribute my data context properties with the validation information, and let an extended binding take care of the rest.

public class MyData : INotifyPropertyChanged
{
    private int value;

    [RangeValidator(1, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive)]
    public int IntValue
    {
        get { return value; }
        set { this.value = value; RaiseNotify("IntValue"); }
    }

...

That's when things get pretty ugly pretty fast. See, the ValidationRule has no context whatesoever. You can't access the binding where the rule is attached to, neither the data item/context, or the property you're validating, etc. It's utterly useless and impossible to extend.

So, I went deeper: why not create a Binding-derived class and override that? After all, it looks like I can inherit it:

public class Binding : BindingBase

intellisense for overrides on Binding

Crap, that doesn't look good. So let's try with BindingBase. If it's "Base", I bet it has some useful overrides!

image

If you're wondering how come I can't override ProvideValue which is a virtual inherited from MarkupExtension, here it is why:

public abstract class BindingBase : MarkupExtension
{
  public sealed override object ProvideValue(IServiceProvider serviceProvider)
  ...
}

Damn... if only that "sealed" was not there!!! 

So, how about creating a plain markup extension, and simply wrapping a true Binding? Remember the Binding.ProvideValue actually returns a BindingExpression? Maybe I can inherit that one and do something!

public sealed class BindingExpression : BindingExpressionBase, IDataBindEngineClient, IWeakEventListener

Mmm... there's another "Base" class there...

image

 That looks promising, but....

public abstract class BindingExpressionBase : Expression, IWeakEventListener
{
  internal abstract void ChangeSourcesForChild(BindingExpressionBase bindingExpression, WeakDependencySource[] newSources);
  internal abstract void InvalidateChild(BindingExpressionBase bindingExpression);
  internal abstract void ReplaceChild(BindingExpressionBase bindingExpression);
  ...
}

*internal abstract*? Dead end there too.

So, backing up a little, if I were creating such a reflection-based intelligent validation for a binding, all I would need is a way to get at the PropertyInfo/DependencyProperty/PropertyDescriptor (whichever applies) of the property I'm binding to, right? Surely the Binding needs the same thing eventually and must provide something like that. It does, but it's all internal stuff.

 

Summary: don't try to extend WPF data binding. If what's provided is not enough (i.e. ValidationRule), you're better off writing your own mechanism (will try to follow-up with another post if I manage to get the reflection-based validation to integrate somehow). For me, this is the first time I find a feature in WPF which sucks from the extensibility point of view :(.

posted Sunday, September 23, 2007 8:18 PM by kzu with 0 Comments

How to develop WPF applications in VS2005 and VS2008 simultaneously

In order to develop WPF apps in VS2005, you must install the corresponding CTP tools for it. These will basically bring in some new project templates, project types, MSBuild taks and designers. The designers are quite slow, and don't always work as expected. Being a CTP, and with VS2008 ("Orcas") already in the beta phase, I don't expect these to be updated anymore. Wouldn't it be great if you had a choice to use VS2008 or VS2005 with the same project files?

I'm not suggesting your entire team moves to VS2008. It's a pain to work with beta versions for your main development environment. Believe me, I developed GAX/GAT using pre-Beta1 versions of VS2005 (then "Whidbey") which we used to upgrade every couple weeks because it was quite unstable. It's no fun. Lots of time wasted.

That's why I think it's better if whoever on the team is willing to take the risk goes ahead and sets up VS2008 on a virtual machine (don't mess your main machine with betas!) and works there.

The process is quite straightforward:

  1. Clone your solution file, typically ending it with "VS2008.sln" to make it explicit that this is the "beta" solution file.
  2. Open in VS2008, and complete the upgrade wizard. This will remove from the project files the MSBuild import of WPF targets, as they are built-in VS2008.
  3. Close the solution, and open each project file with a text/XML editor. Towards the end of the file, add the following Import:
    ...    
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" /> </Project>

 

That import is required by VS2005 in order to properly compile the XAML files, but fortunately, it works just fine also in VS2008.

Now you can have half your team with VS2005 and the adventurous ones on VS2008 :)

posted Wednesday, September 12, 2007 8:07 AM by kzu with 1 Comments

How to get a gazillion XAML clipart for free

You surely know XAML subsumes pretty much all of SVG, right?

SVG has been around for quite more time than XAML, and even if hasn't taken off as fast as many expected, at least I could find a huge collection of freely available clipart.

Now all I needed was a way to convert all that stuff that you can get in a single gigantic download if you want to XAML.

I wish it was always this simple: go download ViewerSvg :).

Not only does the tool work great, it also comes with a library you can use too :). So instead of using the UI, I used the library to create a very simple console application (Svg2Xaml project) that will convert one or more files (or every file it finds recursively in one or more folders) to XAML.

The end result is not a gazillion (but you already guessed that, right?) but a very nice 7557 XAML files :). You can get the self-extracting exe (powered by the very fast and excellent compression tool 7zip) or a standard zip. If you're using IE, use Save Target As because Amazon S3 doesn't send the content-type for the file and IE might try to open it as text :S (a post for another day, why Amazon S3 sucks for standard web hosting). 

The download contains the PNG version from the original SVG so that you can not only compare, but also browse the clipart more easily from windows explorer.

I offered this converted library to the guys at the Open Clip Art Library, so it may end up there soon too.

Enjoy!

posted Friday, August 24, 2007 8:45 AM by kzu with 1 Comments

How to override system default styles in WPF

You should typically use system defined styles for your controls, such as:

<Label FontFamily="{x:Static SystemFonts.MessageFontFamilyKey}" Content="Hello World"/>

or:

  <StackPanel TextBlock.FontSize="{x:Static SystemFonts.MessageFontSize}"
              TextBlock.FontFamily="{x:Static SystemFonts.MessageFontFamily}">
        <Label Content="Hello World" />
  </StackPanel>

When using a static resource binding like this, you're basically giving up the ability to override the styles set by the current OS theme. This may be a good thing in many situations, to have a standard look & feel.

WPF rich styling allows you to very easily have "skins" for your app. These are just resource dictionaries (think CSS style files) that provide the styles for your controls. Moreover, you can switch these styles at run-time. Microsoft Expression Blend, for example, has a very cool default "Dark" theme and a "Light" one:

image

But what if you want to have a differentiating style, yet reuse system styles (maybe one of your skins is "Windows Default Style" which takes the current theme styles)? In this case, you should first turn your static resource bindings into dynamic resource bindings:

  <StackPanel TextBlock.FontFamily="{DynamicResource {x:Static SystemFonts.MessageFontFamilyKey}}">
        <Label Content="Hello World" />
  </StackPanel>

 Note that now you need to use the SystemFonts resource keys instead of the direct resource value. How in order to override the system default from a custom skin, all you need to do is add an entry to your resource dictionary using the same key:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    >
  <System:Double x:Key="{x:Static SystemFonts.MessageFontSizeKey}">11</System:Double>
  <FontFamily x:Key="{x:Static SystemFonts.MessageFontFamilyKey}">Segoe UI</FontFamily>
  <FontWeight x:Key="{x:Static SystemFonts.MessageFontWeightKey}">Normal</FontWeight>
</ResourceDictionary>

 

WPF is so cool, but until we get our heads around building desktop apps the way we learned (through years of pain) to build web apps (that is, no styles on the UI markup, maximize reuse of markup blocks, etc), it's pretty painful. Specially since WPF takes the web styling concept much further (developers should build the UI with the absolute minimal markup required to implement behavior and a graphics designer should come later and using Blend and styles, completely rework the visuals).

posted Monday, August 20, 2007 1:14 PM by kzu with 0 Comments