Archive for the ‘WPF’ category

Registering WPF “Views” with Windsor Fluent API

October 26th, 2009

I’ve had this nagging issue for some time now with WPF views that are registered for an interface.  The Views themselves are WPF UserControls:

namespace SomeApp.Views
{
    public partial class SearchView : UserControl, ISearchView
    {
        public SearchView()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            throw new NotImplementedException();
        }
    }
}

 

I would love to have registered all views in one go by simply adding:

container.Register(AllTypes
                       .FromAssembly(assembly)
                       .Where(x => x.Name.EndsWith("View"))
                       .WithService.FirstInterface());

 

Unfortunately, this registers my Views for System.Windows.Media.Composition.DUCE+IResource implementations which I really couldn’t care less about.  The interface I’m looking for is the “nearest” interface for lack of a better term.  Fortunately, Windsor lets us provide a delegate selector for the interface.  Here’s what I’m using now:

container.Register(AllTypes
                       .FromAssembly(assembly)
                       .Where(x => x.Name.EndsWith(suffix))
                       .WithService.Select((t, bt) =>
                                               {
                                                   var serviceType = t
                                                       .GetInterfaces()
                                                       .Where(x => x.FullName.StartsWith("SomeApp"))
                                                       .FirstOrDefault();

                                                   if (serviceType != null)
                                                       return new[] {serviceType};
                                                   else
                                                       return new[] {t};
                                               }));

 

Use Select versus FirstInterface allows us to provide back the Service type(s?) that this class is implementing.  The StartsWith is obviously fragile, but I’m green right now, so I’ll see you on the other side.

Property Undo/Redo Support

July 4th, 2009

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.

Scaling ItemsControl Items for Explicit Visible Items in WPF

March 12th, 2009

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

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.

The WPF Mindset

November 3rd, 2008

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 WPF? This is why…

July 2nd, 2008

http://dnrtv.com/default.aspx?showID=115

Just awesome!  I didn’t see anything far fetched other than they must have a great designer on staff.  I wonder if the designer was doing the Blend work?

MiniMVC for WPF

April 24th, 2008

Tonight I presented for the Dayton .Net Developers Group on WPF.  Unlike my previous Lap Around WPF @ CONDG which was meant as a beginners guide to WPF, this time we were skipping the basics and plunging into a framework I’ve dubbed MiniMVC. 

MiniMVC is, primarily, a set of DependencyProperties and custom ICommand’s which allow you to specify a Controller for any FrameworkElement and/or Action in the logical tree.  Unlike Dan Crevier’s D-V-VM pattern or Josh Smith’s MVC pattern implementations which rely on explicitly defining RoutedCommands for each action, binding them up manually, and other unpleasantries (in my humble opinion – these guys still know more than I) with this framework you can use any old class, with any old method, and start executing actions immediately.  Here’s an example:

<Window x:Class=”PostDemo.Window1″
      
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
      
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
      
xmlns:mvc=”http://cromwellhaus.com/wpf/minimvc”
      
xmlns:demo=”clr-namespace:PostDemo”>

    <mvc:MiniMVC.Controller>
        <
demo:Window1Controller/>
    </
mvc:MiniMVC.Controller>
   
    <
StackPanel>
        <
TextBox x:Name=”txtMessage”Text=”" />
        <
Button>
            <
mvc:MiniMVC.Action>
                <
mvc:ControllerAction Trigger=”Click”Action=”Echo”
                      
Return=”{BindingElementName=txtResult,Path=Text}”>
                    <
mvc:ParameterParameterName=”message”
                          
Value=”{BindingElementName=txtMessage,Path=Text}” />
                </
mvc:ControllerAction>
            </
mvc:MiniMVC.Action>
          
Write Something
      
</Button>
        <
TextBlock x:Name=”txtResult” />
    </
StackPanel>
</
Window>

Here’s what’s going on:

First, we have this class, Window1Controller, which we are attaching to the Window’s MiniMVC.Controller property. 

Second, we are setting the MiniMVC.Action property on the Button to a ControllerAction triggered by the button’s Click event.  When Click occurs the Action to be taken on the Controller is Echo.  We want to pass into Echo the value of the txtMessage TextBox Text value as parameter message.  Also, we’re specifying that the Return value from Echo should be applied to the Text property of the txtResult TextBox controller.

Cool?  So what’s really going on?  Well, MiniMVC is handling the most importantly the OnChange event handler for the Action DependencyProperty and using that to dynamically create an ICommand class called ActionCommand.  It’s also looking up the Click RoutedEvent via Reflection and adding the ActionCommand’s Execute method as the event handler for the located Click RoutedEvent.  There is ZERO code in my code-behind.  The Designer has free-reign to apply the Controller and action to any RoutedEvent he/she chooses and we can all sleep well knowing our concerns are separated.  A happy marriage in my book.

I must give a lot of credit to Rob Eisenberg’s super-awesome Caliburn project on which much of the Action dependency property is based.  His framework is full featured to the tilt, but is a little over done to my liking with the extensive use of Dependency Injection.  If you find MiniMVC useful, though, I highly recommend you watch for progress on Caliburn.

You can download the full solution, including demo’s, from the presentation, as well as the above example project here.


New Year Indeed

January 15th, 2008

It has been quite a New Year so far.  As of today, Monday January 14th 2008, I have left Speedway and the Speedy Rewards™ team and found new employment with SDS Consulting and I have new eyes courtesy of Lasik Plus of Dayton.  I’d say that’s a fairly ambitious first two weeks.

I’m still working on my side project, hoping to have it completed in the next few weeks or so.  I lost focus with all the holiday excitement, but I’ve picked back up again. 

I’ll also be giving another WPF presentation for the Dayton .Net Developers Group.  This one will be more in-depth than my CONDG presentation which focused on some of the basics of templates, styles, and binding.  One of the big spots in this new version of the presentation will be implementing the MVC pattern within WPF.  Very cool.

WPF Controls TabIndex: Moving to the First Input Control

October 25th, 2007

Here’s one of those real-world situations that no one bothers to demo.  If you are building a form or have some sort of input your gathering from the user, you’ll likely want the first input control Focused initially.  You have a few ways to do this (in ascending order of recommendation):

  1. Call Focus() on the first control explicitly (meh).
  2. Iterate the controls collection and find the lowest TabIndex (terrible code smell).
  3. Do the following in the Load event:
this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));

Lap Around WPF @ CONDG

September 28th, 2007

This evening the Central Ohio .NET Developers Group was nice enough to let me present on WPF.  Everything went fairly well, though it was more a test of my efficiency in WPF development than I had planned; I managed to leave behind the external hard drive that had my code-snippets, slides (both of them), and project template.  So we went from nothing to code-kinda-complete in about an 1 hour and 45 minutes. 

I was very happy with the number of questions and that at least a few people stayed behind to talk afterwards.  If Jim has an opening, I am hoping to give the same presentation again here in Dayton (hint, hint). 

For those that are interested I’ve posted the Demo project, Time Machine, for your general amusement.  If there are any questions or comments regarding the presentation or the demo, feel free to post them and I’ll do my best to answer them.

Thanks to everyone who showed up and for all your questions.