2010-10-20 13 views
10

Me gustaría saber cuál es la mejor manera (leer más elegante) para tener una sola instancia de una ventana determinada por aplicación en WPF.C# WPF cómo aplicar instancias únicas de Windows

Soy un recién llegado a .NET y WPF y lo que se me ocurrió parece muy poco convincente.

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

Thing is ... esto se ve como una mierda absoluta. Debe haber alguna manera de lograr el mismo objetivo de una manera mucho más elegante, ¿verdad?

PD: A menudo utilizo el evento Closed para cambiar valores en otras ventanas abiertas. Por ejemplo, tengo SettingsWindow con el botón "Cuenta". Cuando presiono ese botón, aparece AccountWindow. Cuando cierro AcountWindow, quiero algo en el SettingsWindow para cambiar (una etiqueta). De ahí la constante creación de ventanas.
Además, Cerca es algo que siempre hay que tratar porque el botón X en el marco de la ventana ...

Respuesta

5

Si realmente se necesita para hacer cumplir una sola instancia de una ventana, a continuación, una instancia estática (un poco de sabor de lo que tienes) con un método de creación de fábrica es ciertamente una opción viable, al igual que una sola instancia de DataContext cuando se trabaja con una base de datos.

También podría escribir su propia clase WindowManager, aunque eso parece exagerado, y será esencialmente lo mismo (excepto que los métodos de fábrica estarían en una sola clase).

Sin embargo, al volver a leer su publicación, me pregunto si este es un caso de falta del bosque para los árboles. Al mencionar su SettingsWindow, que a su vez llama a AccountWindow, me hace pensar que simplemente debería usar ShowDialog(). Esto abre una ventana de forma modal, lo que significa que no puede haber interacción con la ventana de llamada (o cualquier otra ventana en su aplicación). Simplemente establece una propiedad en ese diálogo, establece DialogResult en true cuando se presiona el botón Aceptar y lee esa propiedad en la ventana principal.

Básicamente, usted simplemente usa el ShowDialog así. Estoy omitiendo muchos de los detalles de implementación, en lo que respecta a la vinculación frente a la codificación dura de los controles. Esos detalles no son tan importantes como solo ver cómo funciona ShowDialog.

Para simplificar, suponga que tiene una clase llamada MyAppOptions que, bueno, refleja las opciones de su aplicación. Voy a dejar la mayor parte de los detalles de implementación de esto para la simplicidad, pero probablemente llevaría a cabo INotifyPropertyChanged, tienen métodos y campos y propiedades, etc.

public class MyAppOptions 
{ 
    public MyAppOptions() 
    { 
    } 

    public Boolean MyBooleanOption 
    { 
     get; 
     set; 
    } 

    public String MyStringOption 
    { 
     get; 
     set; 
    } 
} 

A continuación, vamos a hacer este sencillo, y asumo que desea mostrar un cuadro de diálogo Opciones cuando presiona un botón en alguna ventana. Además, supondré que hay variables que se han establecido con sus opciones, que se cargaron al inicio.

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

Supongamos ahora que el OptionsDialog es una ventana que ha creado en su proyecto, y tiene una casilla en lo relacionado con la MyBooleanOption y un cuadro de texto para MyStringOption. También tiene un botón Aceptar y un botón Cancelar. Es probable que el código subyacente use Binding, pero por ahora codificaremos los valores.

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

Este es un ejemplo bastante básico en cuanto a los detalles de implementación. Busque en línea ShowDialog para obtener más detalles. Las claves importantes a tener en cuenta son:

  • ShowDialog abre una ventana modal, que significa que es la única ventana de la aplicación que se puede interactuar con .
  • Al establecer DialogResult en true se cerrará el cuadro de diálogo, que puede ser marcado por el padre que llama.
  • Al establecer DialogResult en false, también cerrará el cuadro de diálogo , en cuyo caso omite la actualización de los valores en la ventana de llamada .
  • Al pulsar el botón X en la ventana ajusta automáticamente la DialogResult en false
  • Puede tener propiedades públicas en la ventana de diálogo que se pueden establecer antes de hacer el ShowDialog, y puede obtener valores a partir después de que desaparezca el diálogo. Estará disponible mientras el diálogo aún esté en el alcance.
+0

¡Interesante! En este caso, no me falta el bosque para los árboles ya que soy totalmente nuevo.NET y, además, ¡casi siempre programo cosas del lado del servidor! Este es un territorio totalmente nuevo para mí :) Agradecería si pudieras actualizar tu respuesta con un ejemplo. – biasedbit

+0

¡Gracias por el ejemplo! :RE – biasedbit

13

probablemente hay mejores maneras de hacer esto, pero aquí es una forma relativamente sencilla .... pon un bool estático en tu clase de ventana para marcar si está abierto o no. luego, en el evento load() lo establece en true, y en el evento close lo establece en false. Luego, en el código que abre la ventana, verifique la bandera.

aquí es un poco de pseudo-código para darle una idea ...

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

gusta esta mejor que todo el producto único con lo mutex. Deja cierto margen para abrir más de una ventana, pero las probabilidades de que eso suceda en las aplicaciones basadas en UI son escasas o nulas. Soy un tipo servidor, no puedo dejar de pensar en todos los peores escenarios en simultaneidad :) – biasedbit

1

Lo siguiente se extiende a la solución anterior para volver a mostrar la ventana si ya está abierta. En este caso, es una ventana de ayuda.

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

Este código está todo en un modelo de vista (MVVM) asociado a la ventana. Es invocado por un ICommand enganchado a un botón en la ventana (¡naturalmente, muestra un signo de interrogación!) La siguiente propiedad está involucrada (en este caso es un Telerik RadWindow pero puede ser cualquier objeto de ventana, y usted puede probablemente también acaba de almacenar el identificador de ventana, pero utilizando esta propiedad permite la manipulación del objeto más suavemente por ejemplo HelpWin.BringToFront() como en el ejemplo anterior ...

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

en la propia ventana (ventana de 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; 
    } 

y en la vista Xaml ... ...

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

¿Qué le parece usar un Singleton?

public class MyWindow : Window { 

    private static MyWindow instance; 

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

Entonces sólo tiene que utilizar

MyWindow.Instance.Show() and MyWindow.Instance.Hide() 
Cuestiones relacionadas