2010-06-02 18 views
10

Vorrei creare una finestra di dialogo modale generica/riutilizzabile che possa essere utilizzata nella nostra applicazione WPF (MVVM) - WCF LOB.Come creare una finestra di dialogo modale generica/riutilizzabile per WPF dopo MVVM

Ho una vista e associato ViewModels che vorrei visualizzare utilizzando le finestre di dialogo. I collegamenti tra Views e ViewModels vengono eseguiti utilizzando DataTemplates con targeting per tipo.

Qui ci sono alcuni requisiti che sono stato in grado di redigere:

  • preferisco questo essere basata su una finestra invece di usare Adorners e controlli che agiscono come una finestra di dialogo modale.
  • Dovrebbe ottenere la dimensione minima dal contenuto.
  • Si dovrebbe centrare sulla finestra del proprietario.
  • La finestra non deve mostrare i pulsanti Riduci a icona e Ingrandisci.
  • Dovrebbe ottenere il titolo dal contenuto.

Qual è il modo migliore per farlo?

risposta

7

Sto rispondendo alla mia domanda per aiutare gli altri a trovare tutte le risposte che ho faticato a trovare in un posto. Quanto sopra sembra un problema semplice, in realtà presenta più problemi che spero di rispondere sufficientemente sotto.

Ecco qui.

La finestra WPF che servirà come la finestra di dialogo generica può essere simile a questa:

<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> 

seguito MVVM, il modo giusto per mostrare una finestra di dialogo è attraverso un mediatore. Per utilizzare un mediatore, in genere è necessario anche un localizzatore di servizi. Per dettagli specifici del mediatore, vedi here.

La soluzione che ho scelto implicava l'implementazione di un'interfaccia IDialogService risolta tramite un semplice ServiceLocator statico. This articolo codeproject eccellente ha i dettagli su questo. Prendere nota del messaggio this nel forum degli articoli. Questa soluzione risolve anche il problema di scoprire la finestra del proprietario tramite l'istanza ViewModel.

Utilizzando questa interfaccia, è possibile chiamare IDialogService.ShowDialog (ownerViewModel, dialogViewModel). Per ora, sto chiamando questo dal proprietario ViewModel, il che significa che ho riferimenti duri tra i miei ViewModels.Se usi eventi aggregati, probabilmente lo chiamerai da un conduttore.

L'impostazione della dimensione minima nella vista che verrà visualizzata nella finestra di dialogo non imposta automaticamente la dimensione minima della finestra di dialogo. Inoltre, poiché l'albero logico nella finestra di dialogo contiene ViewModel, non è possibile associare solo le proprietà dell'elemento WindowContent. La domanda This ha una risposta con la mia soluzione.

La risposta che ho menzionato sopra include anche il codice che centra la finestra sul proprietario.

Infine, la disattivazione dei pulsanti di riduzione e ingrandimento è qualcosa che WPF non può fare in modo nativo. La soluzione più elegante IMHO utilizza this.

11

Io di solito affrontare questo iniettando questa interfaccia nei ViewModels appropriate:

public interface IWindow 
{ 
    void Close(); 

    IWindow CreateChild(object viewModel); 

    void Show(); 

    bool? ShowDialog(); 
} 

Questo permette al ViewModels per Spaw finestre figlio e li mostrano modale su modale.

Un riutilizzabile attuazione IWindow è questo:

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)); 
    } 
} 

È possibile utilizzare questa finestra come una finestra ospite riutilizzabile. Non c'è 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> 

Si può leggere di più su questo (così come scaricare il codice di esempio completo) in my book.

+0

Grazie per aver mostrato il tuo approccio! –

+0

Perché il downvote anonimo? –

Problemi correlati