Dinner with Dr. WPF (continued)

October 30, 2008

As Walt recently mentioned here, a few WPF Disciples got together for dinner last night.  Sitting at the table was none other than Dr. WPF in the flesh.  I won’t repeat everything Walt so vividly depicted, but there are some other aspects of that experience worth noting.

I had never heard someone request a side order of XAML before; neither had the waitress.  After she told the good Doctor that they did not have XAML on the menu, he burst into hysterical laughter while threatening to “initiate a furious bout of fisticuffs.”  What can you expect from a guy who drinks kool-aid by the gallon?

After that uncomfortable episode ended, the Doc explained to us, in great detail, how man can control the movement of the Loch Ness monster through animation of TranslateTransforms and RotateTransforms.  I didn’t quite follow, but it sounded very interesting.    He lost me when he mentioned that his plan requires “one brave scuba diver to dive down to Nessy and assign the transform group to her bosom.”

When the steaks arrived, the Doc expressed discontent with the size of his meal.  He kept touching the steak with two fingers and sliding them apart.    Eventually he gave up and started to eat his meal, grumbling about some missing resource key.


Conditionally adding elements declared in XAML to the element tree

June 12, 2008

In my previous post I showed a markup extension that only creates elements declared in your XAML file if you are running with Full Trust from the CLR. After I posted that, Laurent Bugnion, a fellow WPF MVP and WPF Disciple (as well as a really great guy!), asked if I know of a way to use a markup extension to perform the moral equivalent of a #if … #elif … #endif in C#. That was an interesting question, so I decided to investigate. It turned out to be very simple.

Here is my new If class:

You can use that markup extension to conditionally add elements to the element tree, based on whether you are building a DEBUG or RELEASE build. Here is a demo of that class being used:

If you create a DEBUG build, the top TextBlock appears in the Window, otherwise the bottom TextBlock shows up. Naturally, if you have a more complicated set of build options, you can enhance my If class to take those build configurations into account.

[EDIT]

After I posted this, Andrew Smith pointed out that there is another way to achieve this, as seen here.  The approach involves the use of the Markup Compatibility XML namespace, and another assembly to contain your own XML namespaces.  That technique is more complicated to set up, but the elements in excluded blocks will not be created/seen by the XAML reader.

[/EDIT]

Download the source code here. NOTE: Change the file extension from .DOC to .ZIP and then decompress it.


WPF Podcast Player

December 19, 2007

This post shows a simple WPF application that plays podcasts.  The podcast information comes from RSS feeds.  The user has rudimentary control over a podcast, such as pausing and resuming it, adjusting the volume, and muting the podcast player.  Almost the entire application is in XAML.

In a previous post on my other blog, I showed how to create a very basic podcast player using Silverlight 2.0.  After finishing that project, I wanted to come back to WPF and build a better podcast player.  It was such a relief to return to the feature-rich world of WPF, after living a Spartan existence over in Silverlight land.

Here is what my WPF Podcast Player looks like:

WPF Podcast Player (screenshot)

As you can see, the application definitely does not provide a luxurious experience; it is pretty bare bones.  But it does offer the fundamental things need to start, pause, resume, and stop a podcast.  The only part of the application that required me to write code is pausing and resuming a podcast, since you have to call the Pause and Play methods on MediaElement.  You cannot call those methods from XAML.  In addition, a simple value converter class exists to detect if a podcast is an MP4 file, so that we do not show it (MediaElement does not seem to support that format).

All of the podcast RSS feeds are hard-coded into the ComboBox on top of the UI.  The selected item in the ComboBox is used to get the data source from which all podcasts are retrieved.  Here is how that works:

WPF Podcatcher (data sources)

Since we are binding to an RSS feed, which is an XML document, we can use XPath to pull out the values displayed in each ListBoxItem.  Here is the template used to display a podcast in the ListBox:

WPF Podcast Player (podcast template)

The remainder of the UI is just a bunch of controls which provide the user with a means of consuming and adjusting podcasts.  Here is the XAML for most of the UI:

WPF Podcast Player (controls)

Download the Visual Studio 2008 project here: WPF Podcatcher for VS2008

Download the Visual Studio 2005 project here: WPF Podcatcher for VS2005

Be sure to change the file extension from .DOC to .ZIP and then decompress the file.


LINQing WPF value converters

December 1, 2007

A new blogger in the WPF world,


Handling a routed event in any class

August 6, 2007

When you handle an element’s event in XAML, the event handling method you specify must exist in the class contained in the associated code-behind file.  That’s fine for most situations, but what about when you want that element’s event(s) to be handled by another class?  In the first version of WPF there is no support for declaratively handling events with methods in arbitrary classes.  This blog post shows a way around that limitation.

I created a utility class named RoutedEventManager which exposes an attached property named HandlerInfo.  That property is of type RoutedEventHandlerInfo; a class which stores the information needed for this technique to work.  Here is an example of those classes in use.  The XAML seen below shows a Button in a Window.  The Button’s Loaded event is handled by a utility class named ButtonHelper.

RoutedEventManager (XAML)

As seen in that XAML snippet, the Button’s Loaded event is not directly assigned a handler.  The HandlerInfo attached property is set to an instance of RoutedEventHandlerInfo, specifying the information needed to eventually assign a handler to the Loaded event.  Here is the method which handles the Button’s Loaded event, in the ButtonHelper class:

RoutedEventManager (ButtonHelper)

The implementation details of ButtonHelper are unimportant, but it is worth pointing out why it handles the Button’s Loaded event.  An attached property can only be set on an element once.  In the demo app we need to handle three of the Button’s events (Click, MouseEnter, and MouseLeave), so we assign handlers to those events in the method which handles Loaded.  If you only need to handle one event “externally” on the element, say the Click event, this extra step is unnecessary.  In that case you could just establish a handler for the Click event with RoutedEventManager in XAML.

We will not delve too deep into how this works, but free feel to explore the code in the demo app at the end of this post.  For those of you with insatiable curiosity and no time to poke around in a demo project, here is the basic gist.  When the HandlerInfo attached property is set on an element, eventually this method in RoutedEventManager is invoked:

RoutedEventManager (setting new handlerinfo)

That method depends on the CreateHandler method of the RoutedEventHandlerInfo class, which is implemented like this:

RoutedEventManager (creating the event handler)

As of this writing you can only specify a static method as the event handling method.  I’m considering whether it would be helpful to be able to specify an instance method on a particular object.  If you have a compelling reason for that feature to exist, please let me know.

These classes are not “done yet.”  I’m still pondering what other functionality might be useful for them, so feel free to drop me a comment if you have a neat idea. 

Download the demo app here: RoutedEventManager (demo project)  Be sure to change the file extension from .DOC to .ZIP and then decompress it.


Customizing the selected item in a ListBox

July 30, 2007

This blog post demonstrates how to make the selected ListBoxItem in a ListBox display differently than unselected items.  We will see how to add an extra Button to the selected item, but you can add whatever you want by using the technique shown below.  This is what the demo app looks like when you run it and select an item:

Customized SelectedItem (screenshot)

The trick is to set the ListBox’s ItemTemplate to a DataTemplate which knows when to expose the extra visuals for the selected item(s).  Here’s an example of a template which knows how to do that:

 Customized SelectedItem (template)

That DataTemplate creates two Grids.  The outer Grid has two rows: the top row is for the normal item content, and the bottom row hosts the selected item’s extra content.  The inner Grid is in the bottom row.  That inner Grid has a Style which knows when to hide itself (i.e. when the ListBoxItem is not selected).

Here’s a ListBox which consumes that template:

 Customized SelectedItem (ListBox)

Download the demo project here: Customized SelectedItem (Demo) Be sure to change the file extension from .DOC to .ZIP and then decompress it.


A lightweight alternative to DropShadowBitmapEffect

July 24, 2007

This blog post reviews how to create drop shadows around WPF elements in a way which does not have a noticeable impact on an application’s UI performance.

One of the biggest letdowns about the first version of WPF is the fact that bitmap effects are so terribly slow.  The most commonly used bitmap effect is the one which creates a drop shadow around an element; the DropShadowBitmapEffect.  If you start using that class to create drop shadows you will soon find your UI performance suffering immensely.  This is a shame considering how visually appealing a well-placed drop shadow can be.

Here is a screenshot which shows a ListBox with a drop shadow.  The drop shadow was created by adding a DropShadowBitmapEffect to the ListBox’s BitmapEffects collection.

Lightweight DropShadows (using DropShadowBitmapEffect)

If you were to repeatedly resize that Window, you would find that the rendering of the ListBox flickers and is slow.  Why?  The problem is that bitmap effects are not hardware accelerated, and they can execute quite often.  WPF’s support for bitmap effects requires them to be created in unmanaged code (usually C++).  There is currently no way to hardware accelerate those unmanaged routines.  I suppose it is possible to write your own bitmap effects which run themselves on a graphics card’s GPU, but that is way beyond the scope of this post!!

Fortunately there is an easy way to create drop shadows around WPF elements which does not incur any noticeable performance overhead.  The trick is to decorate an element with a SystemDropShadowChrome element.  SystemDropShadowChrome does not have nearly as many settings that you can adjust, but it provides a decent drop shadow for many situations.  Here’s a screenshot of a ListBox which is decorated with that element:

Lightweight DropShadows (usign SystemDropShadowChrome)

SystemDropShadowChrome lives in an obscure assembly and namespace.  If you want to use it, you must add a reference to one of the PresentationFramework.* assemblies (there are currently .Aero, .Luna, .Royale, and .Classic).  You will find the class in the Microsoft.Windows.Themes namespace. 

Each of the PresentationFramework.* assemblies has its own version of the SystemDropShadowChrome class, but after checking them out in Reflector I found out that they are all exactly the same.  Someone must have written the class once, and then pasted it into each of those assemblies.  So, there is no need to worry about choosing the “right” class to use at runtime, based on the user’s current operating system theme settings.  Any one of them will do.

In this post’s demo application I chose to reference the PresentationFramework.Aero assembly, as seen below:

Lightweight DropShadows (assembly references)

Once you have the assembly reference in place, you need to map the Microsoft.Windows.Themes namespace in your XAML file, like this:

xmlns:aero=”clr-namespace:Microsoft.Windows.Themes; assembly=PresentationFramework.Aero”

At that point, it’s simply a matter of wrapping an element in a SystemDropShadowChrome element, and perhaps setting its Color and/or CornerRadius properties.  Here is the XAML which does this in the demo project:

Lightweight DropShadows (XAML)

To read another perspective on the SystemDropShadowChrome class, read here.

Download the demo project here: Lightweight DropShadows (demo project)  Be sure to change the file extension from .DOC to .ZIP and then decompress it.


Follow

Get every new post delivered to your Inbox.

Join 288 other followers