C # WPF, как обеспечить единичные экземпляры окон - PullRequest
11 голосов
/ 21 октября 2010

Я хотел бы знать, как лучше (читай наиболее элегантно) иметь один экземпляр данного окна для приложения в WPF.

Я новичок в .NET и WPF и чтоЯ придумал, выглядит довольно отстойно.

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

Дело в том ... это выглядит как полная чушь.Должен быть какой-то способ достичь той же цели более элегантным способом, верно?

PS: Я часто использую событие Closed для изменения значений в других открытых окнах.Например, у меня есть SettingsWindow с кнопкой «Аккаунт».Когда я нажимаю эту кнопку, появляется AccountWindow .Когда я закрываю AcountWindow , я хочу, чтобы что-то в SettingsWindow изменилось (метка).Отсюда постоянное создание окон.
Кроме того, с Close всегда нужно иметь дело из-за кнопки X на рамке окна ...

Ответы [ 5 ]

15 голосов
/ 21 октября 2010

, вероятно, есть более эффективные способы сделать это, но здесь есть относительно простой способ .... установите статический тип bool для вашего класса окна, чтобы пометить, открыт он или нет.затем в событии load () установите для него значение true, а для события close - для false.Затем в коде, открывающем окно, установите флажок.

вот псевдокод, чтобы дать вам представление ...

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

}
7 голосов
/ 21 октября 2010

Если вам действительно необходимо применить один экземпляр окна, то статический экземпляр (немного похожий на то, что у вас есть) с помощью метода создания фабрики, безусловно, является приемлемым вариантом, очень похожим на один экземпляр DataContext при работе с базой данных..

Вы также можете написать свой собственный класс WindowManager, хотя это кажется излишним, и по сути это будет то же самое (за исключением того, что методы Factory будут в одном классе).

Однако,перечитывая твой пост, мне интересно, если это случай пропустить лес за деревьями.Ваше упоминание вашего SettingsWindow, которое в свою очередь вызывает AccountWindow, заставляет меня думать, что вы должны просто использовать ShowDialog ().Это открывает окно модально, что означает, что не может быть никакого взаимодействия с вызывающим окном (или любым другим окном в вашем приложении).Вы просто устанавливаете свойство в этом диалоговом окне, устанавливаете DialogResult в true при нажатии кнопки ОК и читаете это свойство в родительском окне.

По сути, вы просто используете ShowDialog, как это.Я опускаю много деталей реализации, в том числе связывание и жесткое программирование для элементов управления.Эти детали не так важны, как просто просмотр работы ShowDialog.

Для простоты предположим, что у вас есть класс MyAppOptions, который, в общем-то, отражает параметры вашего приложения.Я оставлю большинство деталей реализации этого для простоты, но он, вероятно, будет реализовывать INotifyPropertyChanged, иметь методы, поля и свойства и т. Д.

public class MyAppOptions
{
    public MyAppOptions()
    {
    }

    public Boolean MyBooleanOption
    {
        get;
        set;
    }

    public String MyStringOption
    {
        get;
        set;
    }
}

Затем давайте сделаем это простым и предположим, что выхотите показать диалог настроек, когда вы нажимаете кнопку в каком-то окне.Кроме того, я предполагаю, что есть переменные, которые были установлены с вашими параметрами, которые были загружены при запуске.

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

Теперь предположим, что OptionsDialog - это окно, которое вы создали в своем проекте, и оноимеет флажок, связанный с MyBooleanOption и TextBox для MyStringOption.Он также имеет кнопку ОК и кнопку Отмена.Кодовая часть, скорее всего, будет использовать Binding, но сейчас мы жестко закодируем значения.

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

Это довольно простой пример с точки зрения деталей реализации.Поиск онлайн для ShowDialog для получения дополнительной информации.Важные ключи, которые нужно запомнить:

  • ShowDialog открывает окно модально, то есть это единственное окно в вашем приложении, с которым можно взаимодействовать.
  • Если для DialogResult задано значение true, диалоговое окно закроется, что можно проверить из вызывающего родителя.
  • Если для DialogResult установлено значение false, диалоговое окно также будет закрыто. В этом случае вы пропускаете обновление значений в вызывающем окне.
  • Нажатие кнопки X в окне автоматически устанавливает для DialogResult значение false
  • Вы можете иметь открытые свойства в диалоговом окне, которые можно установить перед выполнением ShowDialog, и получать значения последиалог исчезает.Он будет доступен, пока диалог находится в области видимости.
1 голос
/ 29 августа 2011

Нижеследующее распространяется на приведенное выше решение, чтобы повторно показать окно, если оно уже открыто. В данном случае это окно справки.

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

Этот код находится в модели представления (MVVM), связанной с окном. Он вызывается ICommand, подключенной к кнопке в окне (естественно, он показывает знак вопроса !!) Следующее свойство задействовано (в данном случае это Telerik RadWindow, но это может быть любой оконный объект, и вы, возможно, также можете просто сохранить дескриптор окна, но использование этого свойства позволяет более плавно управлять объектом, например HelpWin.BringToFront () как в приведенном выше примере ...

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

В самом окне (окно 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;
    }

и в представлении Xaml ... ...

  DataContext="{Binding Path=OnlineHelpViewModelStatic,Source={StaticResource Locator}}" 
  RestoreMinimizedLocation="True" 
  **Loaded="HelpWindowLoaded" Unloaded="HelpWindowUnloaded"** >
0 голосов
/ 16 сентября 2018

Вот альтернативный подход, который не требует статического свойства для установки и обновления в каждом окне:

public static bool IsWindowInstantiated<T>() where T : Window
{
    var windows = Application.Current.Windows.Cast<Window>();
    var any = windows.Any(s => s is T);
    return any;
}

Использование:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    if (IsWindowInstantiated<SettingsWindow>())
        return;

    var window = new SettingsWindow();

    window.Show();
}
0 голосов
/ 18 декабря 2013

Как насчет использования Singleton?

public class MyWindow : Window {

    private static MyWindow instance;

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

Тогда просто используйте

    MyWindow.Instance.Show() and MyWindow.Instance.Hide()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...