вторник, 18 сентября 2012 г.

Динамическое создание компонента в WinForms

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

Для демонстрации, я воспользуюсь двумя компонентами, позволяющими вводить строку и сообщать о том, что пользователь ввел бяку.
Для реализации задумки, создадим интерфейс, который будет реализовывать публичный контракт:

public interface IBaseAbstractControl
{
    string Header
    {
        set;
    }

    string InputString
    {
        get;
    }

    void ShowError();
}

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

public partial class FirstControl : UserControl, IBaseAbstractControl
{
    public FirstControl()
    {
        InitializeComponent();
    }

    public string Header
    {
        set { label1.Text = value; }
    }

    public string InputString
    {
        get { return textBox1.Text; }
    }

    public void ShowError()
    {
        MessageBox.Show("Вы ввели неправильные данные");
    }
}

Получилось достаточно просто. Добавляем второй UserControl, вот с таким дизайном (я заполнил Items у ComboBox всякими разными строками):
Также наследуем его от интерфейса и реализуем свойства и метод:

public partial class SecondControl : UserControl, IBaseAbstractControl
{
    public SecondControl()
    {
        InitializeComponent();
    }

    public string Header
    {
        set { label1.Text = value; }
    }

    public string InputString
    {
        get { return comboBox1.Text; }
    }

    public void ShowError()
    {
        comboBox1.BackColor = Color.Red;
        (new Task(() => { Thread.Sleep(1000); this.BeginInvoke((Action)(() => comboBox1.BackColor = Color.White)); })).Start();
    }
}

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

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    IBaseAbstractControl _control = null;

    private void btCreateControl_Click(object sender, EventArgs e)
    {
        switch (comboBox1.SelectedIndex)
        {
            case 0:
                _control = new FirstControl();
                break;
            case 1:
                _control = new SecondControl();
                break;
            default:
                MessageBox.Show("Выбирите способ ввода");
                return;
        }
        btCreateControl.Enabled = false;
        _control.Header = "Введите текст не менее тре символов";
        panel1.Controls.Add((Control)_control);
        btCheckText.Enabled = true;
    }

    private void btCheckText_Click(object sender, EventArgs e)
    {
        if (_control.InputString.Length < 3)
        {
            _control.ShowError();
        }
        else
        {
            MessageBox.Show("Вы ввели текст: " + _control.InputString);
        }
    }
}

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

P.s. Это жесть объяснять дизайн интерфейса в WinForms, без картинки не объяснишь. Толи дело WPF, вставил XAML и все понятно.
 

Комментариев нет:

Отправить комментарий