Invoking commands from events using the InvokeCommandAction behavior

MVVM is something for me. Databinding your data and behavior so you don’t have any code in your views is for me a nice separation of concepts. However some control’s cannot execute a command for certain events, for example when you select something in a listbox.

Enters InvokeCommandAction, which is a behavior which allows you to invoke a command when a certain event occurs. You can find it beneath Blend’s 4 behaviors:

image

You can attach this behavior to any control, and then select which command to execute when an event occurs. Let’s use a simple ListBox with strings example:

<Window
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:viewModels="clr-namespace:CommandsAndEvents"
       xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" x:Class="CommandsAndEvents.Window1"
       Title="Window1" Height="300" Width="300">
  <Window.DataContext>
    <viewModels:ListViewModel />
  </Window.DataContext>
    <Grid>
    <ListBox x:Name="listBox" ItemsSource="{Binding Names}" >
    </ListBox>
  </Grid
>
</
Window
>

This window will show a list of names from the following ListViewModel:

public class ListViewModel
{
 
public
ListViewModel()
  {
    Names =
new ObservableCollection<string> { "Jan", "Piet", "Joris", "Corneel"
};
    SelectionChangedCommand =
    
new RelayCommand("Test", (item) => ShowSelected((string
)item)); // string is the type of the selected item
  }

 
public ObservableCollection<string> Names { get; set
; }



 
public ICommand SelectionChangedCommand { get; set
; }


 
private void ShowSelected(string
item)
  {
   
MessageBox.Show(item);
  }
}

Now, if I’d like the ListBox to call the SelectionChangedCommand I simply add the following behavior:

<ListBox x:Name="listBox" ItemsSource="{Binding Names}" >
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
      <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
 
                            
CommandParameter="{Binding SelectedItem, ElementName=listBox}"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</ListBox>

 

Notice that this allows for arguments to be passed to the command, which then allows you access to any property on the control!

Comments (14) -

  • Dallas

    10/26/2011 9:43:32 PM |

    Thanks a lot for such a great simple example!
    It is really easy to understand after reading this article how to implement event triggers.
    Oh God! I spent about two or three hours in searching a good sample and finally I've found it here Smile

    Thanks you so much!

  • Houston

    12/10/2011 9:19:31 PM |

    Thanks! finally found these lines of code after searching 3 hours...

  • snort this

    1/20/2014 1:50:00 PM |

    I as well conceive therefore, perfectly written post!

  • LED Lamp Store

    3/17/2014 3:57:31 PM |

    This is a nice blog,the posts can help many people to learn what you already known.

  • Phone Numbers

    3/17/2014 8:17:40 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    3/20/2014 9:48:58 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    3/20/2014 11:48:00 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    3/21/2014 1:05:39 AM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    3/25/2014 10:54:47 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    4/1/2014 1:27:38 AM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    4/6/2014 9:16:33 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    4/10/2014 9:04:19 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • final cut studio 3

    4/11/2014 4:43:48 AM |

    Hands down, Apple's app store wins by a mile. It's a huge selection of all sorts of apps vs a rather sad selection of a handful for Zune. Microsoft has plans, especially in the realm of games, but I'm not sure I'd want to bet on the future if this aspect is important to you. The iPod is a much better choice in that case.

  • Phone Numbers

    4/15/2014 10:47:53 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    4/16/2014 11:15:41 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    4/22/2014 12:33:51 AM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    4/27/2014 10:51:48 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • Phone Numbers

    4/28/2014 8:38:20 PM |

    The Numbers Helpline has sourced 1000's of customer service contact <a href="www.numbershelpline.co.uk/.../";>Phone numbers</a> in the UK. We know how hard it can be to find those <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> at times that why we have made a directory with them all in one place so you will never have waste long periods of time looking for a customer service contact <a href="www.numbershelpline.co.uk/.../";>Phonenumbers</a> again.

  • stainless steel jewelry

    5/25/2014 8:25:57 PM |

    This will increase your chances of walking away with a budget friendly used automobile. Major brands can be seen on sale both and also in stores this year. They may cut their losses if ever the profits don ' t flow.http://www.1080i.co.uk

  • Silvana Cante

    8/6/2014 9:35:29 AM |

    Generally I don't read post on blogs, but I wish to say that this write-up very forced me to try and do so! Your writing taste has been surprised me. Thank you, very great post.

  • Ladawn Vanoflen

    8/8/2014 6:41:47 PM |

    This is the right blog for anyone who wants to find out about this topic. You realize so much its almost hard to argue with you (not that I actually would want…HaHa). You definitely put a new spin on a topic thats been written about for years. Great stuff, just great!

  • Alyssa Mccroy

    8/8/2014 6:49:18 PM |

    I’m impressed, I must say. Really rarely do I encounter a blog that’s both educative and entertaining, and let me tell you, you have hit the nail on the head. Your idea is outstanding; the issue is something that not enough people are speaking intelligently about. I am very happy that I stumbled across this in my search for something relating to this.

Loading

April 2010

Diederik Krols

The XAML Brewer

Globalizing and Localizing a WPF application

This article describes how to localize a WPF application using the WPF Localization Extension project from Codeplex. On one hand, this is a fine tool. On the other hand, it's a crying shame that WPF developers need to rely on external sources for something as basic as localization. As you know, localization support is nicely integrated in Microsoft's other development stacks, like WinForms and ASP.NET. So far for the ranting, let's get to the solution.

Terminology

Globalization is making your application ready to be localized. This boils down to separating culture-specific elements (texts, images, colors, ...) from the code. The globalization code analysis rules may help you to do this.

Localization is tailoring your application toward a specific language and region by providing these culture-specific elements (e.g. in a resource file or a database).

Options for WPF localization

These are a couple of options for localizing a WPF application:

  • use the clumsy and error prone LocBaml tool. Don't get me wrong: I really like the idea of skipping the globalization step and then do localization as an afterthought. But just read the intro of this article for a list of reasons NOT to use LocBaml,
  • use classic RESX files, or
  • use WPF ResourceDictionaries.

More details about the pros and cons of these alternatives can be found here.

The WPF Localization Extension project

The WPF Localization Extension allows you to localize your application using classic .resx files. The project revolves around a custom markup extension that allows you to declaratively (in XAML) bind control properties to localized resources. The library offers a lot of functionality, but more important: it is well-written and well-documented (at least in its source code). I built a small sample application that walks through some of its features. Here's how this application looks like. You find its source code at the end of this article:

The project is decorated with the following resource files: 

Declarative Localization

The markup extension and its namespaces are registered by the following declaration on top your XAML file (you might want to change the project's source code to choose a more appropriate namespace):

xmlns:lex="http://schemas.root- project.org/xaml/presentation"


You can now set up a binding from a control's property to a resource, like this:

<TextBlock Text="{lex:LocText Key=Tomato, Dict=Translations, Assembly=U2UConsult.WPF.Localization.Sample}" />


It's possible to ignore the current culture, and force a specific culture for a binding, like this:

<TextBlock Text="{lex:LocText Key=Duck, Dict=Translations, Assembly=U2UConsult.WPF.Localization.Sample, ForceCulture=nl}" />


My sample application only fetches string resources, but WPF Localization Extension> supports the following resource types out of the box:

  • Brush,
  • Double,
  • FlowDirection,
  • Image,
  • Text, and
  • Thickness.

For other data types you'll need to plug in a Converter.

Programmatic Localization

Sometimes we need to localize a control in source code, e.g. when the control is dynamically created. In this case we can set the binding programmatically, like this:

LocTextExtension loc = new LocTextExtension("U2UConsult.WPF.Localization.Sample:Translations:Pumpkin");

loc.SetBinding(this.PumpkinTextBlock, TextBlock.TextProperty);


If data binding is not needed (or not possible), the you can fallback to a lookup in the .resx file using GetLocalizedObject, like this:

string pinguin = LocalizeDictionary.Instance.GetLocalizedObject<string>(

    "U2UConsult.WPF.Localization.Sample",

    "Translations",

    "Pinguin",

    CultureInfo.GetCultureInfo("nl-BE"));

this.PinguinTextBlock.Text = pinguin;

 

Design time support

Localization at run time is one thing, but as a developer we like to see the result without running the application. The LocalizeDictionary.DesignCulture dependency property provides a Culture for Visual Studio's designers. It can be set in XAML, as follows:

lex:LocalizeDictionary.DesignCulture="nl-BE"


[By the way: the design time support only started working on my VS2010 after I recompiled the codeplex source with VS2010.]

On top of that, the markup extension provides a DesignValue property that sets the value in Visual Studio's Designer, regardless of the (Design-)Culture. Here's a sample:

<TextBlock Text="{lex:LocText Key=Tomato, Dict=Translations, Assembly=U2UConsult.WPF.Localization.Sample, DesignValue=#Tomato#}" />


Here's how these two features look like at design time:

Finally, the markup extension also provides a InitialValue for Blend support.

Missing cultures and missing translations

I guess we all agree that our application should never totally crash if a string can not be found in a resource file, or if the user has an unexpected or unsupported locale. Here are some techniques to get around this.

Missing resources

It's not a good idea to just fallback to a default language for missing entries, without a warning to the end user. In the best case this will only create confusion. But just take a look at the following interlingual homographs and consider what would happen if they appear in the middle of a half-translated window:

  • coin is french for corner,
  • file is dutch for traffic jam, and
  • gift is german for poison

So the invariant culture should NOT be English (or whatever 'real' language), but still remain comprehensible. In my app, the invariant resource file looks like this:

When a translation (or the whole culture) falls back to the invariant culture, the user interface looks like this (Tomato and Pumpkin are not translated):

Missing cultures

If a specific culture is not found (e.g. 'fr-NL') then it makes sense to first check if the language exists ('fr'), and only use the invariant culture as a last resort. That's exactly what the following code does. The 'French' and 'Dzongkha' buttons in the sample application test all code paths:

private void SwitchCulture(string culture)

{

    CultureInfo ci = CultureInfo.InvariantCulture;

    try

    {

        ci = new CultureInfo(culture);

    }

    catch (CultureNotFoundException)

    {

        try

        {

            // Try language without region

            ci = new CultureInfo(culture.Substring(0, 2));

        }

        catch (Exception)

        {

            ci = CultureInfo.InvariantCulture;

        }

    }

    finally

    {

        LocalizeDictionary.Instance.Culture = ci;

        this.CultureTextBlock.Text = ci.EnglishName;

    }

}

 

Source code

Last but not least, here's the source code of the small sample project: U2UConsult.WPF.Localization.Sample.zip (2,41 mb)

Enjoy!

WCF Data Services 4.0 in less than 5 minutes

WCF Data Services 4.0 (formerly known as ADO.NET Data Services, formerly known as Astoria) is one of the ways to expose an Entity Data Model from Entity Framework 4.0 in a RESTful / OData way. This article explains how to create such a data service and how to consume it with a browser and with a WPF client.

The Data Service

Start with an empty ASP.NET Application:

Add a WCF Data Service to it:

Also add an Entity Data Model to the ASP.NET project:

Follow the Model Wizard to create a model containing entities on top of the Employee and Person tables from the AdventureWorks2008 database:

In the designer, you should have something like this:

A lot of code was generated, let's add our own 50ct in the service's code behind. First let it inherit from DataService<AdventureWorks2008Entities>:

public class WcfDataService : DataService<AdventureWorks2008Entities>

{ .. }

 

Then modify the InitializeService method as follows. This exposes all operations and grants all access rights (not really a production setting):

public static void InitializeService(DataServiceConfiguration config)

{

    config.SetEntitySetAccessRule("*", EntitySetRights.All);

    config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);

    config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;

}


Believe it or not, we're done (for the first part): the entity model is now exposed in a RESTful way. At the root URL you get an overview of the exposed entities. In the attached sample the root URL is "http://localhost:1544/WcfDataService.svc", but you may of course end up with another port number:

At the "/Employees" address you find all employees:

In your browser this list of employees may appear like this:

This means it's time to -at least temporarily- disable your rss feed reading view. Here's how to do this in IE:

 

 

To reach an individual entity, just type its primary key value in parentheses at the end of the URL, like "http://localhost:1544/WcFDataService.svc/Employees(1)":

You can navigate via the relationships between entities. This is how to reach the Person entity, connected to the first Employee. The URL is "http://localhost:1544/WcfDataService.svc/Employees(1)/Person":

Other OData URI options options can be found here, including:

  • Filtering: http://localhost:1544/WcfDataService.svc/Employees?$filter=JobTitle eq 'Chief Executive Officer'
  • Projection: http://localhost:1544/WcfDataService.svc/Employees?$select=JobTitle,Gender
  • Client side paging: http://localhost:1544/WcfDataService.svc/Employees?$skip=5&$top=2

Version 4.0 also includes support for server side paging. This gives you some control over the resources. Add the following line in the InitializeService method:

config.SetEntitySetPageSize("Employees", 3);


Only 3 employees will be returned now, even if the client requested all:

A Client

Enough XML for now. WCF Data Services also expose a client side model that allows you to use LINQ.

Create a new WPF application:

Add a Service Reference to the WFC Data Service:

Decorate the Window with two buttons and a listbox. It should look more or less like this:

The ListBox will display Employee entities through a data template (OK, that's XML again):

<ListBox

   Name="employeesListBox"

   ItemTemplate="{StaticResource EmployeeTemplate}"

   Margin="4"

   Grid.Row="1"/>


Here's the template. It not only binds to Employee properties, but also to Person attributes:

<DataTemplate

   x:Key="EmployeeTemplate">

    <StackPanel>

        <StackPanel Orientation="Horizontal">

            <TextBlock

               Text="{Binding Path=Person.FirstName}"

               FontWeight="Bold"

               Padding="0 0 2 0"/>

            <TextBlock

               Text="{Binding Path=Person.LastName}"

               FontWeight="Bold"/>

        </StackPanel>

        <StackPanel Orientation="Horizontal">

            <TextBlock

               Text="{Binding Path=JobTitle}"

               Width="180"/>

            <TextBlock

               Text="{Binding Path=VacationHours}"

               Width="60"

               TextAlignment="Right" />

            <TextBlock

               Text=" vacation hours taken." />

        </StackPanel>

    </StackPanel>

</DataTemplate>


The Populate-Button fetches some Employee entities together with their related Person entity, and binds the collection to the ListBox (in version 4.0 two-way bindings are supported for WPF):

private void Populate_Click(object sender, RoutedEventArgs e)

{

    AdventureWorks2008Entities svc =

        new AdventureWorks2008Entities(

            new Uri("http://localhost:1544/WcfDataService.svc"));

 

    this.employeesListBox.ItemsSource =

        svc.Employees.Expand("Person").Where(emp => emp.BusinessEntityID < 100);

}


Here's the result:

The Update-Button updates the number of vacation hours of the company's CEO. It fetches the Employee, updates its VacationHours property, then tells the state manager to update the employee's state, and eventually persists the data:

private void Update_Click(object sender, RoutedEventArgs e)

{

    AdventureWorks2008Entities svc =

        new AdventureWorks2008Entities(

            new Uri("http://localhost:1544/WcfDataService.svc"));

 

    Employee employee =

        svc.Employees.Where(emp => emp.BusinessEntityID == 1).First();

 

    employee.VacationHours++;

 

    svc.UpdateObject(employee);

    svc.SaveChanges();

}


If you now repopulate the listbox, you will see the increased value:

Source Code

Here's the full source code of this sample (just requires VS2010 with no extra downloads): U2UConsult.WcfDataServices.Sample.zip (96,59 kb)

Enjoy!