Опубликован: 25.03.2010 | Доступ: свободный | Студентов: 1447 / 158 | Оценка: 4.31 / 4.00 | Длительность: 25:42:00
Лекция 16:

Динамическая компоновка формы

Большой пример автоматического размещения элементов управления

Для размещения радиокнопок как единой группы применяется элемент управления GroupBox. Но он требует жесткого позиционирования своих дочерних элементов. Составим на основе элемента FlowLayoutPanel расширение, которое будет служить контейнером для автоматического размещения радиокнопок. Затем составим класс диалогового окна, в котором динамически разместим пользовательские элементы управления, включая радиокнопки. И наконец, создадим класс главного окна, вызывающего диалоговое окно и реализующего выбор пользователя.

Вот этот код, который для удобства размещен в нескольких файлах

using System;
using System.Drawing;
using System.Windows.Forms;
    
namespace Test
{
    // Класс расширения стандартной панели размещения
    class GroupPanel : FlowLayoutPanel
    {
        // Поля класса для видимости в методах
        int xDpi, yDpi;
        new public string Text; // Скрываем наследуемое поле 
                                // Надпись в рамке
    
        // Конструктор
        public GroupPanel()
        {
            // Перетекание по столбцам
            this.FlowDirection = FlowDirection.TopDown;
            // Отключаем автоматический переток элементов
            this.WrapContents = false;
            //Включили способность менять размеры
            this.AutoSize = true;
            // Разрешить контейнеру растягиваться и сжиматься
            this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
    
            // Создаем временный контекст устройства
            Graphics gr = this.CreateGraphics();
            // Извлекаем текущее разрешение экрана
            xDpi = (int)gr.DpiX;
            yDpi = (int)gr.DpiY;
            // Освобождаем критичный ресурс
            gr.Dispose();
            // Устанавливаем отступы внутри рамки контейнера
            this.Padding = new Padding(xDpi / 10, yDpi / 10 + 
    Font.Height, xDpi / 10, yDpi / 10);
        }
    
        // Свойство для управления включением кнопки
        public string Check
        {
            // Аксессоры
            set
            {
            // Проверяем, что это радиокнопка и выделяем ее
            // Поиск по имени радиокнопки
            RadioButton radio = this.Controls[value] as RadioButton;
            if (radio != null)
              radio.Checked = true;
            }
            get
            {
                foreach (Control ctrl in this.Controls)
                {
                    // Ищем выделенную радиокнопку и возвращаем имя
                    RadioButton radio = ctrl as RadioButton;
                    if (radio != null && radio.Checked)
                        return radio.Name;
                }
                return "";
            }
        }
    
        // Переопределяем виртуальный обработчик изменения шрифта
        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);// Базовый обработчик
            this.Padding = new Padding(this.Padding.Left,
                yDpi / 10 + this.Font.Height,
                this.Padding.Right, this.Padding.Bottom);
        }
    
        // Переопределяем виртуальный обработчик перерисовки формы
        // Сами перекомпоновываем элементы и перерисовываем результат
        protected override void OnPaint(PaintEventArgs e)
        {
            // Извлекаем контекст устройства
            Graphics gr = e.Graphics;
            // Смещение 
            int yIdent = yDpi / 25 + this.Font.Height / 2;
            int xIdent1 = xDpi / 10;
            int xIdent2;// Пока просто объявили
    
            // Отображаем надпись внутри рамки, если она задана
            if (this.Text != null && this.Text.Length > 0)
            {
                gr.DrawString(
                    " " + this.Text + " ",
                    this.Font,
                    new SolidBrush(this.ForeColor),
                    xIdent1, yDpi / 25
                    );
                xIdent2 = xIdent1 + (int)(gr.MeasureString(
                    " " + this.Text + " ",
                    this.Font).Width) + 4;// Увеличили отступ справа
            }
            else
            {
                xIdent2 = xIdent1;
            }
    
            // Создаем перья стандартного цвета и рисуем рамку
            Pen pnDark = new Pen(ControlPaint.Dark
    (BackColor));// Темный
            Pen pnLight = new Pen(ControlPaint.Light
    (BackColor));// Светлый
            
            // Рисуем темную рамку
            gr.DrawLine(pnDark, xIdent1, yIdent, 0, yIdent);
            gr.DrawLine(pnDark, 0, yIdent, 0, this.Height - 2);
            gr.DrawLine(pnDark, 0, this.Height - 2, 
    this.Width - 2, this.Height - 2);
            gr.DrawLine(pnDark, this.Width - 2, this.Height - 2, 
    this.Width - 2, yIdent);
            gr.DrawLine(pnDark, this.Width - 2, yIdent, 
    xIdent2, yIdent);
    
            // Рисуем светлую рамку
            gr.DrawLine(pnLight, xIdent1, yIdent + 1, 
    1, yIdent + 1);
            gr.DrawLine(pnLight, 1, yIdent + 1, 1, 
    this.Height - 3);
            gr.DrawLine(pnLight, 0, this.Height - 1, 
    this.Width - 1, this.Height - 1);
            gr.DrawLine(pnLight, this.Width - 1, this.Height - 1, 
    this.Width - 1, yIdent);
            gr.DrawLine(pnLight, this.Width - 3, yIdent + 1, 
    xIdent2, yIdent + 1);
        }
    }
}
Листинг 16.9 . Файл GroupPanel.cs

using System;
using System.Drawing;
using System.Windows.Forms;
    
using System.Reflection;// Для динамического определения типов
    
namespace Test
{
    // Заготавливаем диалоговое окно, как класс размещения
    // двух пользовательских компоновочных панелей
    class ColorFillDialog : Form
    {
        // Видимые поля
        protected GroupPanel grPanel1, grPanel2;
        protected CheckBox checkBox;
    
        public ColorFillDialog()
        {
        // Настройка диалогового окна
        // Заголовок диалогового окна
        this.Text = "Выбор цвета элипса";
        // Стиль диалогового окна фиксированных размеров
        this.FormBorderStyle = FormBorderStyle.FixedDialog;
        // Отключаем системные кнопки
        this.ControlBox = false;// Системная пиктограмма
        this.MaximizeBox = this.MinimizeBox = false;// Сворачивание
        this.ShowInTaskbar = false;// Не отображать в панели задач
        // Подстраивать размер диалогового окна формы под содержимое
        this.AutoSize = true;
        // Может растягиваться и сжиматься
        this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
    
        // Создаем внешнюю компоновочную панель
        FlowLayoutPanel flow1 = new FlowLayoutPanel();
        flow1.Parent = this;// Привязали к форме
        flow1.AutoSize = true;// Способность изменять размер
        // Компоновать внутренние элементы по столбцам
        flow1.FlowDirection = FlowDirection.TopDown;
    
        // Создаем внутреннюю компоновочную панель
        FlowLayoutPanel flow2 = new FlowLayoutPanel();
        flow2.Parent = flow1;// Привязали к внешней панели
        flow2.AutoSize = true;//Способность изменять размер
        // Компоновать содержимое по строкам (по умолчанию)
        flow2.FlowDirection = FlowDirection.LeftToRight;
        // Дочерние элементы (выравнивать по высоте
        flow2.Anchor = AnchorStyles.None;
    
        // Размещаем внутри еще две пользовательских (наших) панели
        grPanel1 = new GroupPanel();
        grPanel1.Parent = flow2;
        grPanel1.AutoSize = true;
        grPanel1.Text = "Цвет элипса";// Надпись внутри рамки
    
        grPanel2 = new GroupPanel();
        grPanel2.Parent = flow2;
        grPanel2.AutoSize = true;
        grPanel2.Text = "Фон формы";// Надпись внутри рамки
    
        // Временно замораживаем авторазмещение в панелях
        grPanel1.SuspendLayout();
        grPanel2.SuspendLayout();
    
        // Считываем все общедоступные свойства 
        // библиотечной структуры System.Drawing.Color,
        // применяя технологию отражения
        Type type = typeof(System.Drawing.Color);
        PropertyInfo[] propertiesColor = type.GetProperties();
    
        // Анализируем все свойства и заполняем панели радиокнопками
        foreach (PropertyInfo prop in propertiesColor)
          {
          // Выбираем свойства, которые могут читаться
          // и имеют статические get-аксессоры (а это есть цвета)
          if (prop.CanRead && prop.GetGetMethod().IsStatic)
            {
            // Выбираем цвета с именами на буквы S и P
            if (prop.Name[0] == 'S' || prop.Name[0] == 'P')
              {
              // Создаем радиокнопку
              RadioButton radio = new RadioButton();
              // Добавляем радиокнопку к нужной панели
              radio.Parent = prop.Name[0] == 'S' ? 
      grPanel1 : grPanel2;
              // Включаем способность изменять размер
              radio.AutoSize = true;
              // Назначаем надпись и имя для узнаваемости
              radio.Text = radio.Name = prop.Name;
              }
            }
          }
    
            // Размораживаем авторазмещение в панелях,
            // чтобы элементы могли скомпоноваться
            grPanel1.ResumeLayout();
            grPanel2.ResumeLayout();
    
            // Добавляем флажок
            checkBox = new CheckBox();
            checkBox.Parent = flow1;// Добавили во внешнюю панель
            checkBox.AutoSize = true; // Подстройка под надпись
            checkBox.Text = "Залитый элипс";
            checkBox.Anchor = AnchorStyles.None;// Выравнивать посредине столбца
    
            // Добавляем еще одну компоновочную панель снизу
            FlowLayoutPanel flow3 = new FlowLayoutPanel();
            flow3.Parent = flow1;
            flow3.AutoSize = true;
            flow3.Anchor = AnchorStyles.None;//Выравнивание посредине столюца
    
            // Добавляем в нижнюю компоновочную панель две кнопки
            Button btn = new Button();
            btn.Parent = flow3;
            btn.AutoSize = true;
            btn.Text = "Выполнить";
            // Для распознавания формой
            btn.DialogResult = DialogResult.OK;
            this.AcceptButton = btn;
    
            btn = new Button();
            btn.Parent = flow3;
            btn.AutoSize = true;
            btn.Text = "Отмена";
            // Для распознавания формой
            btn.DialogResult = DialogResult.Cancel;
            this.CancelButton = btn;
        }
    
        // Свойства
        public System.Drawing.Color Color
        {
            get { return System.Drawing.Color.FromName
    (grPanel1.Check); }
            set { grPanel1.Check = value.Name; }
        }
    
        public System.Drawing.Color Background
        {
            get { return System.Drawing.Color.FromName
    (grPanel2.Check); }
            set { grPanel2.Check = value.Name; }
        }
    
        public bool Fill
        {
            get { return checkBox.Checked; }
            set { checkBox.Checked = value; }
        }
    }
}
Листинг 16.10 . Файл ColorFillDialog.cs

using System;
using System.Drawing;
using System.Windows.Forms;
    
namespace Test
{
    // Главная форма приложения
    class MyClass : Form
    {
        // Поля для хранения выбранных состояний диалогового окна
        Color colorEllipse = Color.Salmon;// Цвет элипса
        bool bFillEllipse = false;// Какой элипс рисовать
    
        public MyClass()
        {
        // Заголовок окна
        this.Text = "Цветовая заливка";
        // Включаем возможность перерисовки формы
        // при изменении ее размеров пользователем
        this.ResizeRedraw = true;
        // Цвет формы
        this.BackColor = Color.PowderBlue;
    
        // Создаем меню
        this.Menu = new MainMenu();// Сам объект меню
        this.Menu.MenuItems.Add("Опции");// Верхний уровень меню
        this.Menu.MenuItems[0].MenuItems.Add(
           "Цвет...", MenuColorOnClick);// Опция и обработчик
        }
    
        // Обработчик опции Color
        private void MenuColorOnClick(object sender, EventArgs e)
        {
        // Создаем наше диалоговое окно налету
        ColorFillDialog dlg = new ColorFillDialog();
        dlg.Color = colorEllipse;// Присваиваем значение поля
        dlg.Fill = bFillEllipse;// Присваиваем состояние флажка
        dlg.Background = this.BackColor;// Включаем нужную радиокнопку
    
        // Показываем диалоговое окно и анализируем выбор пользователя
        if (dlg.ShowDialog() == DialogResult.OK)
          {
          // Запоминаем в поле главной формы новый цвет 
          colorEllipse = dlg.Color;
          // Запоминаем состояние флажка
          bFillEllipse = dlg.Fill;
          // Меняем цвет главной формы
          this.BackColor = dlg.Background;
          // Генерируем необходимость перерисовки 
          // главной формы с новыми установками
          this.Invalidate();
          }
        }
    
        // Переопределяем стандартный обработчик события перерисовки формы
        protected override void OnPaint(PaintEventArgs e)
        {
        // Определяем контекст устройства
        Graphics gr = e.Graphics;
        // Строим прямоугольник над формой
        Rectangle rect = new Rectangle(0, 0,
                this.ClientSize.Width - 1, this.ClientSize.Height - 1);
        if (bFillEllipse)// Рисуем залитый элипс на главной форме
          gr.FillEllipse(new SolidBrush(colorEllipse), rect);
        else // Рисуем контур незалитого элипса на главной форме
                gr.DrawEllipse(new Pen(colorEllipse), rect);
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            Application.EnableVisualStyles();
            // Создали форму и передали ее в цикл сообщений Windows
            Application.Run(new MyClass());
        }
    }
}
Листинг 16.11 . Файл Program.cs

Результат выполнения будет таким


Если не использовать свойство Check, то GroupPanel можно использовать для размещения и других элементов управления, например, флажков.

Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

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

 

Как активировать код?