Модальное окно в Silverlight

Показывает универсальный механизм создания модальных окон

Скачать исходный код - 40.68 Кб

Замечание: версия 2

Данная статья – обновление исходной, опубликованной в сентябре 2008г. После публикации исходной статьи было замечено, что есть проблемы с размещением элементов управления, таких как ComboBox и DataGrid, в модальном окне. Проблема была с классом Popup (всплывающий), не добавленным в визуальное дерево.

В качестве решения было добавлено свойство static, которое нужно установить лишь один раз в приложении, и в идеале оно должно быть инициализировано в VisualRoot (визуальный корень) главной страницы и должен принадлежать к классу Grid (сетка).

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();

        ModalControl.ParentHost = LayoutRoot; // инициализация родительского элемента управления
    }

    // остальная часть кода убрана
}

После инициализации ModalControl.ParentHost можно использовать модальные окна для размещения ComboBoxes и DataGrids.

Демонстрационный проект показывает, как использовать ModalControl и обновленный MessageBox.

Введение

Данная статья показывает, как реализовать класс, делающий возможным модальный вывод на экран любого элемента управления, порожденного из UserControl.

Вводная информация

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

Это работает очень успешно и является самой полной и легкой в использовании реализацией. Но окно конструктора Expression Blend не отображает элементы управления, не порожденные непосредственно от UserControl, поэтому теряются все хорошие функции рисования Blend.

Был придуман другой способ получения такого же поведения, без потери рисования с помощью Blend. Он заключается в агрегации.

Функции класса ModalControl

•    Модальный вывод на экран любого элемента управления, порожденного от UserControl
•    Перетаскивание размещенного элемента управления
•    Центрирование размещенного элемента управления при изменении размера окна браузера
•    Препятствование вытаскиванию размещенного элемента управления за пределы окна браузера

Проект

Данный проект основан на проекте мастера Silverlight, демонстрируя, как показывать мастер модально,  а созданный MessageBox показывает, как можно упаковать класс ModalControl для создания автономного модального окна.

Как он выглядит

Ниже приведена главная страница проекта мастера Silverlight, измененная так, чтобы кнопка показывала мастер модально. Также была добавлена кнопка “Показать диалоговое окно”, вызывающая всплывающее модальное диалоговое окно, реализованное путем агрегации ModalControl.


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


 
Здесь показано модальное диалоговое окно:


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

Вы можете применять сервисы, предоставляемые классом ModalControl, двояко:
•    Использовать как вспомогательный класс и передавать в функцию ShowModal UserControl, который вам нужно вывести на экран модально.
•    Обертывать ModalClass в ваш элемент управления, MessageBox показывает, как использовать данный метод.

ModalControl в качестве помощника

Заметьте в коде ниже, что нужно определить лишь один экземпляр ModalControl; важно, что ShowModal принимает элемент управления для отображения в качестве параметра, а HideModal возвращает UserControl.

// объявляется член экземпляра класса ModalControl
ModalControl _oModalCtrl = new ModalControl();

void OnShowWizardClick(object sender, RoutedEventArgs e)
{
    // создается экземпляр мастера
    Wizard oWizard = new Wizard();

    // задается контекст данных и добавляются страницы в мастер
    oWizard.DataContext = LayoutRoot.DataContext;
    oWizard.Pages.Add(new WizardPage1());
    oWizard.Pages.Add(new WizardPage2());
    oWizard.Pages.Add(new WizardPage3());

    // добавляется слушатель событий страниц в мастер
    oWizard.PageEvent += new WizardPageEvent(OnPageEvent);

    // вызывается ShowModal путем передачи ссылки на мастер модальному элементу управления
    // этот вызов выведет мастер модально, это может быть любой пользовательский элемент управления
   _oModalCtrl.ShowModal(oWizard);
}

// здесь обрабатываются события страницы мастера
void OnPageEvent(Wizard sender, WizardEventArgs e)
{
   _txtMsg.Text = string.Format("Action: {0}, Current: {1}, New: {2}",
   e.Action, e.CurrentPageIndex, e.NewPageIndex);

   // Нажатие кнопки Завершить в мастере вызывает закрытие
   // модального элемента управления, возвращающее ссылку на мастер, что позволяет (позволяющую)
   // выполнить некоторую обработку при необходимости – параметр отправитель делает то же самое
   if(e.Action == WizardAction.Finish)
   {
     // закрытие мастера
     Wizard oWizard = (Wizard)_oModalCtrl.HideModal();
   }
}

Обертывание ModalControl

Упаковка ModalControl внутри вашего собственного класса столь же проста, она полностью показана в классе MessageBox в проекте ModalWizard здесь. Данный код – сильно упрощенная версия реальной реализации MessageBox, но показывающая важные куски.

public class MessageBox : UserControl
{
 // статическая демонстрационная функция
 public static MessageBox Show(string Title, string Message)
 {
// здесь создается экземпляр MessageBox 
MessageBox oBox = new MessageBox(Title, Message);
  // его закрытый член используется для вывода диалогового окна
  oBox.ModalHost.ShowModal(oBox);
  // возвращается ссылка на MessageBox
  return oBox;
 }

 // члены экземпляра
 // --------------------------------------------------------------------
 // объявление закрытого экземпляра ModalControl
 ModalControl ModalHost;

 // передаются параметры конструктору
 private MessageBox(string Title, string Message)
 {
  InitializeComponent();
  txtTitle.Text = Title;
  txtMessage.Text = Message;

  ModalHost = new ModalControl();  // создается экземпляр ModalControl

  btClose.Click += OnCloseClick;
 }
 // обрабатывается нажатие кнопки закрыть
 private void OnCloseClick(object sender, RoutedEventArgs e)
 {
  // HideModal вызывается в модальном элементе управления для закрытия окна сообщения
  ModalHost.HideModal();
 }
}

Использование класса/элемента управления MessageBox

Следующий кусок кода показывает, как использовать элемент управления MessageBox; нужно помнить, что ModalControl не отображает дочерний/размещенный элемент управления модально, он лишь дает иллюзию модальности. В этой версии MessageBox не нужно хранить ссылку на MessageBox, так как допускается одновременное отображение только одного MessageBox. Такое допущение позволяет иметь единственную статическую ссылку на MessageBox, тем самым делая MessageBox более похожим на WinForms MessageBox.

void OnShowDialogClick(object sender, RoutedEventArgs e)
{
     MessageBox.Show("Некий модальный заголовок", "Это некое сообщение!",
        MessageBox.Buttons.YesNo,
        MessageBox.Icons.Information, OnDialogClosed);
}

bool OnDialogClosed(object sender, ExitCode e)
{
    Debug.Print("Диалоговое окно закрылось с кодом: " + e);
    return true;
}

Интересные особенности

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