Property Undo/Redo Support

July 4th, 2009 by ryan No comments »

Undo/Redo support is one of those golden features that really differentiate a client app from many web apps.  There have been a number of methods/techniques to provide this support that I’ve run across from brute force, to the memento pattern, but none that lit any fire for me.

Here’s my answer to the problem using Anonymous methods and my first real attempt at creating (versus using) a Fluent Api.

The most common scenario for Undo/Redu support is form completion.  In your Domain or View Model this is often tied to Two Way bound properties.  Take the simple Person class:

public class Person
{
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }
}

The question is how to provide undo/redo support for a form (WPF often in my case) bound to a Person.  First, I create a ViewModel or Presenter for the Person class:

public class PersonViewModel : ViewModelBase
{
    private readonly Person _person;
 
    public PersonViewModel(Person person)
    {
        _person = person;
    }
 
    public string Name
    {
        get { return _person.Name; }
        set
        {
            _person.Name = value;
            OnPropertyChanged("Name");
        }
    }
 
    public DateTime BirthDate
    {
        get { return _person.BirthDate; }
        set
        {
            _person.BirthDate = value;
            OnPropertyChanged("BirthDate");
            OnPropertyChanged("Age");
        }
    }
 
    public int Age
    {
        get
        {
            var now = DateTime.Now;
            var age = now.Year - _person.BirthDate.Year;
 
            if (now < _person.BirthDate.AddYears(age))
                age--;
 
            return age;
        }
    }
}

Notice that we have a calculated property Age based on the BirthDate.  Any time we change BirthDate we want to make sure to notify that the Age is also changed, obviously. 

Here is the Fluent way to register Undo/Redo support for our PersonViewModel.Name property:

   1: public string Name
   2: {
   3:     get { return _person.Name; }
   4:     set
   5:     {
   6:         var oldName = _person.Name;
   7:  
   8:         _undoRedoManager
   9:             .WithUndo(() => _person.Name = oldName)
  10:             .RepeatAfterBoth(() => OnPropertyChanged("Name"))
  11:             .Do(() => _person.Name = value);
  12:     }
  13: }

Cool, but what is that doing?  Well, it’s pretty simply.  We are simply maintaining two queues; An Undo and a Redo queue.  Here is the longer syntax, without the Fluent helper Api that makes that a little clearer:

public string Name
{
    get { return _person.Name; }
    set
    {
        var oldName = _person.Name;
 
        _undoRedoManager.Push(() =>
                                  {
                                      _person.Name = oldName;
                                      OnPropertyChanged("Name");
                                  },
                                  () =>
                                  {
                                      _person.Name = value;
                                      OnPropertyChanged("Name");
                                  });
    }
}

This may not seem like much more, but if your Repeat part is more than one line, it gets a bit fragile.  Being able to specify the “After” stuff once is important.

From here you can use the IUndoRedoManager.CanUndo(), IUndoRedoManager.CanRedo(), IUndoRedoManager.Redo() methods to go back and forth.  Hook this up to your favorite ICommand implementation and you have most of what you might need.

 

Here is the full project with a WPF Sample and a VB sample.  The VB version is a little ugly since it doesn’t support anonymous methods without a return, but it works.

If you have suggestions (on the Fluent Api especially) let me know.  I’d really like to hear them.

Cin-Day Nerd Dinner Reminder

April 2nd, 2009 by ryan No comments »

This is just a quick reminder that tomorrow (April 2nd) we will be having our first Cin-Day Nerd Dinner.  If you are planning on coming, but haven’t RSVP’ed please do so here so that I can warn Dewey’s Pizza in West Chester of the raucous crowd to be expected.  Can’t wait to see everyone.

Action(Of T) Lambdas in VB

March 30th, 2009 by ryan 1 comment »

Until VB catches up with C# in the lambda support realm, here is how you can accomplish those Moq or Rhino.Mocks style verifications in VB:

dim someSub as Action(Of double) = AddressOf mockObject.SomeSub

Rhino.Mocks.Expect.Call( someSub)

It’s very frustrating going from the full support in C# to VB, but I’m finding sufficient workaround for most items.  I shouldn’t be so hard on VB.  C# doesn’t support inline of Xml which is pretty friggin’ sweet.

Outlook 2007 Huge Performance Improvement

March 26th, 2009 by ryan No comments »

If you use Outlook 2007 go get this hotfix (download here) now.  It’s crazy faster after this update.

 

Hurry!

Cin-Day Nerd Dinner

March 15th, 2009 by ryan 1 comment »

I’m jumping on the bandwagon and you’re all invited to follow.  I’ve scheduled a Nerd Dinner at Dewey’s Pizza in West Chester on April 2nd.  It’s just off the Tylersville exit on I75. 

I’ll have both the Dayton and Cincinnati User Groups announce the event at their upcoming meetings.  Please try to RSVP so I can warn Dewey’s of the wild crowd.

Scaling ItemsControl Items for Explicit Visible Items in WPF

March 12th, 2009 by ryan No comments »

Another part of the overall solution we were trying to come up with in conjunction my previous post on Scrolling Multiple Content Areas, we also needed to come up with a way to only show X number of items in the visible region of an ItemsControl wrapped in a ScrollViewer.  This was more tricky than I had initially anticipated, but the solution seems to work for us and its got relatively few moving parts. 

The end result of the concept can be visualize in this Xbap example.  By moving the slider at the bottom, you effectively change the width of the ItemsControl containing the colored Ellipses.  In order to do this as a reflection of the number of items contained within, you need another IMultiValueConverter.

Here is the meat of the code for the RelativeSizeConverter I came up with:

public class RelativeSizeConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if( values.Length != 3 )
            throw new InvalidOperationException("Don't do that!");

        double containerSize = System.Convert.ToDouble(values[0]);
        int totalItems = System.Convert.ToInt32(values[1]);
        int visibleItems = System.Convert.ToInt32(values[2]);

        var visibleSubgroupSize = containerSize / visibleItems;

        return totalItems * visibleSubgroupSize;
    }
}

Basically we figure out how wide things would be if we only had the visible items and then we apply that to all the items return.  In order for this to work, there cannot be any sizing between the ItemsControl and the ScrollViewer, but its a small price to pay.  That would look odd any way in my opinion, but maybe I am simply justifying the solution!

Anyways, putting this RelativeSizeConverter to use, we get:

<ScrollViewer x:Name="container" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
    <ItemsControl x:Name="itemsToScale">
        <ItemsControl.Width>
            <MultiBinding Converter="{StaticResource sizeConverter}">
                <Binding ElementName="container" Path="ActualWidth" />
                <Binding ElementName="itemsToScale" Path="Items.Count" />
                <Binding ElementName="scaleSize" Path="Value" />
            </MultiBinding>
        </ItemsControl.Width>
    </ItemsControl>
</ScrollViewer>

<Slider x:Name="scaleSize" Width="100" HorizontalAlignment="Center"
        Minimum="2" Maximum="{Binding ElementName=itemsToScale, Path=Items.Count}"
        Value="{Binding ElementName=itemsToScale, Path=Items.Count, Mode=OneWay}"
        IsSnapToTickEnabled="True" TickFrequency="1" />

Notice we use the ScrollViewer’s ActualWidth as our “containerSize”.  The ItemsControl itself has the “totalItems” count, and finally we are using a Slider with the name “scaleSize” to adjust the “visibleItems” count.

Code for the Xbap sample at the top can be downloaded here.

Scrolling Multiple Content Areas with a single ScrollBar in WPF

March 12th, 2009 by ryan No comments »

I struggled to solve a UI dilemma while at my current client.  For various reasons, we came across a situation in which we needed to scroll two areas of the view based on a single Scrollbar.  You can see what we were attempting and finally achieved here.

The end result is smooth, yet not quite intuitive so I thought I would share the results and steps taken.  The idea is to use a surrogate ScrollViewer as the source for the offset of a TranslateTransform being applied to content within a seperate ScrollViewer.

Unfortunately, the offset values exposed by the ScrollViewer are both readonly and opposite in force to which we would need to apply to the surrogate.  That is, if we scroll 20 units to the right on our ScrollViewer, we would need to offset the surrogate ScrollViewer by that amount in the opposite direction.  To resolve this problem, we use an IValueConverter to multiple the offset by –1.

Here we can see the Xaml used to layout and hookup this scenario:

<StackPanel>
    <ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
        <Grid>
            <Grid.RenderTransform>
                <TranslateTransform X="{Binding ElementName=mainScroller, Path=HorizontalOffset, Converter={StaticResource multiplyConv}, ConverterParameter=-1}" />
            </Grid.RenderTransform>
            <!-- Content Scrolled by another goes here -->
        </Grid>
    </ScrollViewer>
    <ScrollViewer x:Name="mainScroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <!-- Content Scrolled on its own accord goes here-->
    </ScrollViewer>
</StackPanel>

You can see that we have two ScrollViewers and the one which will be controlled (versus the controller) has a container element with a TranslateTransform applied to its RenderTransform property.  This is what we will be using to move the controlled content in the opposite direction of the ScrollBar.

If you find that your content for each are not the same size, you can always use a MultiBinding to accomodate the relative scrolling distance as applied to the offset.

You can find the source to the sample Xbap here.

STUFF & FOR XML PATH

March 2nd, 2009 by ryan No comments »

On the get the job done front, my client had a question today that, while it makes me cringe a little to answer the way I did, it got him moving again.

[Thanks to Mike Levy for pointing me in the right direction]

My client has a small administrative utility that displays Things.  Things can be applied to 0 or more categories.  Looks kinda’ like this:

image

He’s using a 3rd party control for the grid itself which provides really easy filtering, but it wouldn’t work with the data structures to which he had been binding.  So he wanted to know how he could return the category listing as a delimited string directly from the T-SQL.

Here’s the gist of his schema (nothing fancy):

Schema

 

How we solved it:

SELECT Id, Name, STUFF(
            (SELECT cat.Name + ‘; ‘ AS [text()]
            FROM Category AS cat
            INNER JOIN ItemCategories AS ic ON cat.Id = ic.CategoryId
            WHERE (ic.ItemId = Item.Id) FOR XML PATH(”)
            ), 1, 0, ”
              )
FROM Item

Obviously we would like to move the UI junk out of the DB, but he can deal with that when he understands the 3rd party control a little better.  Right now, he’s giving his customers some more features.

The WPF Mindset

November 3rd, 2008 by ryan 3 comments »

WPF is awesome, but it requires the developer to make a pretty big shift in their thinking of UI.  I’ve had a hard time explaining this to people, but last week, I had an opportunity to work through an example with Steve Gentile.  That’s the basis of this walkthrough.

 

The Scenerio

Steve has an ASP.Net website he maintains called ForgottenSkies.com.  One of the pages displays a map from the game with events that were captured from the server.  You can see the ASP.Net version here.  Steve was looking to create a Silverlight version of this page.

The Wrong Mindset

The initial response from most developers would be to put an image on the “form” and then figure out a way to place some composite control or other image that would denote each event’s location.  If they had the inclination they might use the DataContext, but they likely would also iterate each Event in some collection and manually place these controls within some container.  I believe Steve was trying to place them into a really big grid with lots of Rows and Columns to represent the Latitude and Longitude of the map.

The WPF Mindset

Decomposing this scenario is vital to using WPF properly.  When it comes down to it, the map is really just a way to visualize a collection of Events. If we wanted we could easily make this a list, couldn’t we?  So lets start there:

For the purposes of this demo, the Window.DataContext is set to an instance of an AttackService, which looks like the following:

public class AttackService
{
    public ObservableCollection<Attack> Attacks { get; private set; }
    public int MapSize { get; set; }

    public AttackService()
    {
        this.MapSize = 160;
        this.Attacks = new ObservableCollection<Attack>();

        Random random = new Random();

        for(int index =0; index < 10;index++)
        {
            var attack = new Attack
            {
                Description = string.Format("Attack {0}", index),
                Latitude = random.Next(0, MapSize),
                Longitude = random.Next(0, MapSize),
                Type = (AttackType)random.Next(0, 2)
            };


            this.Attacks.Add(attack);
        }
    }
}

Our goal is to display the Attacks collection, so let’s do that:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <ListView ItemsSource="{Binding Attacks}" />
    </Grid>
</Window>

imageWe have a ListView which has it’s ItemsSource bound to our Attacks collection.  Nothing special.  WPF doesn’t know how to display an Attack class so if we would just see a 10 instances of our class name listed.  We’ll get there though.

 

For starters, lets look at these as if they were listed in a table (or GridView). To do this, we’ll tell the ListView to use a style, attacksList.  We’ll place this style definition in our Window’s Resources collection for convenience of our demo.

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Resources>
        <Style x:Key="attacksList" TargetType="ListView">
            <Setter Property="View">
                <Setter.Value>
                    <GridView>
                        <GridViewColumn DisplayMemberBinding="{Binding Description}" />
                        <GridViewColumn Header="Latitude" DisplayMemberBinding="{Binding Latitude}" />
                        <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding Longitude}" />
                    </GridView>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Grid>
        <ListView x:Name="attacksList" ItemsSource="{Binding Attacks}" Style="{StaticResource attacksList}" />
    </Grid>
</Window>

What we’ve done is given ourselves a way to set values or properties on the ListView.  imageFor the sample you see here, we set the type of View for the ListView to be a GridView. 

The Map

The ultimate visualization of this information is to display it contextually in a map.  It’s important to remember that this is still a list, we just want to visualize the list differently.  To do this we need to change our attacksListStyle.

<Style x:Key="attacksList" TargetType="ListView">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <Canvas>
                    <Canvas.Background>
                        <ImageBrush ImageSource="Images\Map.jpg" />
                    </Canvas.Background>
                </Canvas>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>

    <Setter Property="ItemContainerStyle" Value="{StaticResource attackContainerStyle}" />
</Style>

Instead of setting the View of our ListView, we are now setting the ItemsPanel to a Canvas.  This tells the ListView to place all of the items its charged with listing into the Canvas.  We have chosen canvas, because we will want to place each item via coordinates (X/Y, Left/Type) rather than stacking, row/column, etc.  The background of our Canvas is going to be a JPG version of our map.  Backgrounds and Foregrounds are always brushes, so we are using an ImageBrush to draw our map on the Canvas background.

The second property we are setting is the ItemContainerStyle to the attackContainerStyle.  The ListView places each item into a container rather than displaying it directly.  In order to place it correctly, we need to set the Top and Left properties of this ItemContainer.

<Style x:Key="attackContainerStyle">
    <Setter Property="Canvas.Top" Value="{Binding Latitude}" />
    <Setter Property="Canvas.Left" Value="{Binding Longitude}" />
</Style>

This binds the Canvas.Top and Canvas.Left DependencyProperties to the Attack.Latitude and Attack.Longitude properties respectively.  To bring this together, we have our ListView bound to a collection of Attacks and the ItemContainer contains each Attack and has its DataContext set to that Attack it is meant to contain.

At this point we have most of what we want.  imageEach little black image you see below represents an attack.  This is done by setting the DataTemplate for the Attack type.

<DataTemplate DataType="{x:Type local:Attack}">
    <Image Source="Images\AlliedTerritory.gif" />
</DataTemplate>

The problem you will find is that if you allow the Window to be scaled beyond the size of the map size as it existed when creating the Attack Latitude and Longitude, the points on the map will not scale or move with the map.  I dealt with this by implementing an IMultiValueConverter.  I won’t go into it here as it doesn’t pertain to our Mindset discussion, but I’ve included it in the attacked source code.

Summary

If you come into WPF trying to create cool graphics or visualizations from the outset, you will miss the power and productivity of the platform.  WPF and the oft-sited Designer/Developer scenario actually thrive when you begin to simplify your application into the basics and then apply visualizations.

Get the source for this project here:  Source Code.  The Xaml for this is literally only 67 lines with spaces.  There is 1 line of custom code in code behind, and you saw the AttackService above.  It’s dead simple.

Why VMWare over Virtual PC

October 21st, 2008 by ryan No comments »

I just found the reason why I’m going to slap down almost $200 bucks for VMWare Workstation over Virtual PC: Unity.  I am using a coworkers VM to help on a project he’s been working on, but he uses VMWare and I use Virtual PC.  Instead of buy a license I’m using VMWare Player.  Well I found option under VMWare called Unity so of course I clicked it.   Well it’s possibly the coolest thing I’ve seen ever. 

Here is my desktop with VMWare running as normal:

VMWare Normal

 

and here it is with Unity:

image

All of the windows/applications that were active inside the VM are now native windows in my host.  Also, when you go down to the start button, a second Start Button with the name of the VM becomes visible that allows you to start other apps in this same mode.  I don’t care what you say, that’s just awesome.