Using DispatchTimer to help in UI Behavior

Wednesday, October 21, 2009 12:49:49 AM (GMT Daylight Time, UTC+01:00)

I have ran into this problem a number of times developing UI.  You have business logic that needs to execute when a user is done entering some piece of data or criteria.  The business logic may be complicated and takes longer then what is considered an acceptable threshold (somewhere around 3 seconds). 

Your first thought might be to use the TextChanged, SelectionChanged or KeyUp events to detect change in the targeted control and run the business logic… but after you have implemented the approach you find out that the logic is just too expensive and the UI appears locked or sluggish.  The LostFocus event doesn’t work well either… it doesn’t get fired in some situations (clicking on a toolbar or selecting a menu).  

The approach that I have used both in WinForms and WPF development is to monitor the TextChanged event and when event has gone dormant for a specified duration, run the business logic. The key is not to wait an extended period of time to run the logic, but rather execute the logic at a tight enough interval that the application appears responsive after the user is has completed their data entry.

This is what the code does.

In the constructor we setup a DispatchTimer to fire at a default interval and subscribe to the TextChange event of our target control.

public ExecuteOnDormant(TextBox ctl, Action action)
{
    _control = ctl;
    _action = action;
    _dormantTimer = new DispatcherTimer();
 
    _defaultTimeout = new TimeSpan(0, 0, 0, 0, _timeOut);
 
    if (_timeOut == 0)
        _dormantTimer.IsEnabled = false;
    else
    {
        _dormantTimer.Interval = _defaultTimeout;
        _dormantTimer.IsEnabled = false;
        _dormantTimer.Tick += new EventHandler(RunBusinessLogic);
 
        //assumging that it is only a textbox we are watching... if we want to make this more generic... then add a  to KeyUp or SelectionChanged
        _control.TextChanged += new TextChangedEventHandler(ctl_TextChanged);
    }
}

The handler for the TextChange event then resets the DispatchTimer… this allows the user to complete typing the value before complex business logic is run.

/// <summary>
/// Handles the TextChanged event of the control that we are monitoring to run business logic on.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> instance containing the event data.</param>
private void ctl_TextChanged(object sender, TextChangedEventArgs e)
{
    ProcessActivity();
}
 
/// <summary>
/// Processes the activity(keyboard and mouse) in the shell to ensure that it stays active and does not fire the 
/// IdleTimeOut event.
/// </summary>
private void ProcessActivity()
{
    //reset the timer, the control has changed.
    _dormantTimer.IsEnabled = false;
    _dormantTimer.Interval = _defaultTimeout;
 
    //now enable the timer, as we want it to fire when it times out.
    _dormantTimer.IsEnabled = true;
}

When the user has stopped typing long enough for the interval to expire the business logic is executed.  To make this class usable across different controls we use an Action delegate to define the business logic.

private void RunBusinessLogic(object sender, EventArgs e)
       {
           //disable the timer, so it doesn't continue to run the business logic
           _dormantTimer.IsEnabled = false;
           Dispatcher.CurrentDispatcher.InvokeOnUIThread(_action);
       }

The business logic definition looks like this:

public partial class Window1 : Window
    {
        private ExecuteOnDormant _dormantWatcher;
        private Random _randomVal = new Random();
        public Window1()
        {
            InitializeComponent();
 
            _dormantWatcher = new ExecuteOnDormant(txtCriteria, ExecuteComplexBusinessLogic(txtCriteria));
        }
 
        private Action ExecuteComplexBusinessLogic(TextBox control)
        {
           return  () =>
                       {
                           Mouse.SetCursor(Cursors.Wait);
                           
                           //run complex business logic...
                           int val = _randomVal.Next(1,3);
 
                           //make it seem like a long time
                           Thread.Sleep(1000);
                           
                           //give the rich feedback on whether the item passed validation
                           control.Background = (val == 1) ? Brushes.LightGreen : Brushes.Salmon;
 
                           Mouse.SetCursor(Cursors.None);
                       };
        }
    }

You will notice that it uses a extension method off of the CurrentDispatcher called InvokeOnUIThread.  This method ensures that the business logic is executed in the correct thread context.  Below is the implementation for the extension method.

/// <summary>
/// Invokes on the UI thread if necessary.
/// this.Dispatcher.InvokeIfNecessary(() =>
/// {
        //do something that might need to be invoked
/// });
/// </summary>
/// <param name="dispatcher">The dispatcher.</param>
/// <param name="action">The action.</param>
public static void InvokeOnUIThread(this Dispatcher dispatcher, Action action)
{
    if (Thread.CurrentThread == dispatcher.Thread)
    {
        action();
    }
    else
    {
        dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate { action(); });
    }
}

You can download the demo project.

PDC 2008 - Microsoft shows huge commitment to WPF in development tools

Thursday, October 30, 2008 10:08:30 PM (GMT Standard Time, UTC+00:00)

Microsoft has used WPF to write tools in the past... Expression Blend and Design being two of those tools among others.  Its extremely exciting to see that they are making even a bigger commitment to WPF as a platform for writing applications.  This week at PDC they announced that the shell of the Visual Studio 2010 IDE will be written in WPF.  Since Visual Studio has a huge legacy code base and many tools already built a top the DTE, not all of it will be converted to WPF and managed code.  However, one of the most noticeable changes will be that the code editor will be written using the rich text capabilities of WPF... which means that we will now have a very powerful presentation model (rich text, animation, etc) in the IDE that we can leverage to build better IDE experiences. This also means that the bar for extending Visual Studio IDE will be greatly reduced.

Customizing the Visual Studio Start Page

One of the extension points for VS 10 will be modifying the start page... which is possible today, but the model for integrating into other aspects of the IDE (like macros or commands) will be far simpler than what we have today.  To customize the home page  will require you to modify the Xaml file that is found in Documents\Visual Studio 10\Start Page\StartPage.xaml.  To integrate with Visual Studio commands will be a matter of passing in a well-known string identifier for the command.

Managed Extension Framework (MEF) enables the Add-In Model

The new text editor will be composed of MEF components which can be swapped out and customized by supplying your own implementation.  The customization capabilities will be limited to the following areas :

Classification: Rich Text Formatting

- Which will allow us to create a richer reading environment for code.

- Ability to leverage WPF's rich text capabilities.

 

Adornments: Power Graphics

Any WPF visual - can render a UI element anywhere.

- Associate adornments with text, render comments in the MSDN document style inline with code.

- Support for animation and behavior of different aspects of the code.

 

Margin and Scrollbar Customization

- Allows for the ability to create visual representations of structure in the margins of the code editor... this looked very similar to the way ReSharper implemented their Marker Bar (shown below).

image

 

 

Intellisense and Smart Tags

- Contribute to completion of Intellisense ... meaning that we can now filter items.  For a long time I have wanted the ability to modify the intellisense so that when I need to I can just view only the properties, methods or events for a type... and it looks like now the bar may be lowered enough to implement something like this relatively easily.

- Override the presentation of the parameter help or quick info

- Add menu items to smart tags

 

If you want more information, you can check out the videos on channel9

WPF Office Ribbon and other goodies released at PDC

Tuesday, October 28, 2008 10:55:06 PM (GMT Standard Time, UTC+00:00)

You can get the newest WPF Ribbon, DatePicker/Calendar control, preview of VisualStateManager (VSM) for WPF from the WPF Toolkit site... in addition the DataGrid is now out of CTP and was released.

image

Another WPF Debugging Tool - Cracked.NET

Monday, October 27, 2008 12:52:27 AM (GMT Standard Time, UTC+00:00)

I met with Josh Smith tonight and he had mentioned that he has released a new debugging tool for WPF (I think it will also work on WinForms) called Cracked.NET.  One of the things that is interesting about this is that Josh is using Iron Python to inject scripts into the application at runtime.

Binding TextBlock to RichText

Thursday, October 02, 2008 4:16:41 AM (GMT Daylight Time, UTC+01:00)

I was doing some updates to a debugging tool for WPF... I was adding a filter feature that would highlight the characters in yellow as you entered them.

image

I was originally using a TextBlock to bind to by information and that worked fine until I was adding the rich text feature of highlighting the search string.  You can't bind to the Text property of a TextBlock and pass in a Inline object (shown below, which is what supports the rich text within a TextBlock).

image

The only time you could pass Inlines to the TextBlock was in the constructor... that wasn't going to work with a binding.

That forced me down the road of investigating the use of a Label, as I could bind to the Content property and it would render the Inline elements I was creating just fine... this resulted in the rich text highlighting.  However, there was a couple of issues... since this is constantly taking information from the Win32 OutPutDebugString call (see DbgMon) it really needed to perform well when rendering.  I was noticing a little bit of a performance hit... not substantial, but noticeable (maybe that is substantial then).   In addition, wrapping text with in a Label turned out to be not so easy. 

So I went back to the TextBlock.  I tried to bind to the Inlines property.  There was a couple of issues with that... one it wasn't a DependencyProperty and the other issue was that it was read-only.

Solution:

Since I had written a converter to handle highlighting of the filtered text within the string, I was returning Inlines. I modified the convert so that I would return a TextBlock and since I was creating the inlines I was able to pass them into the constructor of the TextBlock.  Instead of binding to a TextBlock in the DataTemplate, I binded to a ContentControl, which would intern using the converter to get the TextBlock that was generated with the filter text highlighted.

The resulting converter looked like the following:

public class StringToHighlightConverter :  IValueConverter
   {
       public object Convert(object values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
       {
           //get the data found in the text
           string dataText = (string)values;
 
           if (!string.IsNullOrEmpty(dataText) && !string.IsNullOrEmpty(TextSearchFilter.SearchClause))
           {
               List<StringOccurance> highlightedWords = new List<StringOccurance>();
               foreach(string token in TextSearchFilter.TokenizedSearchClause)
               {
                   highlightedWords.AddRange(StringEx.FindStringOccurances(dataText, token));
               }
               
               if (highlightedWords.Count == 0)
                   return CreateTextBlock(dataText);
 
               int textStartIdx = 0;
               string plainWord = string.Empty;
               int wordOccuranceIdx = 0; //keep count of the words we have proccessed
               Span phrase = new Span();
 
 
               for (int i = textStartIdx; i < dataText.Length; i++)
               {
                   if (wordOccuranceIdx == highlightedWords.Count)
                   {
                       //We have reached the maximum word occurances, add the plain word span
                       Span wordSpan = CreateWordSpan(dataText.Substring(i), false);
                       phrase.Inlines.Add(wordSpan);
                       break;
                   }
 
                   //get the currrent word that needs to be highlighted
                   StringOccurance wordOccurance = highlightedWords[wordOccuranceIdx];
 
                   if (wordOccurance.StartIndex == i)
                   {
                       //add the plain word span
                       if (!string.IsNullOrEmpty(plainWord))
                       {
                           Span wordSpan = CreateWordSpan(plainWord, false);
                           plainWord = string.Empty;
                           phrase.Inlines.Add(wordSpan);
                       }
 
                       Span highLightedWordSpan = CreateWordSpan(wordOccurance.Value, true);
                       wordOccuranceIdx++;
                       //issue with this
                       i = i + (wordOccurance.Value.Length - 1);
                       phrase.Inlines.Add(highLightedWordSpan);
                       continue;
                   }
 
                   if (i >= dataText.Length)
                   {
                       //add the plain word span
                       //Span wordSpan = CreateWordSpan(dataText.Substring(i), false);
                       Span wordSpan = CreateWordSpan(plainWord, false);
 
                       phrase.Inlines.Add(wordSpan);
                       break;
                   }
 
                   plainWord = plainWord + dataText[i];
               }
 
               return CreateTextBlock(phrase);
 
           }
           return CreateTextBlock(dataText);
       }
 
       private TextBlock CreateTextBlock(Inline text)
       {
           TextBlock block = new TextBlock(text);
           block.TextWrapping = System.Windows.TextWrapping.Wrap;
           return block;
       }
       
       private TextBlock CreateTextBlock(string text)
       {
           Span textSpan = new Span();
           textSpan.Inlines.Add(text);
           return CreateTextBlock(textSpan);
       }
 
       private Span CreateWordSpan(string text, bool highlight)
       {
 
           Span wordSpan = new Span();
           if( highlight == true)
           {
               wordSpan.Background = Brushes.Yellow;
           }
               
           wordSpan.Inlines.Add(text);
           return wordSpan;
 
       }
    
       public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
       {
           throw new NotSupportedException("Not supported");
       }
   }

The xaml in the DataTemplate looked like:

<DataTemplate>
    <ContentControl Content="{Binding OutPutDebugString, 
                    Converter={StaticResource highlightConverter}}" 
                    VerticalAlignment="Top" />
</DataTemplate>

Updating Highlighted Search in WPF

Monday, September 29, 2008 3:00:44 AM (GMT Daylight Time, UTC+01:00)

I released a sample that both filters and highlights the results in a listbox a while back.  I got an email from A.V.Ebrahimi about highlighting multiple words when searching.  The sample that I had done... was done in about 20 minutes before a presentation to demonstrate another feature in WPF that didn't require GDI to draw embellishments on the control.  Anyway, to make a long story short... he gave me a solution on how to do it and I forgot to post it as promised:

UI Designer and Developer Workflow

Tuesday, September 09, 2008 10:25:06 PM (GMT Daylight Time, UTC+01:00)

I presented at MDC 2008 today about UI Designer and Developer workflow when building WPF and Silverlight applications.

Here is the summary for the session:

Xaml has been touted as the markup language that will make it easier for both UI designers and developers to work on the same UI artifacts. This is accomplished by tooling, processes, and selecting the right approach based on the skills of your resources. There are a number of different approaches that are available for defining how a developer and UI Designer will interact to build Xaml (WPF Desktop and Silverlight) based applications. The talk will discuss all the different aspects that are required including the tooling that enables the interaction between developer and UI designer and important aspects that you should be aware of when using these tools. It will cover the different processes that will need to be put in place to support the software development lifecycle. And we will evaluate the different approaches and discuss how to select the right approach based on the composition of your team. We will also discuss important guidance and best practices that will help your teams be successful in designing your own UI designer and developer workflow.

 

Mix 08 Recap

Friday, April 18, 2008 5:57:53 PM (GMT Daylight Time, UTC+01:00)

Last week Jeff Brand (Microsoft DPE), Rocky Lhotka and I sat down to discuss the good and bad of the Mix 08 conference.  You can get the podcast from Jeff Brand's blog.

Silverlight Controls Demo - Silverlight Beta 1

Friday, April 18, 2008 5:57:27 PM (GMT Daylight Time, UTC+01:00)

You can find the Silverlight Beta 1 control demos here.  Including some controls we don't see in WPF like the DataGrid, Calendar and Hyperlink button.

Expression Learning Resources

Friday, April 18, 2008 5:53:57 PM (GMT Daylight Time, UTC+01:00)

Expression Blend is great for doing some things that are more complex in Visual Studio when authoring xaml... some of those things include Transformations and  Animations.  The Expression site has a entire area dedicated to helping people get up to speed on the Expression suite tools.

nerd + art

Wednesday, March 19, 2008 2:41:42 PM (GMT Standard Time, UTC+00:00)

Nerd + Art was one of the presentations at Mix 08 that was presented by Identify Mine.  It was to help the artiest release their inner nerd.  It was a group of 10 snippets that could be used in Visual Studio to help artists add certain capabilities to their working models / prototypes of WPF applications.  Those same snippets are also pretty helpful for someone getting up and going on WPF.

nerdsnippets

Using WPF for Business Applications

Wednesday, March 12, 2008 3:14:38 AM (GMT Standard Time, UTC+00:00)

Here is the presentation that I did today at the Microsoft office in Minneapolis today.  It was a segment on real world WPF.  It discusses how WPF isn't just about the cool North Face demo or the New York Times Reader, but there is advantages for using it in every day enterprise applications.  The presentation and source can be found below.  The demo's include sample WPF implementations for Authorization, Application Activity Monitoring and Highlighted Search similar to what you experience when you search in Outlook.  None of the examples are currently at production quality, but with a little polish they could get there.

Authorization UI

Demonstrates how to use attached properties on different UI elements to change whether they are hidden or disabled based on the users authorization for a specific UI Element.  The authorization UI is mocked up, but should give you an idea of how you can extend any UI Element with property metadata to help in the UI authorization scenario.

image

Application Activity Monitoring

Demonstrates how you can implement monitoring of the mouse and keyboard to determine if you should disable the UI of the application... in this implementation it will blur after 5 seconds of no activity.  This implementation still needs some work... you can still edit data after the screen has blurred and there is no login dialog which would pop up after the blur has taken place so you can resume work.

image

Highlighted Search

Similar to how Microsoft Outlook highlights the search term as you type, this implementation demonstrates a simplified version using a listbox.  There is a bunch of sample data... you can test it out with up to 10,000 records.  If you want to generate more seed data, you can use the seed data api that I put together.

image

Mix08 Keynote & Announcements

Thursday, March 06, 2008 2:22:16 AM (GMT Standard Time, UTC+00:00)

This isn't a summary of all the announcements, just a couple of one that I thought were notable.  If you want to see the actual keynote or any of the sessions they have all been recorded and are up on the Mix08 site. Some of the things that I found interesting included:

 

IE 8 Beta 1 is Available 

They have added some interesting features and extensibility mechanism to work with content on the web and are working to abide by the W3C standards for browsers.  Here is a recap of what will be in IE8

  • CSS Compliance
  • Start of implementing HTML 5
  • Web Slices
  • Activities

You can find more about the last two features here.

 

Silverlight 2.0 Beta 1 Released 

They also announced that Silverlight 2.0 Beta 1 was released.  Which has added the awaited support for a number of basic controls.  In the previous release the target was around rich media and a basic subset of xaml.

 

Blend 2.0 Beta 1 is available

Expression Blend Beta 2 was released.

 

Coolest Application Built on Silverlight 2.0

One of the most impressive demos of the keynote was the launch of Hard Rock Cafe's memorabilia collection based on Silverlight and SeaDragon.

Choosing when to use a Third Party Data Grid in WPF

Friday, February 01, 2008 8:43:34 PM (GMT Standard Time, UTC+00:00)

Before I began working with WPF I couldn't believe that Microsoft hadn't supplied a basic data grid.  Once I began working with WPF in depth I thought... why are people complaining so much about not having a data grid, WPF gives you the power to build your own relatively easily.
Here is some of the insight and considerations for making a selection between a Third Party Data Grid and the WPF GridView.  I looked at both the Xceed DataGrid for WPF and Infragistic’s xamGrid from a feature prospective and this is what I have determined.

Disadvantages of using third party grid:
•    Having to deploy another dependency for the application.
•    If other applications reuse the development artifacts (we are using a composite application approach where modules could be consumed by other applications) they will also have to take on the dependency.
•    UI Designers that want to model UI will have to take it as a dependency.
•    Third party control API may not be consistent with the WPF framework in the way it implements styling, templating or other WPF concepts.

Advantages of using third party grid:

No development required to support common grid scenarios that are not supported with the current WPF grid.

The typical grid scenarios for Data Grids would include:
•    Sorting and Sorting Indicators
•    Multi-field sorting
•    Grid line support
•    Grouping
•    Alternate row highlighting
•    Auto Fit for column for expanding columns to fill available space
•    Grouping with multilevel support
•    Rich in place editing (events to support Row / Column / Cell activity or validation)
•    Auto select of cell editor by data type
•    Unbound columns to enable displaying of calculated data
•    Nesting support
•    Row Height based on largest item

If you consider the above features I think it makes it a lot easier to determine when a third party data grid should be considered.


GridView – The following are fairly easy to implement in the current WPF GridView and examples are found on the Internet.
•    Sorting and Sorting Indicators
•    Multi-field sorting (I know I saw this somewhere, but can't find the link)
•    Grid lines support (Example1, Example2)
•    Grouping (Example1, Example2)
•    Alternate row highlighting (Example1, Example2)
•    Auto Fit for column for expanding columns to fill available space

Third Party Data Grid - As soon as you cross into needing support for one or more of the following, a third party data grid makes more sense because of the increased complexity to implement these types of features in WPF with an average development team.
•    Grouping with multilevel support
•    Rich in place editing (events to support Row / Column / Cell activity or validation)
•    Auto select of cell editor by data type
•    Unbound columns to enable displaying of calculated data
•    Nesting support
•    Row Height based on largest item

Building Business Applications with WPF

Friday, January 11, 2008 1:24:44 AM (GMT Standard Time, UTC+00:00)

Jeff Brand (Microsoft Developer Evangelist) and I sat down the other day and recorded a pod cast (download) that discusses building business applications using WPF.  If you want some insight into building business applications with WPF, check it out. We talked about decisions around why to use a WPF for a business application, challenges of getting up to speed on WPF, Workflow with designers and a little bit about building enterprise composite applications.

Registered for MIX 08

Thursday, January 10, 2008 3:15:57 AM (GMT Standard Time, UTC+00:00)

I registered for MIX 08, so I just thought I would show off my new bling.  This will be the first year I have attended MIX... I decided it would be a good opportunity to see how others are using WPF.  I have always been a huge UI advocate and had an interest in creating refined user interfaces.  The power of WPF has really made this substantially less complex then the hoops that you had to jump through before.

However, with this additional power it really helps to have a UI Designer involved to make the UI's look more professional... this also introduces other issues... from a workflow and collaboration perspective during the software development lifecycle.

If a UI Designer isn't involved developers can always use the default styles that are provided with the WPF controls, but then you just get an old fashioned Windows application look and feel.  Many developers struggled with usability and behavior not to mention color coordination, style and layout.  With this additional freedom how can we use it in a responsible fashion.  Most projects don't include UI Designers to help guide us.

There is a number of presentations that I am interested in hearing what they have to say about designers and their role in the development lifecycle.

How Design Impacts the Bottom Line
Audience(s): Business, Creative

Is design really worth the investment? Or should it be the first item to cut when time and money are tight? Hear from businesses about their experiences around the payoff for implementing design.

Applications = Designers + Developers
Audience(s): Creative, Technical

Behind the scenes, great applications feature not only clever code, but they also expose a great user experience and design. We illustrate the workflow possible using the Microsoft Expression tools and Microsoft Visual Studio to create great applications.

Example of deriving from a WPF User Control

Friday, June 22, 2007 3:24:31 PM (GMT Daylight Time, UTC+01:00)

I put together an example of how to derive from a base class for a WPF user control when the base class is found in the local assembly or a reference one.  If you want more detail on how to do the derivation you can read my previous post.

UserControlDerivationExample.zip (77.79 KB)

Using a ResourceDictionary stored in a seperate assembly

Monday, May 07, 2007 10:06:44 PM (GMT Daylight Time, UTC+01:00)

I had some trouble getting this to work, so I thought others may benefit from it.  Here is the scenario... we have a bunch of control templates and styles we want to define for our application... with the possibility at some point we may want to modify aspects of what the application looks like.  The modification would be handled by replacing the dll that held the style of the application.  I had read in the documentation that it was possible do embed a ResourceDictionary in another assembly and then reference the assembly using merged dictionaries and an URI.

The syntax to identify the merged dictionary was simple enough... and I added it to my app.xaml.

   1: <Application x:Class="WPFThemeSeperation.App"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     StartupUri="Window1.xaml"
   5:     >
   6:     <Application.Resources>
   7:         
   8:         <ResourceDictionary>
   9:             <ResourceDictionary.MergedDictionaries>
  10:                 <ResourceDictionary Source="pack://application:,,,/OtherTheme;component/skin.xaml"></ResourceDictionary>
  11:                      </ResourceDictionary.MergedDictionaries>
  12:               </ResourceDictionary> 
  13:     </Application.Resources>
  14: </Application>

The above configuration indicates that I want to use an assembly called OtherTheme... which is the name of the a dll with out the .dll extension... and in that assembly called OtherTheme I have skin.xaml in the root of the project.

I then created a class library project which was added to my solution.  Clicked on my newly added project and selected Add -> New Item... at that point I selected a ResourceDictionary (WPF) from the dialog and named it skin.xaml... which caused it to add some references to the project. I modified the skin.xaml file to create a new style for my buttons (shown below):

 

   1: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   2:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   3:     >
   4:     <Style TargetType="{x:Type Button}">
   5:         <Setter Property="Foreground" Value="white"/>
   6:         <Setter Property="Margin" Value="1"/>
   7:         <Setter Property="Template">
   8:             <Setter.Value>
   9:                 <ControlTemplate TargetType="{x:Type Button}">
  10:                     <Grid>
  11:                         <Rectangle x:Name="GelBackground" Opacity="1" RadiusX="9" RadiusY="9"
  12:                            Fill="Red" StrokeThickness="0.35">
  13:                             <Rectangle.Stroke>
  14:                                 <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
  15:                                     <GradientStop Color="White" Offset="0"></GradientStop>
  16:                                     <GradientStop Color="#666666" Offset="1">
  17:                                     </GradientStop>
  18:  
  19:                                 </LinearGradientBrush>
  20:                             </Rectangle.Stroke>
  21:                         </Rectangle>
  22:                         <Rectangle x:Name="GelShine" Margin="2,2,2,0" VerticalAlignment="Top"
  23:                            RadiusX="6" RadiusY="6" Opacity="1" Stroke="Transparent" Height="15px">
  24:                             <Rectangle.Fill>
  25:                                 <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
  26:                                     <GradientStop Color="#ccffffff" Offset="0"/>
  27:                                     <GradientStop Color="Transparent" Offset="1"/>
  28:                                 </LinearGradientBrush>
  29:                             </Rectangle.Fill>
  30:                         </Rectangle>
  31:                         <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
  32:                     </Grid>
  33:                     <ControlTemplate.Triggers>
  34:                         <Trigger Property="IsMouseOver" Value="True">
  35:                             <Setter Property="Fill" TargetName="GelBackground">
  36:                                 <Setter.Value>
  37:                                     <RadialGradientBrush>
  38:                                         <GradientStop Color="Lime" Offset="0"/>
  39:                                         <GradientStop Color="DarkGreen" Offset="1"/>
  40:  
  41:                                     </RadialGradientBrush>
  42:                                 </Setter.Value>
  43:                             </Setter>
  44:                         </Trigger>
  45:                         <Trigger Property="IsPressed" Value="true">
  46:                             <Setter Property="Fill" TargetName="GelBackground">
  47:                                 <Setter.Value>
  48:                                     <RadialGradientBrush>
  49:                                         <GradientStop Color="#ffcc00" Offset="0"/>
  50:                                         <GradientStop Color="#cc9900" Offset="1"/>
  51:                                     </RadialGradientBrush>
  52:                                 </Setter.Value>
  53:                             </Setter>
  54:                         </Trigger>
  55:                     </ControlTemplate.Triggers>
  56:                 </ControlTemplate>
  57:             </Setter.Value>
  58:         </Setter>
  59:         <Style.Triggers>
  60:             <Trigger Property="IsMouseOver" Value="True">
  61:                 <Setter Property="Foreground" Value="Black"/>
  62:             </Trigger>
  63:             <Trigger Property="IsPressed" Value="True">
  64:                 <Setter Property="Foreground" Value="Black"/>
  65:             </Trigger>
  66:         </Style.Triggers>
  67:     </Style>
  68: </ResourceDictionary>

 

When I ran the application... I got the following error:

System.Windows.Markup.XamlParseException was unhandled
Message="'pack://application:,,,/OtherTheme;component/skin.xaml' value cannot be assigned to property 'Source' of object 'System.Windows.ResourceDictionary'. Cannot locate resource 'skin.xaml'. Error at object 'System.Windows.ResourceDictionary' in markup file 'WPFThemeSeperation;component/app.xaml' Line 10 Position 25."

 

Hmmm... I knew it was in there... but it wasn't finding it.  I thought it might be a path issue so I popped open Reflector and looked in the binary to see if it was compiled to a resource... nothing was there.

One thing I noticed in the documentation was a reference to marking the resource with a Build Action "Resource"... the problem was that the Resource option wasn't available when looking in the Build Action... it defaulted to page when it was added.  I assumed that when the ResourceDictionary file was added through the New Item menu it had handled the modification of the project that would expose "Resource"  as an option... after all it brought in a couple of references automatically.  That assumption was not correct... and this was the missing piece to the puzzle.

Modifying the Project File

You need to make the following modifications to the class library project that hosts your styling. Add line 2 as shown below which will import the .Net 3.0 build targets:

   1: <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   2: <Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" />

Also modify the build action for your skin.xaml file to "Resource"... as shown below:

   1: <ItemGroup>
   2:   <Resource Include="skin.xaml" />
   3: </ItemGroup>

Now when you build you should get a resource compiled into the

Note: If you change the name of the resource file at a later time, make sure you Clean the Solution and then Rebuild the Solution... both options are found on the context menu of the Solution in the Solution Explorer, otherwise you will get an error indicating that your xaml file can not be found.

Another Note... this isn't the most flexiable approach. We took a very simple stance.... we would not allow our users to change the look of the application and we didn't want to have to recompile the entire application if we decided to change the style of the application.  This allows us to compile a seperate "skin" dll and drop it in the application directory at anytime to change the way the application looks. 

Dinner Now Sample

Friday, March 23, 2007 1:28:08 AM (GMT Standard Time, UTC+00:00)

Looking for a sample that implements all of the latest Microsoft technologies in one package.... Including:

Windows Workflow

Windows Presentation Foundation

Windows Communication Foundation

Vista Sidebar Gadgets

Windows PowerShell

Linq

ASP.NET 2.0 AJAX Extensions

Mobile Device Development

 

You can find all of this in the DinnerNow.NET reference application.

WPF Training

Thursday, March 22, 2007 3:04:41 AM (GMT Standard Time, UTC+00:00)

I missed the MVP Summit last week to attend WPF training.  I have to admit I was initially pretty disappointed that I was going to spend last week in WPF training, and miss out seeing some old friends at the MVP Summit.  But since I just started with a new client and needed to get up to speed on WPF I decided to forgo the MVP Summit.

It turned out the training was done by Robbie Ingebretsen, who was a Program Manager on the WPF team, responsiable for many of the controls that were built for WPF.  It ended up being a great week of training and I learned alot.  If your interested in WPF training, take a look at Identity Mine where Robbie works.

Exploring WPF Applications

Friday, March 16, 2007 1:37:04 AM (GMT Standard Time, UTC+00:00)

There is some pretty cool applications being built in WPF.  I really like Denounce... Notescraps is also pretty cool.  You can find a list of applications written in WPF on Channel9.

WPF Examples

Monday, March 12, 2007 12:30:29 PM (GMT Standard Time, UTC+00:00)

Looking for some really good WPF examples.  The two that I like the most are Kevin's Bag O Tricks and UnFold (Diagraming Tool).

Where did the Application.ThreadException go in WPF?

Monday, March 12, 2007 12:30:06 PM (GMT Standard Time, UTC+00:00)

Spending alot of time in WPF lately... today I ran into something interesting.  There is no Application.ThreadException in the WPF Application object.

It looks like the only other option to catch exceptions at the edge of the application is to use AppDomain.UnhandledException.