Posts mit dem Label Design Pattern werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Design Pattern werden angezeigt. Alle Posts anzeigen

Samstag, 3. Juli 2010

Silverlight: Converter with multiple parameters

Sometimes it is necessary that you have a converter (interface IValueConverter) with multiple parameters. To reach this goal, you can use a DependencyProperty. That’s normally a very easy step, but I spend some extraordinary time with my converter becuase in my case the second parameter should be bindable

– So what was the problem?

I have tried to derive the Converter from UserControl – great mistake: You have to use FrameworkElement instead!

Usage:
   1: <Helpers:StatisticsConverter x:Key="StatisticsConverter" ViewModel="{Binding ElementName=LayoutRoot,Path=DataContext.Statistics}" />
   2:     
   3: <TextBlock Margin="0,0,10,0"
   4:    FontSize="9"  
   5:    x:Name="InfoSummeText" Text="{Binding Converter={StaticResource StatisticsConverter}, ConverterParameter=Summe}" >
   6: </TextBlock>
Converter:
   1: public class StatisticsConverter : FrameworkElement, IValueConverter
   2:    {
   3:  
   4:        #region IValueConverter Members
   5:  
   6:        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
   7:        {
   8:            var statisticsViewModel = (StatisticsViewModel)ViewModel;
   9:            var statistics = statisticsViewModel.GetAnzeigeKategorieStatistics(value as AnzeigeKategorie);
  10:            switch (parameter.ToString())
  11:            {
  12:                case "Summe":
  13:                    {
  14:                        return String.Format("{0:N2}", statistics.Summe) + " EUR";
  15:                    }
  16:                default:
  17:                    {
  18:                        throw new NotImplementedException(parameter as string);
  19:                    }
  20:            }
  21:        }
  22:  
  23:        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  24:        {
  25:            return "";
  26:        }
  27:  
  28:        #endregion
  29:  
  30:        #region object ViewModel (DependencyProperty)
  31:  
  32:        public object ViewModel
  33:        {
  34:            get { return (object)GetValue(ViewModelProperty); }
  35:            set { SetValue(ViewModelProperty, value); }
  36:        }
  37:  
  38:        public static readonly DependencyProperty ViewModelProperty =
  39:            DependencyProperty.Register(
  40:                "ViewModel",
  41:                typeof(object),
  42:                typeof(StatisticsConverter),
  43:                new PropertyMetadata(null, null));
  44:  
  45:  
  46:        #endregion ViewModel (DependencyProperty)
  47:    }

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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   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">
  12:     
  13:     <Detail:DetailsViewSelector Content="{Binding SelectedEntryDetails}"  TemplateType="{Binding SelectedEntryDetails}"  >
  14:         <Detail:DetailsViewSelector.Resources>
  15:            <DataTemplate
  16:             x:Key="DetailWertpapierView"
  17:                         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  18:                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  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>
  25:         
  26:        
  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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   6:                   Content="{Binding}">
   7:     <ContentPresenter.Resources>
   8:     </ContentPresenter.Resources>
   9: </ContentPresenter>

Code for the Control:

   1: public partial class DetailsViewSelector : ContentPresenter
   2:    {
   3:  
   4:        public static readonly DependencyProperty TemplateTypeProperty =
   5:        DependencyProperty.Register("TemplateType", typeof(DetailViewModelBase),
   6:        typeof(DetailsViewSelector), new PropertyMetadata(new DetailWertpapierViewModel()));
   7:  
   8:        public DetailViewModelBase TemplateType
   9:        {
  10:            get { return (DetailViewModelBase)GetValue(TemplateTypeProperty); }
  11:            set { SetValue(TemplateTypeProperty, value); }
  12:        }
  13:  
  14:        public DetailsViewSelector()
  15:        {
  16:            Loaded += new RoutedEventHandler(OnLoaded);
  17:        }
  18:  
  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.

Architecture

image 

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);
   6:  
   7:     }
The implementation:
   1: public class DialogControlProvider:IDialogControlProvider
   2:     {
   3:  
   4:         #region IDialogProvider Members
   5:  
   6:         public void DisplayErrorDialog(string errorMessage)
   7:         {
   8:             var popup=new PopupErrorMessage("Fehlermeldung",errorMessage,true);
   9:             popup.Show();
  10:         }
  11:  
  12:         public void DisplayInformationDialog(string informationMessage)
  13:         {
  14:             var popup = new PopupErrorMessage("Information", informationMessage, false);
  15:             popup.Show();
  16:         }
  17:  
  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:     }

Samstag, 3. April 2010

Localization in Silverlight 3

Um eine Silverlight 3 Anwendung zu lokalisieren, gibt es einen wichtigen Punkt, den man auch nicht unbedingt auf den Microsoft “How-To-Dos” findet. Man muss nämlich zum Notepad greifen und das csproj-File seines Silverlight-Projekts editieren. In der Sektion SupportedCultures müssen alle unterstützen Cultures der Silverlight Anwendung eingetragen werden. Macht man dies nicht, so werden im XAP durch den Compiler keine Ressourcendateien erzeugt.

Beispiel (nur deutsche und Default-Culture verfügbar):

   1: <SupportedCultures>de
   2:     </SupportedCultures>

Folgende Schritte sind zur Lokalisierung notwendig:

  1. Ressourcen-Dateien hinzufügen
  2. Access-Modifier der Ressourcen-Dateien (im Ressource Editor) auf Public stellen
  3. Eine Zugriffsklasse auf das Ressource-File definieren (Namespace des Ressource-Files im Beispiel AppServices.Ressources und Name des Ressource-File ist CommonRessources).
       1: namespace AppServices
       2: {
       3:     public class LocalizedStrings
       4:     {
       5:         public LocalizedStrings()
       6:         {
       7:         }
       8:         private static AppServices.Resources.CommonRessource commonRessource = new AppServices.Resources.CommonRessource();
       9:         public AppServices.Resources.CommonRessource CommonRessource { get { return commonRessource; } }
      10:     }
      11:  
      12: }
  4. In UserControl.Ressources oder Application.Ressources des Silverlight UserControls oder der Silverlight Seite die angelegte Zugriffsklasse aus (3) referenzieren (Namespace und Name anpassen)

       1: <UserControl.Resources>
       2:         <local:LocalizedStrings xmlns:local ="clr-namespace:AppServices"
       3:                            x:Key="LocalizedStrings" />
  5. Das Silverlight Control an die Ressource binden. Hier muss im Binding Path der Dateiname des Ressource-Files und der Key der Resource verwendet werden! Die Source-Eigenschaft enthält den Key aus (4)

       1: <Button Height="23" Width="73.5" 
       2:        Canvas.Left="24" Canvas.Top="118.5" 
       3:        Content="{Binding Path=CommonRessource.btn_signin, Source={StaticResource LocalizedStrings }}" 
  6. Die neutrale Sprache im Assembly Info File einstellen. Entweder über die Properties->Assembly Information in Visual Studio oder direkt in der AssembleyInfo.cs-Datei.

  7. [assembly: NeutralResourcesLanguageAttribute("en")]