2010-06-02 2 views
10

Je voudrais construire une boîte de dialogue modale générique/réutilisable que je peux utiliser dans notre application WPF (MVVM) - WCF LOB.Comment construire une boîte de dialogue modale générique/réutilisable pour WPF suivant MVVM

J'ai une vue et ViewModels associés que je voudrais afficher en utilisant des boîtes de dialogue. Les liaisons entre Views et ViewModels sont effectuées à l'aide de DataTemplates ciblés par type.

Voici quelques exigences que j'ai pu rédiger:

  • Je préfère que cela soit basé sur une fenêtre au lieu d'utiliser Adorners et des contrôles qui agissent comme une boîte de dialogue modale.
  • Il devrait obtenir sa taille minimale du contenu.
  • Il doit être centré sur la fenêtre du propriétaire.
  • La fenêtre ne doit pas afficher les boutons Réduire et Agrandir.
  • Il devrait obtenir son titre du contenu.

Quelle est la meilleure façon de faire cela?

Répondre

7

Je réponds à ma propre question pour aider les autres à trouver toutes les réponses que j'ai eu du mal à trouver en un seul endroit. Ce qui précède semble être un problème simple, présente en réalité de multiples problèmes que j'espère répondre suffisamment ci-dessous.

Voilà.

Votre fenêtre WPF qui servira de la boîte de dialogue générique peut ressembler à ceci:

<Window x:Class="Example.ModalDialogView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:ex="clr-namespace:Example" 
     Title="{Binding Path=mDialogWindowTitle}" 
     ShowInTaskbar="False" 
     WindowStartupLocation="CenterOwner" 
     WindowStyle="SingleBorderWindow" 
     SizeToContent="WidthAndHeight" 
     ex:WindowCustomizer.CanMaximize="False" 
     ex:WindowCustomizer.CanMinimize="False" 
     > 
    <DockPanel Margin="3"> 
     <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" FlowDirection="RightToLeft"> 
      <Button Content="Cancel" IsCancel="True" Margin="3"/> 
      <Button Content="OK" IsDefault="True" Margin="3" Click="Button_Click" /> 
     </StackPanel> 
     <ContentPresenter Name="WindowContent" Content="{Binding}"/> 
    </DockPanel> 
</Window> 

Après MVVM, la bonne façon de montrer une boîte de dialogue se fait par un médiateur. Pour utiliser un médiateur, vous avez généralement besoin d'un localisateur de service. Pour les détails spécifiques au médiateur, regardez here.

La solution sur laquelle j'ai choisi impliquait l'implémentation d'une interface IDialogService qui est résolue via un simple ServiceLocator statique. This excellent article codeproject a les détails à ce sujet. Prenez note du message this dans le forum de l'article. Cette solution résout également le problème de la découverte de la fenêtre propriétaire via l'instance ViewModel.

En utilisant cette interface, vous pouvez appeler IDialogService.ShowDialog (ownerViewModel, dialogViewModel). Pour l'instant, je l'appelle du propriétaire ViewModel, ce qui signifie que j'ai des références difficiles entre mes ViewModels.Si vous utilisez des événements agrégés, vous appellerez probablement cela d'un conducteur.

La définition de la taille minimale sur la vue qui sera éventuellement affichée dans la boîte de dialogue ne définit pas automatiquement la taille minimale de la boîte de dialogue. En outre, puisque l'arbre logique dans la boîte de dialogue contient le ViewModel, vous ne pouvez pas simplement lier aux propriétés de l'élément WindowContent. This question a une réponse avec ma solution.

La réponse que je mentionne ci-dessus comprend également le code qui centre la fenêtre sur le propriétaire. Enfin, la désactivation des boutons de réduction et de maximisation est quelque chose que WPF ne peut pas faire en mode natif. La solution la plus élégante IMHO utilise this.

11

Je traite habituellement avec ce en injectant cette interface dans les ViewModels appropriés:

public interface IWindow 
{ 
    void Close(); 

    IWindow CreateChild(object viewModel); 

    void Show(); 

    bool? ShowDialog(); 
} 

Cela permet aux ViewModels de Spaw fenêtres enfants et de leur montrer sur modalement modales.

Une implémentation réutilisable de IWindow est ceci:

public class WindowAdapter : IWindow 
{ 
    private readonly Window wpfWindow; 

    public WindowAdapter(Window wpfWindow) 
    { 
     if (wpfWindow == null) 
     { 
      throw new ArgumentNullException("window"); 
     } 

     this.wpfWindow = wpfWindow; 
    } 

    #region IWindow Members 

    public virtual void Close() 
    { 
     this.wpfWindow.Close(); 
    } 

    public virtual IWindow CreateChild(object viewModel) 
    { 
     var cw = new ContentWindow(); 
     cw.Owner = this.wpfWindow; 
     cw.DataContext = viewModel; 
     WindowAdapter.ConfigureBehavior(cw); 

     return new WindowAdapter(cw); 
    } 

    public virtual void Show() 
    { 
     this.wpfWindow.Show(); 
    } 

    public virtual bool? ShowDialog() 
    { 
     return this.wpfWindow.ShowDialog(); 
    } 

    #endregion 

    protected Window WpfWindow 
    { 
     get { return this.wpfWindow; } 
    } 

    private static void ConfigureBehavior(ContentWindow cw) 
    { 
     cw.WindowStartupLocation = WindowStartupLocation.CenterOwner; 
     cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true)); 
    } 
} 

Vous pouvez utiliser cette fenêtre comme une fenêtre d'accueil réutilisable. Il n'y a pas de code-behind:

<Window x:Class="Ploeh.Samples.ProductManagement.WpfClient.ContentWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:self="clr-namespace:Ploeh.Samples.ProductManagement.WpfClient" 
     xmlns:pm="clr-namespace:Ploeh.Samples.ProductManagement.PresentationLogic.Wpf;assembly=Ploeh.Samples.ProductManagement.PresentationLogic.Wpf" 
     Title="{Binding Path=Title}" 
     Height="300" 
     Width="300" 
     MinHeight="300" 
     MinWidth="300" > 
    <Window.Resources> 
     <DataTemplate DataType="{x:Type pm:ProductEditorViewModel}"> 
      <self:ProductEditorControl /> 
     </DataTemplate> 
    </Window.Resources> 
    <ContentControl Content="{Binding}" /> 
</Window> 

Vous pouvez en savoir plus sur ce (ainsi que télécharger l'échantillon complet de code) dans my book.

+0

Merci de montrer votre approche! –

+0

Pourquoi le downvote anonyme? –

Questions connexes