in3steps.com Articles   Languages   Authors   Affiliate   Search   About us
Создание унаследованных .NET компонентов в C#  •  Функциональность

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

Внутри нашего класса DialogButton создаем публичный метод GetText. Благодаря тому, что в C# любая переменная умеет возвращать свое значение в строковом формате, код получается очень простым, и основную его часть занимает код изменения регистра символов:

public class DialogButton : Button
{
  public virtual string GetText()
  {
    string S = DialogResult.ToString();
    switch (TextCharCase)
    {
      case CharCase.LowerCase:
        S = S.ToLower();
        break;
      case CharCase.UpperCase:
        S = S.ToUpper();
        break;
    }
    return S;
  }
  ...

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

Теперь нам надо сделать свойство, которое будет управлять регистром символов. Это многоэтапная работа. Прежде всего, нам надо будет объявить перечислимый тип для свойства, который будет содержать три варианта - по умолчанию, большие буквы и маленькие буквы. Объявляем новый публичный тип CharCase внутри класса, располагая каждое значение на отдельной строке (верьте мне, потом будет ясно, почему именно на отдельных строках):

public enum CharCase
{
  Default,
  LowerCase,
  UpperCase
}

Теперь создаем приватную переменную textCharCase только что объявленного нами типа для хранения значения свойства:

private CharCase textCharCase = CharCase.Default;

Теперь создаем публичное свойство TextCharCase с соответствующими методами доступа. Кстати, в методе set мы впервые используем заготовленный нами метод GetText:

public CharCase TextCharCase
{
  get { return textCharCase; }
  set
  {
    textCharCase = value;
    Text = GetText();
  }
}

Чтобы избежать сериализации свойства в том случае, если его значение не отличается от CharCase.Default, указываем в атрибуте DefaultValue значение по умолчанию, а для того, чтобы в окне Properties среды Visual Studio при выборе нашего нового свойства отображалась подсказка, в атрибуте Description вводим текст подсказки:

[DefaultValue(typeof(DialogButton.CharCase), "Default"),
Description("Character case of Text property's automatical values")]
public CharCase TextCharCase
...

Со свойством закончили, но наш компонент пока еще не работает так, как нужно. Для того, чтобы было все идеально, нам нужно установить начальное значение текста на кнопке. Лучшее место для этого - конструктор: Кстати, это уже второе место, где мы применили наш метод GetText, так что интуиция нас не обманула:

public DialogButton()
{
  Text = GetText();
}

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

public override DialogResult DialogResult
{
  get { return base.DialogResult; }
  set
  {
    base.DialogResult = value;
    Text = GetText();
  }
}

Кажется, всё... Но постойте, а как же быть с оригинальным свойством Text, унаследованным нашим компонентом от класса Button? Мы изменяем надпись на кнопке автоматически, и присутствие свойства, которое позволяет сделать это вручную, крайне нелогично и нежелательно. Придется убирать. Списком свойств компонентов ведает класс ControlDesigner, который мы и используем. Прежде всего сделаем наш новый внутренний унаследованный класс дизайнера специально для нашего компонента, перекроем там виртуальный метод PostFilterProperties и спокойненько исключим Text из списка свойств:

internal class DialogButtonDesigner : ControlDesigner
{
  protected override void PostFilterProperties(
    System.Collections.IDictionary properties)
  {
    properties.Remove("Text");
    base.PostFilterProperties(properties);
  }
}

Осталось связать наш дизайнер с нашим компонентом. Это делается через атрибут Designer:

[Designer(typeof(DialogButtonDesigner))]
public class DialogButton : Button
{
...

Для того, чтобы все это скопмилировалось и заработало, нужно не забыть добавить сборку SystemDesign в References нашего проекта и using System.Windows.Forms.Design; в файл DialogButton.cs.

С надеждой жмем кнопку F5, чтобы запустить наш проект с полнофукциональной версией кнопки и... получаем ошибку компиляции. Оказывается, среда Visual Studio создала в Form1.Designer.cs строку инициализации свойства Text нашей кнопки, а мы только что удалили это свойство насовсем. Отправляемся в указанное компилятором место и удаляем проблемную строку. С трепетом нажимаем кнопку F5 еще раз и радуемся тому, что наше тестовое приложение с нашей кнопкой наконец-то запустилось и работает именно так, какмы от него ожидали.

С фукциональностью закончили. Самый креативный этап позади, но не расстраивайтесь и не думайте, что впереди нас ждет только рутина. Впереди нас ждет самое сладкое - придание нашему компоненту внешнего лоска. Что и как нам осталось сделать, мы узнаем в последнем шаге.

 • justaman  • 

Начало  •  Предыдущий шаг  •  Следующий шаг  •  Скачать код примера  •  English


See also
Greatis Software
MegaDetailed.NET
Search for more →
 

greatis just4fun network