Практикум прикладного программирования на C# в среде VS.NET 2005

Нестандартные формы и стандартные диалоги

Показывать лекцию целиком

Упражнение 1. Создание непрямоугольной формы

Файлы к лабораторной работе Вы можете скачать здесь.

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

Но мы, как проектировщики, должны знать о существовании возможности создания окон или элементов управления и в нетрадиционном стиле. При создании непрямоугольных форм и элементов управления используется свойство Region базового класса System.Windows.Forms.Control всех визуальных компонентов, которое ожидает ссылки на объекты-экземпляры класса System.Drawing.Region.

  • Командой File/New/Project создайте новый проект FormsApp
  • Через панель Solution Explorer откройте файл Program.cs и скопируйте из него в класс Form1 точку входа для индивидуального запуска новой формы
  • Теперь класс Form1 файла Form1.cs должен стать таким

    public partial class Form1 : Form
        {
            [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
        
            public Form1()
            {
                InitializeComponent();
            }
        }
  • Выполните команду Project/FormsApp Properties и установите в параметре Startup object вкладки Application форму Form1 в качестве стартовой
  • Через панель Solution Explorer удалите файл Program.cs
  • Откройте файл Form1.cs в режиме View Designer, выделите форму щелчком по ее заголовку и через панель Properties установите размеры формы в свойстве
  • Size=450;200

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

    В библиотеке .NET Framework (FCL - Framework Class Library) существует класс System.Drawing.Drawing2D.GraphicsPath, который можно использовать как коллекцию для заблаговременного сохранения различных графических примитивов, которые затем можно нарисовать все "одним махом", если передать эту коллекцию в функцию

    System.Drawing.Graphics.DrawPath(System.Drawing.Pen pen, System.Drawing.Drawing2D.GraphicsPath path).

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

    System.Drawing.Graphics.FillPath(System.Drawing.Brush brush, System.Drawing.Drawing2D.GraphicsPath path)

    Давайте продемонстрируем упомянутые возможности, нарисовав на новой форме головку Буратино. Код рисования поместим в отдельную функцию, а вызов этой функции разместим в переопределении метода OnPaint() формы, чтобы при каждом разрушении окна формы (перемещение, изменение размеров) прорисовка изображения повторялась.

  • Подключите в начале файла Form1.cs пространство имен System.Drawing.Drawing2D для сокращенной идентификации класса GraphicsPath
  • using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Drawing.Drawing2D;
  • Добавьте к классу Form1 новый метод с именем DrawBuratino(), который заполните так
  • // Рисование головки Буратино
            private void DrawBuratino()
            {
                GraphicsPath path = new GraphicsPath();
                path.AddArc(20, 20, 120, 120, 0, 330);  // Голова без носа
                path.AddArc(220, 20, 10, 10, 270, 180); // Удаленный кончик носа
                path.CloseFigure();                     // Замкнули незаконченные дуги
                path.AddEllipse(90, 40, 20, 20);        // Добавили глаз
        
                Graphics gr = this.CreateGraphics();  // Создаем холст
                gr.DrawPath(Pens.Red, path);          // Рисуем контур Буратино
                gr.TranslateTransform(200, 0);        // Смещаемся вправо
                gr.FillPath(Brushes.Blue, path);      // Рисуем заливку Буратино
                gr.DrawPath(Pens.Red, path);          // Рисуем контур для заливки
            }
  • Переопределите метод OnPaint() формы, который наследуется ею от класса Control
  • Код начинайте вводить вручную с ключевого слова override. Когда вы его введете и нажмете клавишу пробела, подсказчик кода ( суфлер ) выдаст список переопределяемых методов формы, в котором можно быстро позиционироваться, продолжая набирать имя переопределяемого метода в любом регистре. Нажатие клавиши ввода завершит создание правильной заготовки переопределяемого метода.

  • Разместите в переопределенном методе вызов функции рисования
  • // Переопределяем метод перерисовки формы 
            protected override void OnPaint(PaintEventArgs e)
            {
                DrawBuratino();
                base.OnPaint(e);
            }
  • Запустите приложение и должен получиться такой результат
  • Теперь сделаем форму непрямоугольной. Для этого необходимо воспользоваться свойством Region формы, определяющим ее силуэт.

  • Добавьте к классу Form1 новую функцию с именем DrawForm()
  • // Рисование непрямоугольной формы
        private void DrawForm()
        {
          GraphicsPath path = new GraphicsPath();
          path.AddEllipse(0, -300, this.Width, 600);
          this.Region = new Region(path);
        }

    В функции

    public void AddEllipse (int x, int y, int width, int height)

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

  • Добавьте в функции OnPaint() вызов этой новой функции DrawForm()
  • // Переопределяем метод перерисовки формы 
            protected override void OnPaint(PaintEventArgs e)
            {
                DrawBuratino();
                DrawForm();
                base.OnPaint(e);
            }
  • Перейдите в режим View Designer формы, выделите форму щелчком по ее заголовку и через панель Properties в режиме Events создайте обработчик для события Resize, в который поместите требование перерисовки формы при изменении размеров
  • private void Form1_Resize(object sender, EventArgs e)
            {
                this.Invalidate();
            }
  • Запустите форму на выполнение - должен получиться такой результат
  • Событие Resize мы использовали для прорисовки формы на случай изменения ее размеров в сторону возрастания.

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

  • Добавьте к классу Form1 объявления двух локальных полей целого типа, которые будут хранить координаты курсора в момент нажатия на левую кнопку мыши над формой
  • int x, y;
  • Создайте для формы через панель Properties в режиме Events обработчики для событий MouseDown и MouseMove, которые заполните так
  • private void Form1_MouseDown(object sender, MouseEventArgs e)
            {
                // Сохраняем координаты мыши на момент щелчка относительно формы
                if (e.Button == MouseButtons.Left)
                {
                    x = e.X;
                    y = e.Y;
                }
            }
        
            private void Form1_MouseMove(object sender, MouseEventArgs e)
            {
                // Измеряем смещение относительно щелчка
                // и подвигаем форму на это смещение
                if (e.Button == MouseButtons.Left)
                {
                    this.Left += e.X - x;
                    this.Top += e.Y - y;
                }
            }

    Этот код обеспечивает перемещение формы относительно начального положения, которое она имела до нажатия левой кнопки мыши, ровно настолько, на сколько переместился курсор мыши после нажатия кнопки. Поэтому нажатый курсор и форма перемещаются синхронно.

    Остался последний штрих для этого упражнения - изменять форму курсора при захвате формы.

  • Добавьте в код обработчика нажатия кнопки мыши строку изменения формы курсора
  • private void Form1_MouseDown(object sender, MouseEventArgs e)
            {
                // Сохраняем координаты мыши на момент щелчка относительно формы
                if (e.Button == MouseButtons.Left)
                {
                    x = e.X;
                    y = e.Y;
        
                    // Форма курсора 
                    Cursor.Current = Cursors.SizeAll;
                }
            }
  • Измените заголовок формы, задав ее свойству Text значение " Упражнение 1. Непрямоугольная форма "
  • Запустите приложение и убедитесь, что при нажатии левой кнопки мыши на клиентской области форма курсора меняется и окно послушно следует за ним
  • Упражнение 2. Страницы свойств и стандарные диалоги

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

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

  • System.Windows.Forms.MessageBox - простое диалоговое окно сообщений
  • System.Windows.Forms.OpenFileDialog - диалоговое окно открытия файла
  • System.Windows.Forms.SaveFileDialog - диалоговое окно сохранения файла
  • System.Windows.Forms.ColorDialog - диалоговое окно выбора цвета
  • System.Windows.Forms.FontDialog - диалоговое окно выбора шрифта
  • System.Windows.Forms.PrintDialog - окно задания на печать документа
  • System.Windows.Forms.PrintPreviewDialog - окно предварительного просмотра документа перед печатью
  • System.Windows.Forms.PageSetupDialog - настройка параметров страницы
  • System.Windows.Forms.FolderBrowserDialog - диалоговое окно для навигации по папкам файловой системы компьютера
  • Добавьте к проекту новую форму командой Project/Add Windows Form с именем StandardDialogs.cs
  • Добавьте к классу StandardDialogs точку входа вместе с атрибутом однопоточности для автономного запуска формы
  • [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new StandardDialogs());
            }
  • Установите в свойствах проекта форму StandardDialogs стартовой
  • Настройте свойства формы StandardDialogs в соответствии с таблицей
  • Таблица свойств формы StandardDialogs
    Свойство Значение Пояснения
    StartPosition CenterScreen Открывать в центре экрана
    FormBorderStyle FixedDialog Запретить пользователю менять размер
    MaximizeBox false Отключить системную кнопку развертывания
    MinimizeBox false Отключить системную кнопку свертывания
    ShowInTaskbar false Запретить появление в панели задач
    Size 576; 488 Размер формы
    Text Упражнение 2. StandardDialogs Заголовок окна

    В этом упражнении мы будем рассматривать ряд стандартных диалогов по отдельности. Для настройки и управления каждым рассматриваемым диалогом организуем свое окно, которое будет являться вкладкой компонента TabControl. Это компонент является контейнером для заданного количества вкладак и существенно экономит место на экране. Отметим некоторые его свойства

    Некоторые свойства компонента TabControl
    Свойство Пояснения
    Cursor Определяет форму курсора, отображаемого над элементом
    Multiline Разрешает поддерживать несколько вкладак
    TabCount Количество вкладок
    TabPages Коллекция вкладок в элементе
  • Поместите на форму из вкладки Containers панели Toolbox компонент TabControl и задайте ему через панель Properties следующие свойства
  • Начальные свойства компонента TabControl
    Свойство Значение
    Name tabControl
    Dock Fill
    Multiline True
    TabPages.Name tabPage0
    TabPages.Text Обзор
    TabPages.Name tabPage1
    TabPages.Text MessageBox
    TabPages.Name tabPage2
    TabPages.Text OpenFileDialog
    TabPages.Name tabPage3
    TabPages.Text SaveFileDialog
    TabPages.Name tabPage4
    TabPages.Text ColorDialog
    TabPages.Name tabPage5
    TabPages.Text FontDialog
    TabPages.Name tabPage6
    TabPages.Text PrintDialog
    TabPages.Name tabPage7
    TabPages.Text PrintPreviewDialog
    TabPages.Name tabPage8
    TabPages.Text PageSetupDialog
    TabPages.Name tabPage9
    TabPages.Text FolderBrowserDialog
    TabPages.Name tabPage10
    TabPages.Text MyDialog

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

    Разработка вкладки "Обзор"

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

  • В режиме View Designer выделите вкладку " Обзор " и поместите на нее компонент GroupBox как контейнер для красоты оформления. Настройте его так
  • Контейнер для радиокнопок на вкладке "Обзор"
    Свойство Значение
    Name groupReview
    Dock Fill
    Text Пусто
  • Выделив компонент groupReview, поместите внутрь этого контейнера две текстовых метки Label и 10 радиокнопок (элемент RadioButton ). Выровняйте их и настройте как показано на снимке
  • Для текстовой метки с белым фоном установите такие свойства
  • Настройки текстовой метки
    Свойство Значение
    AutoSize false
    Location 79, 18
    Size 364, 126
    BackColor White
    Font.Name Arial
    Font.Size 20
    Font.Bold True
    ForeColor Blue
    Text Визуальный обзор окон стандартных диалогов
    TextAlign MiddleCenter

    Радиокнопки согласованно работают только тогда, когда они помещены в контейнер. Это может быть форма Form, элемент GroupBox или элемент Panel. Если требуется создать несколько отдельных групп радиокнопок, то их нужно скомпоновать по отдельным контейнерам. Элемент Panel для этих целей хорош тем, что его границы могут быть невидимыми. Любой контейнер является родителем для размещенных в нем элементов. Изменяя свойства контейнера (перемещение, видимость, цвет, стиль), можно управлять аналогичными свойствами всей группы дочерних элементов.

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

  • Предварительно замкните интерфейс, выполнив групповую команду Format/Lock Controls
  • Выделите все радиокнопки вкладки, обведя их курсором. В панели Properties создайте общий для всех радиокнопок обработчик для события Click двойным щелчком на поле этого события
  • Опять выделите все радиокнопки и в панели Properties скорректируйте имя обработчика события Click, сгенерированное автоматически, на свое - standardDialogs_Click
  • Оболочка с обработчиками работает так: если тело обработчика пустое и нет иных подключений, то он переименовывается, если непустое или есть на него ссылки иных элементов, то создается пустой обработчик с новым именем.

    В теле обработчика воспользуемся тем фактом, что значение свойства Text всех радиокнопок мы задали по имени соответствующего диалога, кроме последнего, который разработаем позднее. При каждом срабатывании события Click мы по свойству Text радиокнопки определим, какой диалог создать и вывести на экран.

  • Заполните обработчик standardDialogs_Click() следующим кодом
  • #region Вкладка "Обзор"
            private void standardDialogs_Click(object sender, System.EventArgs e)
            {
                RadioButton radio = (RadioButton)sender;
                // Для контроля при отладке
                System.Diagnostics.Debug.WriteLine("Нажата радиокнопка " + radio.Text);
        
                switch (radio.Text.Trim())// Чтобы убрать возможные пробелы
                {
                    case "MessageBox":
                        MessageBox.Show( // Статический метод - модальный вызов
                            "Это удобная форма выдачи сообщений!", // Сообщение
                            "Стандартный диалог MessageBox", // Заголовок окна
                            MessageBoxButtons.OK, // Кнопка OK
                            MessageBoxIcon.Stop);// Критическая иконка
                        break;
                    case "OpenFileDialog":
                        OpenFileDialog openFileDialog = new OpenFileDialog();
                        openFileDialog.Title = "OpenFileDialog";
                        openFileDialog.ShowDialog();// Модальный вызов
                        openFileDialog.Dispose();// Вызов сборщика мусора
                        break;
                    case "SaveFileDialog":
                        SaveFileDialog saveFileDialog = new SaveFileDialog();
                        saveFileDialog.Title = "SaveFileDialog";
                        saveFileDialog.ShowDialog();// Модальный вызов
                        saveFileDialog.Dispose();// Вызов сборщика мусора
                        break;
                    case "ColorDialog":
                        ColorDialog colorDialog = new ColorDialog();
                        colorDialog.ShowDialog();// Модальный вызов
                        colorDialog.Dispose();// Вызов сборщика мусора
                        break;
                    case "FontDialog":
                        FontDialog fontDialog = new FontDialog();
                        fontDialog.ShowDialog();// Модальный вызов
                        fontDialog.Dispose();// Вызов сборщика мусора
                        break;
                    case "PrintDialog":
                        PrintDialog printDialog = new PrintDialog();
                        printDialog.AllowSomePages = true;// Настраиваем доступ к выбору страниц
                        printDialog.ShowHelp = true; // Отображать кнопку помощи
                        printDialog.Document =
                            new System.Drawing.Printing.PrintDocument();
                        DialogResult result = printDialog.ShowDialog();// Модальный вызов
                        if (result == DialogResult.OK)
                            MessageBox.Show("Пользователь хотел печатать");
                        printDialog.Dispose();// Вызов сборщика мусора
                        break;
                    case "PrintPreviewDialog":
                        PrintPreviewDialog printPreviewDialog =
                            new PrintPreviewDialog();
                        printPreviewDialog.Document =
                            new System.Drawing.Printing.PrintDocument();
                        printPreviewDialog.ShowDialog();// Модальный вызов
                        printPreviewDialog.Dispose();// Вызов сборщика мусора
                        break;
                    case "PageSetupDialog":
                        PageSetupDialog pageSetupDialog = new PageSetupDialog();
                        pageSetupDialog.PageSettings =
                            new System.Drawing.Printing.PageSettings();
                        pageSetupDialog.PrinterSettings =
                            new System.Drawing.Printing.PrinterSettings();
                        //Не показывать сеть в диалоге принтера
                        pageSetupDialog.ShowNetwork = false;
                        pageSetupDialog.ShowDialog();// Модальный вызов
                        pageSetupDialog.Dispose();// Вызов сборщика мусора
                        break;
                    case "FolderBrowserDialog":
                        FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
                        folderBrowserDialog.Description = "Установите нужный каталог текущим";
                        // Каталог по умолчанию - "Мои документы" 
                        folderBrowserDialog.RootFolder = Environment.SpecialFolder.Personal;
                        folderBrowserDialog.ShowDialog();// Модальный вызов
                        folderBrowserDialog.Dispose();// Вызов сборщика мусора
                        break;
                    default:
                        MessageBox.Show("Пока нет - создадим позднее", 
                            "Самодельный диалог",
                            MessageBoxButtons.OK, 
                            MessageBoxIcon.Stop);
                        break;
                }
            }
            #endregion

    Здесь мы применили операторы #region... #endregion, чтобы для удобства редактирования разместить код, относящийся к каждой вкладке, в отдельные секции.

  • Запустите приложение и испытайте вкладку "Обзор" . Должны вызываться окна всех упомянутых диалогов, например, ColorDialog
  • Заметьте, что все диалоги мы создаем и вызываем через код, хотя каждый из них имеет визуальный аналог во вкладке Toolbox. Здесь всю работу графических возможностей среды разработки мы сделали вручную. Это не всегда лучший вариант для быстрой разработки интерфейса, но всегда нужно понимать, что делает оболочка с кодом, чтобы в любой момент можно было вмешаться и подправить код более тонко. Аналогичная ситуация при работе с Web -редакторами: основную работу можно автоматизировать через графическую среду, но может возникнуть необходимость вмешиваться в генерируемый ею HTML -код.

    Разработка вкладки MessageBox

    На этой вкладке нашего упражнения мы испытаем возможности простейшего стандартного диалогового окна MessageBox, которое применяется разработчиками для быстрого оформления сообщений пользователю. Это окно реализуется классом System.Windows.Forms.MessageBox, который имеет 12 перегружаемых статических методов Show() для отображения сообщений в различной форме. Чтобы воспользоваться этим классом, не нужно создавать его экземпляры, а можно сразу вызывать метод MessageBox.Show(), в который помещать нужные аргументы. Перегрузки метода Show() имеют следующий синтаксис

  • public static System.Windows.Forms.DialogResult Show ( System.String text )
  • public static System.Windows.Forms.DialogResult Show ( System.String text , System.String caption )
  • public static System.Windows.Forms.DialogResult Show ( System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons )
  • public static System.Windows.Forms.DialogResult Show ( System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons , System.Windows.Forms.MessageBoxIcon icon )
  • public static System.Windows.Forms.DialogResult Show ( System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons , System.Windows.Forms.MessageBoxIcon icon , System.Windows.Forms.MessageBoxDefaultButton defaultButton )
  • public static System.Windows.Forms.DialogResult Show ( System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons , System.Windows.Forms.MessageBoxIcon icon , System.Windows.Forms.MessageBoxDefaultButton defaultButton , System.Windows.Forms.MessageBoxOptions options )
  • public static System.Windows.Forms.DialogResult Show ( System.Windows.Forms.IWin32Window owner , System.String text )
  • public static System.Windows.Forms.DialogResult Show ( System.Windows.Forms.IWin32Window owner , System.String text , System.String caption )
  • public static System.Windows.Forms.DialogResult Show ( System.Windows.Forms.IWin32Window owner , System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons )
  • public static System.Windows.Forms.DialogResult Show ( System.Windows.Forms.IWin32Window owner , System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons , System.Windows.Forms.MessageBoxIcon icon )
  • public static System.Windows.Forms.DialogResult Show ( System.Windows.Forms.IWin32Window owner , System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons , System.Windows.Forms.MessageBoxIcon icon , System.Windows.Forms.MessageBoxDefaultButton defaultButton )
  • public static System.Windows.Forms.DialogResult Show ( System.Windows.Forms.IWin32Window owner , System.String text , System.String caption , System.Windows.Forms.MessageBoxButtons buttons , System.Windows.Forms.MessageBoxIcon icon , System.Windows.Forms.MessageBoxDefaultButton defaultButton , System.Windows.Forms.MessageBoxOptions options )
  • В нашей вкладке мы реализуем вызовы наиболее распространенных ( шесть первых в списке ) перегрузок метода MessageBox.Show(). Для этого нам еще понадобятся раскрывающиеся списки ComboBox, текстовые поля TextBox, текстовые метки Label, чтобы выбирать настройки каждой перегрузки. Все перегрузки возвращают значение перечисления System.Windows.Forms.DialogResult, по которому можно определить выбор пользователя.

    Параметры метода Show()

    Метод MessageBox.Show() в выделенных перегрузках имеет следующие параметры:

  • System.String text - сообщение пользователю
  • System.String caption - заголовок окна
  • System.Windows.Forms.MessageBoxButtons buttons - состав кнопок ( перечисление )
  • AbortRetryIgnore
  • OK
  • OKCancel
  • RetryCancel
  • 18_23No
  • 18_23NoCancel
  • System.Windows.Forms.MessageBoxIcon icon - пиктограмма ( перечисление )
  • Asterisk
  • Error
  • Exclamation
  • Hand
  • Information
  • None
  • Question
  • Stop
  • Warning
  • System.Windows.Forms.MessageBoxDefaultButton defaultButton - кнопка, имеющая при запуске фокус ввода по умолчанию ( перечисление )
  • Button1
  • Button2
  • Button3
  • System.Windows.Forms.MessageBoxOptions options - настройки ( перечисление )
  • DefaultDesktopOnly
  • RightAlign
  • RtlReading
  • ServiceNotification
  • Все перечисленные настройки мы реализуем в нашей вкладке, чтобы продемонстрировать, как они выглядят.

    Разработка итерфейса вкладки MessageBox

    Оформим вкладку MessageBox, чтобы удобно было испытывать стандартное окно сообщений.

  • Разместите на вкладке MessageBox элементы управления и частично оформите их, как показано на снимке
  • Программирование вкладки MessageBox

  • Заполните свойство Items элемента comboBox1 следующими строками
  • 1) MessageBox.Show(text);
    2) MessageBox.Show(text, caption);
    3) MessageBox.Show(text, caption, buttons);
    4) MessageBox.Show(text, caption, buttons, icon);
    5) MessageBox.Show(text, caption, buttons, icon, defaultButton);
    6) MessageBox.Show(text, caption, buttons, icon, defaultButton, options);
  • Заполните свойство Items элемента comboBox2 следующими строками
  • AbortRetryIgnore
    OK
    OKCancel
    RetryCancel
    18_23No
    18_23NoCancel
  • Заполните свойство Items элемента comboBox3 следующими строками
  • Asterisk
    Error
    Exclamation
    Hand
    Information
    None
    Question
    Stop
    Warning
  • Заполните свойство Items элемента comboBox4 следующими строками
  • Button1
    Button2
    Button3
  • Заполните свойство Items элемента comboBox5 следующими строками
  • DefaultDesktopOnly
    RightAlign
    RtlReading
    ServiceNotification
  • Выделите на вкладке MessageBox все списки и текстовые поля (текстовые метки Label оставьте в покое ), входящие в контейнер groupBox1. Установите для них следующие значения свойств:
  • Text = ПУСТО
  • Enabled = False
  • Проверьте, что для элемента comboBox1 установлено свойство Enabled = True
  • Создайте для элемента comboBox1 обработчик события SelectedIndexChanged с именем comboBox1_SelectedIndexChanged(), который заполните так
  • private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
        {
          // Перебираем все элементы контейнера groupBox1
          // и все, кроме текстовых меток, делаем недоступными
          foreach(Control box in groupBox1.Controls)
          {
            String type = box.GetType().ToString();
            // Выделяем чистый тип без пути
            type = type.Substring(type.LastIndexOf('.') + 1);
            if( type == "Label")
              continue;
            else
              box.Enabled = false;
          }
      
          // Открываем нужные списки параметров
          // Организуем цепочку выбора
          switch(comboBox1.SelectedIndex + 1)
          {
            case 6:
              comboBox5.Enabled = true;
              goto case 5;
            case 5:
              comboBox4.Enabled = true;
              goto case 4;
            case 4:
              comboBox3.Enabled = true;
              goto case 3;
            case 3:
              comboBox2.Enabled = true;
              goto case 2;
            case 2:
              textBox2.Enabled = true;
              goto case 1;
            case 1:
              textBox1.Enabled = true;
              break;
          }
        }

    В этом коде при каждом новом выборе пользователя в списке comboBox1 мы вначале закрываем все списки и текстовые поля для параметров, затем открываем нужные в зависимости от выбранного варианта. Здесь мы вынужденно используем оператор перехода goto, так как синтаксис C# требует, чтобы непустое тело каждого варианта case заканчивалось break или goto. В языке C++ обязательного выполнения этого условия не требуется. Но пустые case могут следовать друг за другом перед первым непустым.

  • Задайте кнопке значение Name = btnShow и создайте для нее обработчик. Заполните обработчик так
  • private void btnShow_Click(object sender, EventArgs e)
            {
                // Проверяем, выбрана ли перегрузка
                if (comboBox1.Text == "")
                {
                    MessageBox.Show("Выберите перегрузку метода Show()");
                    return;
                }
        
                // Проверяем, все ли параметры заданы
                foreach (Control box in groupBox1.Controls)
                {
                    String type = box.GetType().ToString();
                    type = type.Substring(type.LastIndexOf('.') + 1);
                    // Пропускаем метки и все закрытые элементы
                    if (type == "Label" || box.Enabled == false)
                        continue;
                    // Убираем пробелы и определяем, что незаполнено
                    else if (box.Text.Trim().Length <= 0)
                    {
                        MessageBox.Show("Задайте все необходимые параметры");
                        return;
                    }
                }
        
                // Формируем массивы ссылок на перечисления
                MessageBoxButtons[] buttons = {
                    MessageBoxButtons.AbortRetryIgnore,
                    MessageBoxButtons.OK, 
                    MessageBoxButtons.OKCancel, 
                    MessageBoxButtons.RetryCancel,
                    MessageBoxButtons.18_23No, 
                    MessageBoxButtons.18_23NoCancel
                };
        
                MessageBoxIcon[] icon = {
                    MessageBoxIcon.Asterisk,
                    MessageBoxIcon.Error,
                    MessageBoxIcon.Exclamation,
                    MessageBoxIcon.Hand,
                    MessageBoxIcon.Information,
                    MessageBoxIcon.None,
                    MessageBoxIcon.Question,
                    MessageBoxIcon.Stop,
                    MessageBoxIcon.Warning  
                };
        
                MessageBoxDefaultButton[] defaultButton = {
                    MessageBoxDefaultButton.Button1,
                    MessageBoxDefaultButton.Button2,
                    MessageBoxDefaultButton.Button3
                };
        
                MessageBoxOptions[] options = {
                    MessageBoxOptions.DefaultDesktopOnly,
                    MessageBoxOptions.RightAlign,
                    MessageBoxOptions.RtlReading,
                    MessageBoxOptions.ServiceNotification
                };
        
                // Безопасно отображаем простое диалоговое окно сообщений
                try
                {
                    switch (comboBox1.SelectedIndex)
                    {
                        case 0:
                            MessageBox.Show(textBox1.Text);
                            break;
                        case 1:
                            MessageBox.Show(textBox1.Text, textBox2.Text);
                            break;
                        case 2:
                            MessageBox.Show(textBox1.Text, textBox2.Text,
                                buttons[comboBox2.SelectedIndex]);
                            break;
                        case 3:
                            MessageBox.Show(textBox1.Text, textBox2.Text,
                                buttons[comboBox2.SelectedIndex],
                                icon[comboBox3.SelectedIndex]);
                            break;
                        case 4:
                            MessageBox.Show(textBox1.Text, textBox2.Text,
                                buttons[comboBox2.SelectedIndex],
                                icon[comboBox3.SelectedIndex],
                                defaultButton[comboBox4.SelectedIndex]);
                            break;
                        case 5:
                            MessageBox.Show(textBox1.Text, textBox2.Text,
                                buttons[comboBox2.SelectedIndex],
                                icon[comboBox3.SelectedIndex],
                                defaultButton[comboBox4.SelectedIndex],
                                options[comboBox5.SelectedIndex]);
                            break;
                    }
                }
                catch (System.Exception exc)
                {
                    MessageBox.Show("Внимательно проверьте параметры метода!"
                        + Environment.NewLine
                        + exc.Message,
                        "Ошибка",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Stop);
                }
            }

    Здесь мы применили операторы обработки исключений try ( попытка ) ... catch ( ловушка ), которые позволяют сделать код более безопасным.

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

  • Выделите все раскрывающиеся списки ComboBox в контейнере groupBox1 (текстовые поля не трогайте!) и задайте вручную для их события KeyPress имя обработчика lockBox_KeyPress, который заполните так
  • // Запрещаем раскрывающиеся списки для редактирования
        private void lockBox_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
          e.Handled = true;
        }
  • Заключите код для вкладки MessageBox в секционные скобки для удобства редактирования
  • #region Вкладка MessageBox
        ..............................................
        #endregion
  • Постройте приложение и испытайте работу вкладки MessageBox. Все должно работать, как мы задумали.
  • Этап тестирования программы - наиболее ответственный при ее создании. Специально составляют карты испытаний, где предусматривают все возможные ситуации, в которых программа может оказаться. Здесь очень важно привлекать к этому реальных пользователей на самых начальных стадиях работы. Только они могут найти и вовремя подсказать возможные ошибки.

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

    Базовый класс FileDialog

    Классы System.Windows.Forms.OpenFileDialog и System.Windows.Forms.SaveFileDialog являются законченными ( sealed ) классами, поэтому не могут иметь потомков. Зато общим предком для этих двух классов является абстрактный класс System.Windows.Forms.FileDialog, который передает им много полезных общих свойств настройки.

    Некоторые публичные свойства базового класса FileDialog
    Свойство Тип Пояснения
    AddExtension bool Получает или устанавливает флаг, указывающий, должно ли диалоговое окно автоматически добавляь расширение к имени файла, если пользователь опускает расширение при ручном вводе имени файла. Значение по умолчанию true - должно
    CheckFileExists bool Получает или устанавливает флаг, указывающий, отображает ли диалоговое окно предупреждение перед закрытием, если пользователь вручную вводит имя файла, которое не существует. Значение по умолчанию false - не отображает
    CheckPathExists bool Получает или устанавливает флаг, указывающий, отображает ли диалоговое окно предупреждение перед закрытием, если пользователь вручную вводит путь, который не существует. Значение по умолчанию true - отображает
    DefaultExt string Получает или устанавливает заданное по умолчанию расширение имени файла. По умолчанию - пустая строка
    DereferenceLinks bool Получает или устанавливает флаг, указывающий, возвращает ли диалоговое окно в свойстве FileName местоположение файла, который адресуется ярлыком, или возвращает местоположение самого ярлыка (.lnk). Значение по умолчанию true - местоположение адресуемого ярлыком файла
    FileName string Получает или устанавливает строку, содержащую имя файла, выбранное в диалоговом окне. До выбора по умолчанию - пустая строка
    FileNames string[ ] Получает имена всех файлов, выбранных в диалоговом окне OpenFileDialog, если его свойство Multiselect равно true
    Filter string Получает или устанавливает строку фильтров, содержащую варианты расширений файлов, доступных для выбора в диалоговом окне
    FilterIndex int Получает или устанавливает индекс текущего фильтра, установленного в диалоговом окне. По умолчанию 1 - действует первый фильтр
    InitialDirectory string Получает или устанавливает начальный каталог, отображенный диалоговым окном. По умолчанию - пустая строка
    RestoreDirectory bool Получает или устанавливает флаг, указывающий, восстанавливает ли диалоговое окно текущий каталог перед закрытием к его первоначальному значению, если пользователь сменил каталог при поиске файлов. По умолчанию false - не восстанавливать первоначальный каталог, а запоминать новый каталог в системном реестре
    ShowHelp bool Получает или устанавливает флаг, указывающий, отображена ли кнопка Help в диалоговом окне. По умолчанию false - не включать кнопку Help в состав диалогового окна
    SupportMultiDottedExtensions bool Получает или устанавливает флаг, указывающий, поддерживает ли диалоговое окно многоточечные (составные) расширения
    Title string Получает или устанавливает заголовок диалогового окна
    ValidateNames bool Получает или устанавливает флаг, указывающий, принимает ли диалоговое окно только правильные (допустимые) имена файлов Win32. Значение по умолчанию true - принимает только правильные имена файлов Win32

    Следует помнить, что при перемещении по каталогам через OpenFileDialog или SaveFileDialog, если не была выбрана кнопка отмены, меняется текущий каталог приложения (по умолчанию RestoreDirectory=false ). Установить начальный каталог диалогового окна или восстановить текущий каталог приложения после закрытия диалогового окна позволяют свойства InitialDirectory и RestoreDirectory.

    Хотя большинство пользователей перемещается по каталогам и выбирает файлы из предложенного, кто-то может ввести имя файла или каталога вручную. В этом случае, если подняты флаги CheckFileExists и CheckPathExists, перед закрытием диалоговое окно само проверяет допустимость пути или имени файла и выдает соответствующее сообщение.

    Фильтры в свойстве Filter следуют парами друг за другом " Название фильтра|шаблон_фильтра ". Разделитель "Вертикальная черта" используется как внутри пары, отделяя видимое название фильтра от шаблона, так и между парами. Строка фильтра должна быть непрерывной. В шаблонах_фильтра не должно быть пробелов ни внутри, ни в окружении, иначе фильтр не сработает. Особенно нужно проверять, чтобы в последнем шаблоне строки фильтра не остался невидимый пробел.

    Разработка вкладки OpenFileDialog

    Кроме свойств, унаследованных от класса FileDialog, класс OpenFileDialog расширяется своими индивидуальными свойствами. Вот они

    Публичные свойства класса OpenFileDialog
    Свойство Тип Пояснения
    CheckFileExists bool Получает или устанавливает флаг, указывающий, отображает ли диалоговое окно предупреждение перед закрытием, если пользователь вручную вводит имя файла, которое не существует. Переопределяет одноименное виртуальное свойство базового класса FileDialog. Значение по умолчанию true - проверяет и отображает
    Multiselect bool Получает или устанавливает флаг, разрешающий пользователю множественный выбор файлов с помощью клавиш Ctrl или Shift. Значение по умолчанию false - одиночный выбор
    ShowReadOnly bool Определяет, показывать ли в диалоговом окне флажок "Только чтение", или показан ли он. Значение по умолчанию false - не показывать
    ReadOnlyChecked bool Определяет, установил ли пользователь показанный флажок "Только чтение"

    Если нужно разрешить пользователю создать новый файл при помощи OpenFileDialog, следует установить CheckFileExists в значение false.

    Кратко ознакомившись со свойствами диалогового окна OpenFileDialog, далее продемонстрируем некоторые возможности по использованию стандартного диалога открытия файла в одноименной вкладке нашего проекта. Здесь мы применим стандартный диалог при разработке простейшего текстового редактора. Демонстрацию настроек, унаследованных от класса FileDialog, реализуем чуть позже, во вкладке SaveFileDialog, поскольку оба диалога работы с файлами имеют общего родителя.

  • Поместите на вкладку OpenFileDialog приложения контейнер Panel и две кнопки Button вне контейнера Panel
  • Кнопки помещайте так: удерживая клавишу Ctrl, щелкните на компоненте Button вкладки Common Controls панели Toolbox. Не отпускайте клавишу Ctrl и щелкните два раза на вкладке редактируемой формы вне элемента Panel и справа от него. Щелкните на компоненте Pointer вкладки Common Controls, чтобы освободить курсор.

  • Установите для компонента Panel свойство Dock в значение Left
  • Поместите внутрь контейнера Panel компонент TextBox
  • Добавьте к форме компоненты OpenFileDialog и SaveFileDialog, которые оболочка разместит в подвале формы как невизуальные
  • Задайте свойства новых элементов так
  • Свойства элементов вкладки OpenFileDialog
    Элемент Свойство Значение
    TextBox Name txtSimpleEdit
      Text Пусто
      Multiline True
      Dock Fill
      ScrollBars Both
      WordWrap False
      MaxLength 60000
    Button Name btnOpenFile
      AutoSize true
      Text Open
    Button Name btnSaveFile
      AutoSize true
      Text Save
    OpenFileDialog Name openFileDialog
      Filter Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*
      Title Открыть текстовый файл
    SaveFileDialog Name saveFileDialog
  • Создайте обработчики для кнопок btnOpenFile и btnSaveFile, которые заполните так
  • #region Вкладка OpenFileDialog
        
            String fileName = ""; // Имя текстового файла
            StreamReader readFile; // Дескриптор файла
            private void btnOpenFile_Click(object sender, EventArgs e)
            {
                openFileDialog.FileName = String.Empty; // Очищаем текстовое поле
                DialogResult result = openFileDialog.ShowDialog();
                if (result == DialogResult.OK)
                {
                    try // Попытка
                    {
                        fileName = openFileDialog.FileName;// Сохранить имя файла
                        readFile = new StreamReader(fileName);// Открыть поток
                        txtSimpleEdit.Text = readFile.ReadToEnd();// Читать до конца
                    }
                    catch // Перехват всех исключений
                    {
                        MessageBox.Show("Не могу открыть файл\n",
                            "Ошибка",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Stop);
                    }
            finally // Выполняется в любом случае
                    {
                        readFile.Close();
                    }
                }
            }
        
            StreamWriter writeFile; // Дескриптор файла
            private void btnSaveFile_Click(object sender, EventArgs e)
            {
                // Динамические настройки
                saveFileDialog.Title = "Сохранить текстовый файл";
                saveFileDialog.Filter = "Текстовые файлы (*.txt)|*.txt";
        
                // Если не открывали и не сохраняли, то открыть диалог
                if (fileName.Length <= 0)
                {
                    DialogResult result = saveFileDialog.ShowDialog();
                    if (result == DialogResult.OK)
                    {
                        fileName = saveFileDialog.FileName;// Сохранить имя файла
                    }
                    else
                        return;
                }
        
                try
                {
                    writeFile = new StreamWriter(fileName);// Открыть поток
                    writeFile.Write(txtSimpleEdit.Text);// Сохранить все
                }
                catch
                {
                    MessageBox.Show("Не могу сохранить текст\n",
                        "Ошибка",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Stop);
                }
                finally
                {
                    writeFile.Close();
                }
            }
        
            #endregion

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

  • В начале файла StandardDialogs.cs пропишите инструкцию
  • using System.IO;

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

    Обратите внимание, что мы заключили код для вкладки OpenFileDialog в секционные скобки #region... #endregion для удобства редактирования

  • Постройте приложение и испытайте работу вкладки OpenFileDialog. Внешне она должна выглядеть примерно так
  • Обратите внимание, что после закрытия приложения и повторном его запуске стандартное диалоговое окно OpenFileDialog открывается в каталоге последнего загруженного файла. Это происходит потому, что путь к выбранному в конечном итоге каталогу OpenFileDialog каждый раз автоматически сохраняет в системном реестре.

    Метод ShowDialog() возвращает одно из значений перечисления System.Windows.Forms.DialogResult, которое соответствует выбранной пользователем кнопке в этом диалоговом окне. В зависимости от этого наш код может принимать то или иное решение о дальнейших действиях.

    Разработка вкладки SaveFileDialog

    Кроме свойств, унаследованных от класса FileDialog, класс SaveFileDialog расширяется своими индивидуальными свойствами. Вот они

    Публичные свойства класса SaveFileDialog
    Свойство Тип Пояснения
    CreatePrompt bool Получает или устанавливает флаг, определяющий, должно ли диалоговое окно запрашивать у пользователя разрешение создать файл, если пользователь указал имя файла, который не существует. Значение по умолчанию равно false - не запрашивать, а сразу записывать в новый файл
    OverwritePrompt bool Получает или устанавливает флаг, определяющий, отображать ли предупреждение, если пользователь указал имя файла, который уже существует. Значение по умолчанию равно true - запрашивать подтверждение на перезапись содержимого существующего файла

    При разработке вкладки OpenFileDialog мы в режиме проектирования уже добавили к приложению экземпляр класса SaveFileDialog и оболочка сохранила ссылку на него как поле класса saveFileDialog. В обработчике btnSaveFile_Click() события Click кнопки мы использовали этот экземпляр и динамически настраивали его свойства. В новой вкладке SaveFileDialog мы будем использовать тот же самый объект saveFileDialog, но будем запускать его каждый раз с новыми настройками. Для того, чтобы вернуться к первоначальным настройкам, а затем программно применить новые, каждый раз нужно использовать метод Reset() этого объекта для восстановления настроек по умолчанию.

    Сначала мы оформим интерфейс вкладки изучения настроек диалогового окна SaveFileDialog, а потом определим ее функциональность. На самой вкладке мы определим список флажков, устанавливающих булевы свойства диалогового окна SaveFileDialog, ряд текстовых полей, устанавливающих значения строковых свойств, и кнопку вызова диалогового окна с установленными настройками. Никакого кода записи в выбранные файлы мы писать не будем, а только познакомимся с функциональностями самого диалогового окна.

  • Оформите вкладку SaveFileDialog в соответствии со снимком. Список флажков checkedListBox заполните через коллекцию Items строго по названиям свойств класса FileDialog, поскольку далее они будут участвовать в коде
  • Для всех трех кнопок вкладки создайте обработчики события Click, оберните их секцией #region, чтобы с учетом вспомогательных функций код стал таким
  • #region Вкладка SaveFileDialog
        
            private void btnBrowseFileName_Click(object sender, EventArgs e)
            {
                openFileDialog.CheckFileExists = false;
                openFileDialog.CheckPathExists = false;
                openFileDialog.ShowDialog();
                String str =openFileDialog.FileName;
                txtFileName.Text = str;
                txtInitialDirectory.Text = Path.GetDirectoryName(str);
            }
        
            private void btnShowDialog_Click(object sender, EventArgs e)
            {
                CheckToSaveFileDialog();    // Настраиваем окно
                saveFileDialog.ShowDialog();// Показать окно
                SaveFileDialogToCheck();    // Обновить интерфейс
            }
        
            private void btnReset_Click(object sender, EventArgs e)
            {
                // Сбрасываем состояние диалогового окна
                // в значения по умолчанию 
                saveFileDialog.Reset();
                SaveFileDialogToCheck();
            }
        
            void CheckToSaveFileDialog()
            {
                // Корректируем диапазон значений NumericUpDown
                filterIndex.Minimum = 1;
                filterIndex.Maximum = txtFilter.Text.Split('|').Length / 2;
        
                // Устанавливаем булевы свойства
                saveFileDialog.AddExtension =
                    checkedListBox.GetItemChecked(
                    checkedListBox.Items.IndexOf("AddExtension"));
                saveFileDialog.CheckFileExists =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("CheckFileExists"));
                saveFileDialog.CheckPathExists =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("CheckPathExists"));
                saveFileDialog.DereferenceLinks =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("DereferenceLinks"));
                saveFileDialog.RestoreDirectory =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("RestoreDirectory"));
                saveFileDialog.ShowHelp =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("ShowHelp"));
                saveFileDialog.ValidateNames =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("ValidateNames"));
                saveFileDialog.CreatePrompt =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("CreatePrompt"));
                saveFileDialog.OverwritePrompt =
                        checkedListBox.GetItemChecked(
                        checkedListBox.Items.IndexOf("OverwritePrompt"));
        
                // Устанавливаем свойства из полей интерфейса
                saveFileDialog.DefaultExt = txtDefaultExt.Text;
                saveFileDialog.FileName = txtFileName.Text;
                saveFileDialog.Filter = txtFilter.Text;
                saveFileDialog.FilterIndex = (int)filterIndex.Value;
                saveFileDialog.InitialDirectory = txtInitialDirectory.Text;
                saveFileDialog.Title = txtTitle.Text;
            }
        
            void SaveFileDialogToCheck()
            {
                // Настраиваем список флажков интерфейса
                // в соответствии со свойствами
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("AddExtension"),
                    saveFileDialog.AddExtension);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("CheckFileExists"),
                    saveFileDialog.CheckFileExists);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("CheckPathExists"),
                    saveFileDialog.CheckPathExists);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("DereferenceLinks"),
                    saveFileDialog.DereferenceLinks);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("RestoreDirectory"),
                    saveFileDialog.RestoreDirectory);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("ShowHelp"),
                    saveFileDialog.ShowHelp);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("ValidateNames"),
                    saveFileDialog.ValidateNames);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("CreatePrompt"),
                    saveFileDialog.CreatePrompt);
                checkedListBox.SetItemChecked(
                    checkedListBox.Items.IndexOf("OverwritePrompt"),
                    saveFileDialog.OverwritePrompt);
        
                // Настроиваем те поля интерфейса, которые можно изменить в окне
                // txtDefaultExt.Text = saveFileDialog.DefaultExt;
                txtFileName.Text = saveFileDialog.FileName;
                // txtFilter.Text = saveFileDialog.Filter;
                // filterIndex.Value = saveFileDialog.FilterIndex;
                // txtInitialDirectory.Text = saveFileDialog.InitialDirectory;
                // txtTitle.Text = saveFileDialog.Title;
            }
        
            #endregion
  • Запустите приложение и испытайте работу вкладки SaveFileDialog
  • Разберитесь в коде...
  • Распределение кода по отдельным файлам

    Мы программируем большую задачу по изучению стандартных диалогов и весь код размещаем в двух файлах: StandardDialogs.cs и StandardDialogs.Designer.cs. Для обозримости мы коды для отдельных вкладок упаковали в секции #region. Но у нас осталось еще много диалогов, поэтому для удобства разместим эти секции в отдельных файлах как отдельные части одного класса StandardDialogs.

  • В панели Solution Explorer вызовите для корневого узла проекта контекстное меню и командой Add/New Item добавьте новый пустой файл с именем MessageBox.cs
  • Откройте файл StandardDialogs.cs в режиме View Em, выполните команду меню Edit/Select All и скопируйте все содержимое в новый файл MessageBox.cs
  • В файле MessageBox.cs вызовите для рабочей области контекстное меню и выполните команду Outlining/Collapse to Definitions, чтобы свернуть все секции кода
  • Отредактируйте файл MessageBox.cs так, чтобы в свернутом состоянии он выглядел следующим образом (убрали базовый класс Form и ключевое слово public в объявлении класса)
  • В панели Solution Explorer сделайте две копии теперь уже файла MessageBox.cs и присвойте им имена OpenFileDialog.cs, SaveFileDialog.cs
  • Щелкните правой кнопкой мыши на вкладке StandardDialogs.cs редактора и выполните команду Close All But This, чтобы закрыть все иные ненужные вкладки
  • Выбирайте поочередно в файле StandardDialogs.cs свернутые коды для соответствующих вкладок и перенесите (в новое скопировать, а в старом убрать) в свои одноименные файлы как члены частичного класса, открывая файлы в режиме View Em. В файле StandardDialogs.cs должен остаться только код для вкладки "Обзор"
  • В панели Solution Explorer щелкните правой кнопкой мыши на корневом узле проекта и командой Add/New Folder добавьте новую папку с именем ItemsDialogs
  • Удерживая клавишу Ctrl, выделите файлы MessageBox.cs, OpenFileDialog.cs, SaveFileDialog.cs и мышью переместите их в новую папку
  • Выполните команду меню Window/Close All Documents, чтобы закрыть все редактируемые файлы
  • Запустите приложение и убедитесь, что работоспособность его не изменилась, но код расфасован удобнее
  • Разработка вкладки ColorDialog

    Диалоговое окно ColorDialog порождается классом System.Windows.Forms.ColorDialog. Оно позволяет выбрать цвет, который содержится в свойстве Color типа System.Drawing.Color и может открываться в обычном или расширенном варианте. Несколько других свойств типа Boolean управляют различными настройками диалогового окна ColorDialog. Вот они

    Публичные свойства класса ColorDialog
    Свойство Тип Пояснения
    Color System.Drawing.Color Выбранный пользователем цвет
    AllowFullOpen bool При значении true, установленном по умолчанию, окно готово раскрываеться в расширенном варианте с возможностью выбора любых дополнительных цветов
    AnyColor bool Любой цвет
    FullOpen bool Раскрывается в расширенном варианте
    SolidColorOnly bool Только основные цвета
    ShowHelp bool Отобразить кнопку справки
    CustomColors int[ ] Массив дополнительных цветов

    Булевыми значениями этих публичных свойств мы и будем управлять из вкладки ColorDialog.

  • Поместите на вкладку ColorDialog необходимые элементы управления и настройте их в соответствии с таблицей
  • Свойства элементов вкладки ColorDialog
    Элемент Свойство Значение
    Panel Name panelColorDialog
      Dock Left
      BorderStyle Fixed3D
    Label Name lblColorDialog
      Text Булевы свойства диалога ColorDialog:
      TextAlign MiddleCenter
    CheckBox Name AllowFullOpen
      Text AllowFullOpen
      Checked true
    CheckBox Name AnyColor
      Text AnyColor
    CheckBox Name FullOpen
      Text FullOpen
    CheckBox Name SolidColorOnly
      Text SolidColorOnly
    CheckBox Name ShowHelp
      Text ShowHelp
    Label Name lblColorTitle
      Text Наименование выбранного цвета:
    Label Name lblColorName
      Text DefaultColor
      AutoSize true
    Button Name btnResetColorDialog
      AutoSize true
      Text Reset
    Button Name btnShowColorDialog
      AutoSize true
      Text Show

    В итоге пользовательский интерфейс вкладки должен выглядеть примерно так

  • Из панели Toolbox поместите на форму двойным щелчком компонент ColorDialog и присвойте ему имя colorDialog
  • Создайте для кнопки Show обработчик с именем btnShowColorDialog_Click
  • Создайте для кнопки Reset обработчик с именем btnResetColorDialog_Click
  • Упакуйте обработчики в секцию #region Вкладка ColorDialog...#endregion
  • Через панель Solution Explorer создайте в папке ItemsDialogs копию файла SaveFileDialog.cs и присвойте ей имя ColorDialog.cs
  • Разместите в файле ColorDialog.cs секцию #region с заготовками обработчиков btnShowColorDialog_Click и btnResetColorDialog_Click, автоматически помещенными оболочкой при создании в основную часть класса в файле StandardDialogs.cs, и заполните их кодом. Общее содержимое файла ColorDialog.cs должно быть примерно таким
  • using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Windows.Forms;
    using System.IO;
        
    using System.Reflection;
        
    namespace FormsApp
    {
        partial class StandardDialogs
        {
            #region Вкладка ColorDialog
        
            // Поле для хранения дополнительных цветов
            int[] customColors = new int[16]
                {
                    0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,
                    0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,
                    0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,
                    0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF
                };
        
            private void btnShowColorDialog_Click(object sender, EventArgs e)
            {
                // Создаем совершенно новое диалоговое окно
                ColorDialog dlg = new ColorDialog();
        
                // Настраиваем диалоговое окно
                dlg.CustomColors = customColors;
                foreach (Control ctrl in this.tabPage4.Controls)
                {
                    // Ищем наши флажки на дочерней вкладке
                    if (ctrl is CheckBox)
                    {
                        // Позиционируемся по имени флажка
                        switch (ctrl.Name.ToString())
                        {
                            case "AllowFullOpen":
                                dlg.AllowFullOpen = ((CheckBox)ctrl).Checked;
                                break;
                            case "AnyColor":
                                dlg.AnyColor = ((CheckBox)ctrl).Checked;
                                break;
                            case "FullOpen":
                                dlg.FullOpen = ((CheckBox)ctrl).Checked;
                                break;
                            case "SolidColorOnly":
                                dlg.SolidColorOnly = ((CheckBox)ctrl).Checked;
                                break;
                            case "ShowHelp":
                                dlg.ShowHelp = ((CheckBox)ctrl).Checked;
                                break;
                        }
                    }
                }
        
                // Инициируем предыдущий цвет
                dlg.Color = panelColorDialog.BackColor;
        
                // Показываем диалоговое окно и извлекаем
                // выбранный пользователем цвет 
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    panelColorDialog.BackColor = dlg.Color;
                    lblColorName.Text = dlg.Color.Name;
        
                    // Сохранить выбор дополнительных цветов
                    customColors = dlg.CustomColors;
                }
            }
        
            private void btnResetColorDialog_Click(object sender, EventArgs e)
            {
                // Создаем псевдоним постоянно существующего экземпляра окна
                ColorDialog dlg = colorDialog;
        
                // Восстанавливаем окно
                dlg.Reset();
        
                // Применяем рефлексию для перебора свойств в цикле 
                // и передачи их значений флагам интерфейса
                PropertyInfo[] props = 
                    typeof(ColorDialog).GetProperties();// Получаем все свойства
                // Перебираем свойства 
                foreach (PropertyInfo prop in props)
                {
                    // Выбираем только логические свойства
                    if (prop.PropertyType == typeof(bool))
                    {
                        // Ищем все элементы-флажки с именем свойства
                        // У нас только один флажок с уникальным именем
                        Control[] controls = this.Controls.Find(prop.Name, true);
        
                        // Извлекаем значение свойства и присваиваем флажку
                        ((CheckBox)controls[0]).Checked =
                            (bool)prop.GetGetMethod().Invoke(dlg, null);
                    }
                }
        
                // Восстанавливаем элементы интерфейса
                panelColorDialog.BackColor = dlg.Color;
                lblColorName.Text = dlg.Color.Name;
            }
        
            #endregion
        }
    }

    Свойство CustomColors содержит массив из 16 четырехбайтных целых, в которых содержаться коды дополнительных цветов RGB -модели, представленных в окне 16 плашками. В каждом элементе массива младший байт соответствует цвету Red, затем Green и предпоследний байт - цвету Blue. Последний байт не используется. При создании массива мы сразу инициализировали RGB -байты единицами, что соответствует изначально белому цвету.

    Для демонстрации того, что механизм сохранения дополнительных цветов работает, мы специально в обработчике кнопки Show каждый раз создаем диалоговое окно dlg заново и восстанавливаем в нем сохраненные в массиве customColors дополнительные цвета. Постоянное же диалоговое окно colorDialog мы используем только для сброса пользовательского интерфейса.

    В обработчике btnResetColorDialog_Click() мы используем механизм отражения ( рефлексию ) для динамического анализа свойств порождающего класса диалогового окна и извлечения их значений из существующего объекта. Для этой цели мы подключили пространство имен System.Reflection. Элементы флажков методом Find() мы ищем во всей форме по их именам, совпадающим с именами свойств, и присваиваем им значения динамически извлеченных свойств. Это сделало программу несколько запутанной, но для большого количества элементов такой подход позволил бы значительно сократить объем кода. Кроме того, таким образом мы знакомимся с разными возможностями языка C#.

  • Запустите приложение и испытайте работу вкладки ColorDialog
  • Добавление иконок к формам через файл ресурсов

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

  • В панели Solution Explorer вызовите контекстное меню для узла проекта и командой Add/New Folder создайте новую папку с именем Resources
  • Вызовите контекстное меню для папки Resources и командой Add/New Item добавьте в нее файл ресурсов с именем Resource1.resx
  • Двойным щелчком на файле Resource1.resx вызовите графический редактор ресурсов, раскройте в его верхней части выпадающий список Add Resource и опцией Add Existing File добавьте в каталог ресурсов проекта (и в файл ресурсов) две иконки из прилагаемого к данной работе каталога Source
  • Установите в графическом редакторе ресурсов режим представления View Details и, поочередно выделяя добавленные ресурсы, через панель Properties присвойте им имена Icon1 и Icon2
  • В панели Solution Explorer выделите файлы формы и через контекстное меню откройте их в режиме View Em
  • Добавьте в конструктор каждой формы после вызова функции InitializeComponent() код присоединения соответствующей иконки из файла ресурсов, например, для конструктора Form1() код будет таким
  • public Form1()
            {
                InitializeComponent();
        
                this.Icon = Resources.Resource1.Icon1;
            }
  • Запустите приложение для каждой формы и убедитесь, что иконки присоединяются к окну формы, например
  • Перейдите к следующей части работы (если она уже есть)
  • Вернуться к учебному плану