Samstag, 29. Mai 2010

DataTemplate Selector with Silverlight

With Windows Presentation Foundation (WPF) it is very easy to switch your datatemplate in dependency of for example the type of data you want to bind. But Silverlight does not support this “Out-of-box”. So we have investigated some time in our current project to get a similar mechanism. The idea for my solution is, that we have a area in our form which should contains dynamic objects.One of the conditions for the solution was that it should support the common MVVM pattern.

The key point of my solution is to use a DependencyProperty and bind this to our viewmodel instance.

Step 1: Write a user control for the dynamic area which binds the view model
   1: <UserControl x:Class="SDX.FinancialPlanning.Views.Uebersicht.EinzelItemView"
   2:     xmlns=""
   3:     xmlns:x=""
   4:     xmlns:d=""
   5:     xmlns:mc="" 
   6:     xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
   7:              xmlns:Detail1="clr-namespace:SDX.FinancialPlanning.ViewModels.Detail" 
   8:              xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:CommonHelpers="clr-namespace:SDX.FinancialPlanning.CommonHelpers" 
   9:              xmlns:Detail="clr-namespace:SDX.FinancialPlanning.Views.Detail" 
  10:              mc:Ignorable="d"
  11:     d:DesignHeight="300" d:DesignWidth="400">
  13:     <Detail:DetailsViewSelector Content="{Binding SelectedEntryDetails}"  TemplateType="{Binding SelectedEntryDetails}"  >
  14:         <Detail:DetailsViewSelector.Resources>
  15:            <DataTemplate
  16:             x:Key="DetailWertpapierView"
  17:                         xmlns=""
  18:                         xmlns:x=""
  19:                         xmlns:Control="clr-namespace:SDX.FinancialPlanning.Views.Detail">
  20:                 <Control:DetailWertpapierView />
  21:             </DataTemplate>
  22:             <DataTemplate x:Key="EmptyTemplate">
  23:             </DataTemplate>
  24:         </Detail:DetailsViewSelector.Resources>
  27:     </Detail:DetailsViewSelector>
  28: </UserControl>

The DetailsViewSelector have two bindings

  • Content: Binds the view model to the choosen control
  • TemplateType: Binds also the view model to select the view by naming comventions

In my sample I habe only one DataTemplate and a empty Data Template if no corresponding objects could be bound.

Step 2: Implement the Detail View Selector
   1: <ContentPresenter x:Class="SDX.FinancialPlanning.Views.Detail.DetailsViewSelector"
   2:     xmlns=""
   3:     xmlns:x=""
   4:     xmlns:d=""
   5:     xmlns:mc="" 
   6:                   Content="{Binding}">
   7:     <ContentPresenter.Resources>
   8:     </ContentPresenter.Resources>
   9: </ContentPresenter>

Code for the Control:

   1: public partial class DetailsViewSelector : ContentPresenter
   2:    {
   4:        public static readonly DependencyProperty TemplateTypeProperty =
   5:        DependencyProperty.Register("TemplateType", typeof(DetailViewModelBase),
   6:        typeof(DetailsViewSelector), new PropertyMetadata(new DetailWertpapierViewModel()));
   8:        public DetailViewModelBase TemplateType
   9:        {
  10:            get { return (DetailViewModelBase)GetValue(TemplateTypeProperty); }
  11:            set { SetValue(TemplateTypeProperty, value); }
  12:        }
  14:        public DetailsViewSelector()
  15:        {
  16:            Loaded += new RoutedEventHandler(OnLoaded);
  17:        }
  19:        private void OnLoaded(object sender, RoutedEventArgs e)
  20:        {
  21:            DetailViewModelBase templateType = TemplateType;
  22:            string viewName = templateType.GetType().FullName.Replace("Model", "");
  23:            Type viewType = Type.GetType(viewName);
  24:            if (viewType == null)
  25:            {
  26:                ContentTemplate = (DataTemplate) Resources["EmptyTemplate"];
  27:            }
  28:            else
  29:            {
  30:                DataTemplate dataTemplate = (DataTemplate)Resources[viewType.Name];
  31:                ContentTemplate = dataTemplate;
  32:            }
  33:        }
  34:    }

To have a very flexible mechanism the DetailsViewSelector works with naming conventions to choice the View for the given view model.

Samstag, 15. Mai 2010

MVVM Pattern und user interaction (modal dialogs)

If you are using a MVVM pattern you have quickly the problem, that you need stuff like confirmation dialogs or message boxes to interact with the user. In my actual project (Silverlight 4 project with RIA Services hosted at the Windows Azure cloud) I use the approach to define a neutral interface IDialogProvider and a implementation with specific coding to show popup windows and return optional the result via callbacks. With this approach the view model is still independent from the presentation layer and testable with unit tests.



Implementation draft

The interface:
   1: public interface IDialogControlProvider
   2:     {
   3:         void DisplayErrorDialog(string errorMessage);
   4:         void DisplayInformationDialog(string informationMessage);
   5:         void DisplayConfirmationDialog(string title,string message,Action<int> callback);
   7:     }
The implementation:
   1: public class DialogControlProvider:IDialogControlProvider
   2:     {
   4:         #region IDialogProvider Members
   6:         public void DisplayErrorDialog(string errorMessage)
   7:         {
   8:             var popup=new PopupErrorMessage("Fehlermeldung",errorMessage,true);
   9:             popup.Show();
  10:         }
  12:         public void DisplayInformationDialog(string informationMessage)
  13:         {
  14:             var popup = new PopupErrorMessage("Information", informationMessage, false);
  15:             popup.Show();
  16:         }
  18:         public void DisplayConfirmationDialog(string title,string message, Action<int> callback)
  19:         {
  20:             var result = SdxDialogResult.Yes;
  21:             callback(result);
  22:         }
Usage in the View model:

private IDialogControlProvider _dialogControl;

_dialogControl can be set via the contructor of the view model instance (in my case a view model locator class) or with a special initialize method.

   1: _dialogControl.DisplayConfirmationDialog("Frage", "Soll wirklich gespeichert werden?",
   2:     (dialogResult) =>
   3:     {
   4:         if (dialogResult == SdxDialogResult.Yes)
   5:         {
   6:             // do something
   7:         }
   8:     }

Montag, 10. Mai 2010

WCF RIA Services and Entity Framework complex type

After an evening debug session I found out that the WCF RIA Services supports no complex data types in the entity model. The properties of type Complex type in the domain context are not available ( no warning or something else). Now everything in my entity Model is scalar again .-)

Entity Framework and datetime2

Today I was a little bit confused about a error message “…conversation error for datetime2 in datetime…”. Because in my database scheme I am not using any new SQL Server 2008 datetime2 data types. After a while of researching, I found out that the Entity Framework 4 internally works with the datetime2 data type and wants to convert it in my datetime database type.

To use the datetime data type in Entity Framwork 4 you have to switch the ProviderManifestToken in the edmx-file to "2005". After thisn the Entity Frameweok 4 will work with the datetime data type.

Freitag, 7. Mai 2010

Drag & Drop with Silverlight 4

In our new project we use the new Drag and Drop features of Silverlight 4. This new feature increase the usability of any Sliverlight application.

More informations could be found on Tim Heuers blog: Silverlight Toolkit adds DragDrop targets!

Refresh cached EntitySet of Silverlight Client

If we loads a set of entities with WCF RIA Services they will be cached in an EntitySet on the client-side (Silverlight client), so after submitting changes or a after making a other query for the same EntitySet, only the entities we have removed on the client will be removed from the client-side’s EntitySet. If someone else have removed some of the entities from the database we work against, we still have those cached on the client-side. At the moment there aren’t any reload features added to the WCF RIA Services, so we need to handle it by our self.

We can’t just make a new call to the Load method of our DomainContext, it will only merge the existing EntitySet with changes or add new entities, but not removing anything. So for example if you query objects with a special condition and make a other query with a different condition it will bring back a merged list of entities.

So, what I am doing is:

   1: // Clear the client cache
   2: _DomainContext.Geldanlages.Clear();
   3: // make the query
   4: _DomainContext.Load(queryWertpapier);
   5: _DomainContext.Load(queryWertpapier).Completed += new EventHandler(LoadItemsWP_Completed);