2010-10-20 6 views
10

Je voudrais savoir quel est le meilleur moyen (lire le plus élégant) d'avoir une seule instance d'une fenêtre donnée par application dans WPF. Je suis un nouveau venu à .NET et WPF et ce que j'ai trouvé semble assez boiteux.C# WPF comment appliquer des instances uniques de fenêtres

private static readonly Object MUTEX = new Object(); 
private static AboutWindow INSTANCE; 

public static AboutWindow GetOrCreate() { 
    lock (MUTEX) { 
     if (INSTANCE == null) { 
      INSTANCE = new AboutWindow(); 
     } 
     INSTANCE.Show(); 
     return INSTANCE; 
    } 
} 

private AboutWindow() { 
    InitializeComponent(); 
} 

private void AboutWindow_Closed(object sender, EventArgs e) { 
    // the Closed events are handy for me to update values across 
    // different windows. 
    lock (MUTEX) { 
     INSTANCE = null; 
    } 
} 

Chose est ... cela ressemble à de la merde absolue. Il doit y avoir un moyen d'atteindre le même objectif d'une manière beaucoup plus élégante, n'est-ce pas? PS: j'utilise souvent l'événement Closed pour modifier les valeurs dans d'autres fenêtres ouvertes. Par exemple, j'ai le SettingsWindow avec le bouton "Compte". Quand j'appuie sur ce bouton, le AccountWindow apparaît. Lorsque je ferme AcountWindow, je veux quelque chose dans le SettingsWindow pour changer (une étiquette). D'où la création constante de fenêtres.
Par ailleurs, Close est quelque chose que vous avez toujours à traiter à cause du bouton X sur le cadre de la fenêtre ...

Répondre

5

Si vous avez vraiment besoin d'appliquer une seule instance d'une fenêtre, puis une instance statique (un peu de saveur de ce que vous avez) Avec une méthode de création d'usine est certainement une option viable, un peu comme une seule instance DataContext lorsque vous travaillez avec une base de données.

Vous pouvez également écrire votre propre classe WindowManager, bien que cela semble être trop, et sera essentiellement la même chose (sauf que les méthodes Factory seraient dans une seule classe). Cependant, en relisant votre message, je me demande s'il s'agit d'un cas de disparition de la forêt pour les arbres. Votre mention de votre Windows SettingsWindow, qui à son tour appelle AccountWindow, me fait penser que vous devriez simplement utiliser ShowDialog(). Cela ouvre une fenêtre de façon modale, ce qui signifie qu'il ne peut y avoir d'interaction avec la fenêtre d'appel (ou toute autre fenêtre de votre application). Vous définissez simplement une propriété dans cette boîte de dialogue, définissez DialogResult sur true lorsque le bouton OK est enfoncé et lisez cette propriété dans la fenêtre parente.

Fondamentalement, vous utilisez simplement le ShowDialog comme ceci. Je laisse de côté beaucoup de détails sur l'implémentation, en ce qui concerne la liaison et le codage en dur des contrôles. Ces détails ne sont pas aussi importants que de voir comment fonctionne ShowDialog. Par simplicité, supposons que vous avez une classe appelée MyAppOptions qui, bien, reflète les options de votre application. Je partirai de la plupart des détails de mise en œuvre de cette situation pour la simplicité, mais il serait probablement mettre en œuvre INotifyPropertyChanged, ont des méthodes et des champs et des propriétés, etc.

public class MyAppOptions 
{ 
    public MyAppOptions() 
    { 
    } 

    public Boolean MyBooleanOption 
    { 
     get; 
     set; 
    } 

    public String MyStringOption 
    { 
     get; 
     set; 
    } 
} 

Ensuite, nous allons faire ce simple, et je suppose que vous voulez affiche une boîte de dialogue Options lorsque vous appuyez sur un bouton dans une fenêtre. En outre, je vais supposer qu'il existe des variables qui ont été définies avec vos options, qui ont été chargées au démarrage.

void btnOptions_Click(object sender, RoutedEventArgs e) 
{ 
    MyAppOptions options = new MyAppOptions(); 
    options.MyBooleanOption = mSomeBoolean; 
    options.MyStringOption = mSomeString; 

    OptionsDialog optionsDialog = new optionsDialog(options); 
    if (optionsDialog.ShowDialog() == true) 
    { 
     // Assume this function saves the options to storage 
     // and updates the application (binding) appropriately 
     SetAndSaveOptions(optionsDialog.AppOptions); 
    } 
} 

Supposons maintenant que la OptionsDialog est une fenêtre que vous avez créé dans votre projet, et il a une CheckBox sur elle liée à MyBooleanOption et une zone de texte pour MyStringOption. Il a également un bouton Ok et un bouton Annuler. Le code-behind utilisera probablement Binding, mais pour le moment nous allons coder les valeurs.

public class OptionsDialog : Window 
{ 
    public OptionsDialog(MyAppOptions options) 
    { 
     chkBooleanOption.IsChecked = options.SomeBooleanOption; 
     txtStringOption.Text = options.SomeStringOption; 
     btnOK.Click += new RoutedEventHandler(btnOK_Click); 
     btnCancel.Click += new RoutedEventHandler(btnCancel_Click); 
    } 

    public MyAppOptions AppOptions 
    { 
     get; 
     set; 
    } 

    void btnOK_Click(object sender, RoutedEventArgs e) 
    { 
     this.AppOptions.SomeBooleanOption = (Boolean) chkBooleanOption.IsChecked; 
     this.AppOptions.SomeStringOption = txtStringOption.Text; 

     // this is the key step - it will close the dialog and return 
     // true to ShowDialog 
     this.DialogResult = true; 
    } 

    void btnClose_Click(object sender, RoutedEventArgs e) 
    { 
     // this will close the dialog and return false to ShowDialog 
     // Note that pressing the X button will also return false to ShowDialog 
     this.DialogResult = false; 
    } 
} 

Ceci est un exemple assez basique en ce qui concerne les détails d'implémentation. Recherche en ligne pour ShowDialog pour plus de détails. Les clés importantes à retenir sont:

  • ShowDialog ouvre une fenêtre de façon modale, ce qui signifie qu'il est la seule fenêtre dans votre application qui peut être interagi avec .
  • La définition de DialogResult sur ferme la boîte de dialogue, qui peut être vérifiée depuis le parent appelant.
  • La définition de DialogResult sur false fermera également la boîte de dialogue , auquel cas vous ignorerez la mise à jour des valeurs dans la fenêtre d'appel .
  • En appuyant sur la touche X sur la fenêtre définit automatiquement les DialogResult false
  • Vous pouvez avoir des propriétés publiques dans la fenêtre de dialogue qui peuvent être définies avant de faire le ShowDialog, et peut obtenir des valeurs de après la boîte de dialogue disparaît. Il sera disponible tant que la boîte de dialogue est toujours dans la portée.
+0

Intéressant! Dans ce cas, je ne manque pas la forêt pour les arbres puisque je suis totalement nouveau à.NET et de plus, je programme surtout des trucs côté serveur! C'est un tout nouveau terrain pour moi :) J'apprécierais si vous pouviez mettre à jour votre réponse avec un exemple! – biasedbit

+0

Merci pour l'exemple! :RÉ – biasedbit

13

il y a probablement de meilleures façons de le faire, mais voici une façon relativement simple .... mettre un bool static sur votre classe de fenêtre pour marquer si c'est ouvert ou non. Ensuite, dans l'événement load(), définissez-le sur true et sur l'événement close, définissez-le sur false. Ensuite, dans le code qui ouvre la fenêtre, vérifiez le drapeau.

Voici quelques pseudo-code pour vous donner une idée ...

public class AboutWindow 
{ 

    public static bool IsOpen {get;private set;} 

    onLoadEvent(....) 
    { 
     IsOpen = true; 
    } 

    onUnloadEvent(...) 
    { 
     IsOpen = false; 
    } 

} 


public void OpenAbout() 
{ 
    if (AboutWindow.IsOpen) return; 
    AboutWindow win = new AboutWindow(); 
    win.Show(); 

} 
+2

Comme mieux que toute chose avec singleton mutex. Est-ce que laisser une certaine marge pour l'ouverture de plus d'une fenêtre, mais les chances de ce qui se passe dans les applications basées sur l'interface utilisateur sont minces à aucun - je suis un mec serveur, ne peut pas arrêter de penser à tous les pires scénarios en concurrence simultanée – biasedbit

1

Ce qui suit étend sur la solution ci-dessus pour reshow la fenêtre si elle est déjà ouverte. Dans ce cas, c'est une fenêtre d'aide.

///<summary> 
    /// Show help from the resources for a particular control by contextGUID 
    ///</summary> 
    ///<param name="contextGUID"></param> 
    private void ShowApplicationHelp(string contextGUID = "1") 
    { 

     if (HelpWin != null) 
     { 
      if (HelpWin.IsOpen) 
      { 
       HelpWin.BringToFront(); 
       return; 
      } 
     } 

     HelpWin = new MigratorHelpWindow(); 
     HelpWin.Owner = Application.Current.MainWindow; 
     HelpWin.ResizeMode = ResizeMode.CanResizeWithGrip; 
     HelpWin.Icon = new Image() 
          { 
           Source = 
            new BitmapImage(
            new Uri(
             "pack://application:,,,/ResourceLibrary;component/Resources/Images/Menu/Help.png", 
             UriKind.RelativeOrAbsolute)) 
          }; 
     HelpWin.Show(); 
     HelpWin.BringToFront(); 
    } 

Ce code est tout dans un modèle de vue (MVVM) associé à la fenêtre. Il est appelé par un ICommand accroché à un bouton sur la fenêtre (naturellement, il montre un point d'interrogation !!) La propriété suivante est impliquée (dans ce cas, il s'agit d'un Telerik RadWindow mais il peut s'agir de n'importe quel objet fenêtre, et vous pouvez probablement juste stocker la poignée de fenêtre, mais en utilisant cette propriété permet la manipulation de l'objet plus en douceur par exemple HelpWin.BringToFront() comme dans l'exemple ci-dessus ...

... 
    ... 
    private Telerik.Windows.Controls.RadWindow **HelpWin** 
    { 
     get; 
     set; 
    } 
    ... 
    ... 

dans la fenêtre elle-même (fenêtre WPF)

///<summary> 
    /// Flag to indicate the window is open - use to prevent opening this particular help window multiple times... 
    ///</summary> 
    public static bool IsOpen { get; private set; } 
    ... 
    ... 
    ... 

    private void HelpWindowLoaded(object sender, RoutedEventArgs e) 
    { 
     IsOpen = true; 
    } 

    private void HelpWindowUnloaded(object sender, RoutedEventArgs e) 
    { 
     IsOpen = false; 
    } 

et dans la vue Xaml ... ...

DataContext="{Binding Path=OnlineHelpViewModelStatic,Source={StaticResource Locator}}" 
    RestoreMinimizedLocation="True" 
    **Loaded="HelpWindowLoaded" Unloaded="HelpWindowUnloaded"** > 
0

Comment utiliser un singleton?

public class MyWindow : Window { 

    private static MyWindow instance; 

    public static MyWindow Instance 
    { 
     get 
     { 
      if (instance == null) 
      { 
       instance = new MyWindow(); 
      } 
      return instance; 
     } 
    } 
} 

Ensuite, il suffit d'utiliser

MyWindow.Instance.Show() and MyWindow.Instance.Hide() 
Questions connexes