Wednesday, April 7, 2010

How to handle WPF events with MVVM on the cheap (without a framework).

In my experience, developers starting out with WPF and MVVM need (and want) to slowly wade in.   WPF is already a significant mind set shift and adding the overhead of learning MVVM and an MVVM framework is often just too much.

This blog entry assumes you have a basic understanding of WPF and MVVM and are just starting to ask the question – how do I handle WPF events in an MVVM architecture?   Normally you would handle WPF events in the code behind.   Of course this is anathema in MVVM (kidding – but code behind in MVVM is controversial as of now).   MVVM is all about continuing the concept of separation of UI and business logic and, in my humble opinion, adding any significant business logic functionality in the code behind is not a best practice (note I am a strong believer in unit tests which require separation of UI and business functionality).

Back to the original question – since adding significant functionality for events in code behind is not desired for MVVM the only other place for this code to go is in the View Model – but how does the code in the View Model get triggered.   There are many MVVM frameworks and all of them have their extensions to handle this.   But for many small to medium size applications, incorporating an MVVM framework isn’t required - a few simple base classes are all that is needed.   So if you want something simple to handle WPF events with MVVM for smaller programs or to just to continue getting your feet wet without having to incorporate an MVVM framework, read on.

One of the first things we learn in WPF/MVVM is that clicks on buttons are a type of event that is translated to Commands which we can handle on our View Model.   Unfortunately nearly all other events do not have this convenience. However, we can easily create this behavior cheaply!   The concept is to handle the event and then translate that event to a button command.   The questions are:  Where will the intermediate buttons be loacted and how to do the translation from event to button command?

Let’s answer the button location first.   Simple, the button is anywhere in the XAML markup but with visibility set to hidden!   Hidden buttons can still fire commands and are perfect for our needs.

In the sample code for this blog we will be handling the Closing Window and Close Window events.   Applications often ask “Do you want to save?” during shutdown. This sample code shows how this can be implemented.   Note however that the method described here can be used with any event!

Here are the obligatory screen shots of the sample application.

User clicked X on window - closing window event command fired:


User answered yes to shutdown - close window event command fired:


The following two hidden buttons will handle our closing window event and our close window event. Note the command bindings which will fire the command handler code in our View Model.

<!-- Hidden buttons to help us convert events to commands -->
<Button Visibility="Hidden" Name="ClosingMainViewModel" Command="{Binding Path=ClosingMainViewModelCommand}"/>
<Button Visibility="Hidden" Name="CloseMainViewModel" Command="{Binding Path=CloseMainViewModelCommand}"/>

Now to answer the second question – how to translate the event to a command handler.   Here is where simple code behind comes to the rescue.  We handle the event in the code behind and execute the command on the hidden button for that event.

private void MainView_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    ClosingMainViewModel.Command.Execute(e);
}

private void MainView_Closed(object sender, System.EventArgs e)
{
    CloseMainViewModel.Command.Execute(null);
}

In my opinion we don’t violate any MVVM rules since there is no significant business logic in the code behind – we are simply translating our event to a command.

The full code is available here. 

Enjoy!
Todd Stephan

Saturday, March 27, 2010

Microsoft Offers Career Help

Microsoft has launched a new website to help advance your career called Thrive. The site includes resources to enhance your skills, connect to your community, improve interviewing skills and search for jobs. One of the most valuable resources for the developer is a podcast series on improving your soft skills. Even if you have great communication, work and interviewing skills this is a great reminder of those skills and each webcast includes valuable web resources/references at the end of the webcast.

Enjoy!
Todd Stephan

Wednesday, March 24, 2010

How to Create ToolTips on the cheap – integrating ToolTip and IDataErrorInfo.

In a previous post I discussed how to create a more pleasing to the eye error template.

But it has been noted by the WPF guru Karl Shifflett and others that this simple mechanism for integrating tooltips and IDataErrorInfo has some draw backs. The most annoying of which is that you can’t assign a tool tip to any control that might show a data validation error! If you do assign a tooltip, the data validation error will not appear.

There is a simple work around. Use the tag of the control to hold the tooltip and use it for the content of the tooltip when there is no data validation error. Here are a few screen shots of the tooltips and data validation in action.





Here is the relevant code snippet:

<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<!--
In the following xaml, Path=AdornedElement.(Validation.Errors)[0].ErrorContent} throws an exception if no error condition is present,
although the exception does not cause a crash. It shows up in the debug window which is annoying.
This work around implemented here - http://joshsmithonwpf.wordpress.com/2008/10/08/binding-to-validationerrors0-without-creating-debug-spew/
-->
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" />
</Trigger>
<Trigger Property="Validation.HasError" Value="False">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
</Trigger>
</Style.Triggers>

The tooltip is set using the tag of the control as content in the second trigger when Validation.HasErrors is false. Note that I implemented Josh Smith's fix for debug spew.

The full code is available here. 

Enjoy!
Todd Stephan

Apple News Aggregator - aggregates Top 200 Apple news sites

Check out the site - very well formatted for quick scanning / searching.

http://apple.alltop.com/

Enjoy!
Todd Stephan

Saturday, March 13, 2010

Yet Another WPF Boolean Converter – or not! The only WPF Boolean Converter you need.

I sat down to write yet another WPF BooleanToXXX converter and decided this was not acceptable. Bool converters are used to select between two choices based on the true/false value of the bool in question. To me this cried out for a generic class to handle all of these cases so I created a class called BooleanToValueConverter as shown below.


public class BooleanToValueConverter : IValueConverter
{
// parameter specifies what to return based on the boolean
// parameter is of the format value1;value2 where value1 is returned if the bool is false and value2 is returned if the bool is true
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
System.Diagnostics.Debug.Assert(value is bool);
System.Diagnostics.Debug.Assert(parameter as string != null);
if (value == null || !(value is bool) || parameter as string == null)
return null;

string[] valueList = (parameter as string).Split(new char[] { ';' });
System.Diagnostics.Debug.Assert(valueList.Length == 2);
if (valueList.Length != 2)
return null;

if ((bool)value == true)
return valueList[1];

return valueList[0];
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

The following XAML snippets show how the converter is used. The trick to making this converter generic is the ConverterParameter which takes a string of two options separated by a semicolon. The converter parameter is of the format value1;value2 where value1 is returned if the bool is false and value2 is returned if the bool is true.


<Window.Resources>
<local:BooleanToValueConverter x:Key="booleanToValueConverter"/>
</Window.Resources>

<Button Content="_Delete" Visibility="{Binding Path=IsDeleteAllowed, Converter={StaticResource booleanToValueConverter}, ConverterParameter=Collapsed;Visible}" />

Now in the example screen shot below we see that the Delete button is not visible since the IsDeleteAllowed property is returning false and thus the button is collapsed.



I have used this in many locations to choose between two values including visibility as shown above, two margins, grid row/column widths, and more. The grid row use case is interesting because you can make a grid row disappear by binding to the row height and using a BooleanToValueConverter to set the width to 0 when you want the row to disappear.

The full code is available here. 

Enjoy!
Todd Stephan

Sunday, February 28, 2010

WPF Error Template - a more pleasing user experience

We have all seen the default error template which is a red border around the element being adorned. In my experience, when a user first sees a screen of red boxes there is a gasp and groan. A sea of red certainly doesn’t inspire the user.



I searched around and didn’t find anything to my liking so I took a cue from Excel and mimicked the method used there. Excel uses a small triangle when a user inserts a comment or when there is a potential formula inconsistency. I thought the subtle visual cue was still effective and didn’t give the user the feeling of dread upon seeing a full dialog box of them.



The code is fairly simple. A Polygon is used to draw and fill the triangle. Here is the code to achieve this effect:

<!-- Template when a validation error exists. Note that two items sit in the
same grid which causes them to overlap. Also note the margin - I felt that an
offset of 1 had a better look since the triangle sits just inside the edge of the
control. You may want to customize the position for ComboBoxes. -->

<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Polygon Fill="Red" Stroke="Red" StrokeThickness="1" Points="0,0 5,0 5,5"
Margin="1,1" HorizontalAlignment="Right" VerticalAlignment="Top"></Polygon>

<AdornedElementPlaceholder />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>

The full code is available here.

Enjoy!
Todd Stephan

Saturday, February 27, 2010

My first blog in many years...

This is my first blog in many years. Why the absence? I moved to Chicago a little over 4 years ago and it took some time to get adjusted to my new life, coworkers and weather! I also started my MBA with a concentration in Project Management shortly after and just didn't have time for everything. My current interests on the technology front are WPF (C#, .NET, etc) and Apple iPhone development. I am always interested in new business and project management processes and techniques. I am currently piloting a project with Agile (Scrum) and I am a CSM (Certified Scrum Master). Most of my posts will be related to these topics - thanks for visiting and I always appreciate feedback.

- Todd