Исходники.Ру - Программирование
Исходники
Статьи
Книги и учебники
Скрипты
Новости RSS
Магазин программиста

Главная » Статьи по программированию » .NET - Windows Forms »

Обсудить на форуме Обсудить на форуме

Объединение нескольких элементов управления в один
статья демонстрирует процесс создания нового элемента управления путем объединения нескольких элементов управления Microsoft Windows Forms; каждый пример разработки элемента управления Windows следует читать вместе с обзорной статьей на смежную тему.

Резюме

Статья демонстрирует процесс создания нового элемента управления путем объединения нескольких элементов управления Microsoft Windows Forms; каждый пример разработки элемента управления Windows следует читать вместе с обзорной статьей на смежную тему. (10 печатных страниц)

Исходный код WinFormControls.exe можно загрузить из MSDN Code Center.

Эта статья является третьей статьей в серии из пяти статей по разработке элементов управления в Microsoft® .NET:

  • Разработка пользовательских элементов управления Windows с помощью Visual Basic .NET (обзор)
  • Добавление проверки правильности регулярного выражения
  • Объединение нескольких элементов управления в один
  • Расширение элемента управления TreeView
  • Рисование собственных элементов управления с помощью GDI+

Содержаниее

Введение

В этой статье рассматриваются две ключевые концепции разработки элементов управления: создание пользовательских элементов управления путем объединения нескольких существующих элементов управления в один и поддержка связывания данных с пользовательским элементом управления. В качестве примера построим элемент управления - кнопку опроса (см. рис. 1), которая не является наиболее часто используемым элементом управления, однако требует объединения нескольких других элементов управления и имеет пользовательское свойство, которое можно связать с источником данных.

Рис. 1. Элемент управления - кнопка опроса позволяет установить группу радио-кнопок и закодировать ее как один элемент.

Вместо пяти индивидуальных радио-кнопок для каждого из вопросов этого опроса эти кнопки можно объединить в один элемент управления, который может использоваться и быть связан с данными как модуль. Для этого необходимо создать элемент управления, который очень похож на "классический" элемент управления Microsoft® ActiveX® для Microsoft Visual Basic 5.0® и Visual Basic 6.0, объединяющий несколько стандартных элементов управления в новый пользовательский элемент управления, и может быть обработан в Microsoft .NET при создании пользовательского элемента управления. Пользовательские элементы управления - это созданные разработчиком элементы управления, наследуемые из класса System.Windows.Forms.UserControl (для получения дополнительной информации см. Документацию Microsoft Visual Studio .NET® по концепциям Visual Basic и Microsoft Visual C#®) и имеющие поверхность для проектирования, очень похожую на Microsoft Windows® Form. Для создания этого элемента управления в Visual Basic .NET или Microsoft Visual C#® .NET используются предоставленные шаблоны, поэтому для добавления нового пользовательского элемента управления к проекту нужно щелкнуть правой кнопкой мыши на проекте, щелкнуть Add и затем Add User Control, как показано на рис. 2.

Примечание.

Рис. 2. Чтобы добавить новый пользовательский элемент управления, щелкните правой кнопкой мыши на проекте в Solution Explorer или используйте команду Project в главном меню.

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

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

Добавление составляющих элементов управления

В соответствии с этими шагами сначала к новому пользовательскому элементу управления были добавлены и расположены соответствующим образом пять радио-кнопок (см. рис. 3). Удалите текст заголовка из всех кнопок и уменьшите их высоту и ширину настолько, насколько возможно, и затем расположите их в строке.

Рис. 3. Расположение радио-кнопок

Примечание. Опции выравнивания элемента управления на панели инструментов размещения значительно облегчают размещение этих элементов управления: выберите их все, щелкните Align tops, чтобы выровнять элементы управления вертикально, и затем уменьшайте/увеличивайте горизонтальный интервал, пока не достигнете требуемого результата.

После настройки размеров элементов управления и их расположения измените имена пяти радио-кнопок на имена rb0 - rb4, которые были выбраны только для сокращения количества ошибок в коде элемента управления. Теперь необходимо удостовериться, что имена элементов управления соответствуют их расположению (rb0-rb1-rb2-rb3-rb4) и порядку переключения при нажатии клавиши Tab (для просмотра текущих значений выберите меню View, щелкните Tab Order, как показано на рис. 4)

Рис. 4. Порядок переключения при нажатии клавиши Tab можно просмотреть и установить визуально в Visual Studio .NET.

Обработка изменения размеров

Для элемента управления с несколькими составляющими элементами управления на фиксированных позициях, подобного рассматриваемому, можно один раз установить требуемые размеры и сделать их изменение недоступным пользователю или автоматически настраивать расположение при изменении размеров элемента управления. В данном случае размер этого элемента управления будет фиксирован. Изменение размеров радио-кнопок не является целью рассмотрения в этой статье, поэтому к данному элементу управления необходимо добавить определенный код, чтобы в форме пользователя изменение размеров стало недоступным. Переопределив метод SetBoundsCore, можно перехватывать все попытки изменения размера или перемещения элемента управления и запретить все изменения, кроме перемещения элемента управления. В переопределенном SetBoundCore информация о положении (x,y) передается без изменений, что позволяет произвольно перемещать элемент управления. Однако, независимо от передаваемой методу информации о высоте и ширине, всегда передается фиксированный размер:

Const fixedWidth As Integer = 120
Const fixedHeight As Integer = 24

Protected Overrides Sub SetBoundsCore(ByVal x As Integer, 
               ByVal y As Integer, _
            ByVal [width] As Integer, ByVal height As Integer, _
            ByVal specified As BoundsSpecified)
    MyBase.SetBoundsCore(x, y, fixedWidth, fixedHeight, specified)
End Sub

Если изменение размеров требуется обрабатывать по-другому, например, корректировать размер и положение составляющих элементов управления, добавьте собственный код к событию Resize своего базового класса (UserControl):

Private Sub SurveyRadioButtons_Resize(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles MyBase.Resize

Что необходимо сделать, чтобы код работал?

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

Const numButtons As Integer = 5

Dim radioButtons(numButtons - 1) As RadioButton

Public Sub New()
    MyBase.New()
    'Этот вызов требуется для Windows Form Designer.
    InitializeComponent()
    'Добавьте любое значение для инициализации после вызова.  InitializeComponent()
    radioButtons(0) = Me.rb0
    radioButtons(1) = Me.rb1
    radioButtons(2) = Me.rb2
    radioButtons(3) = Me.rb3
    radioButtons(4) = Me.rb4
    Me.rb0.Checked = True
End Sub

Затем добавьте свойство для текущей выбранной радио-кнопки:

Dim currentValue As Integer = 1

<System.ComponentModel.Category("Appearance")> _
Public Property CurrentSelection() As Integer
    Get
        Return currentValue
    End Get
    Set(ByVal Value As Integer)
        If Value > 0 And Value <= numButtons Then
            radioButtons(Value - 1).Checked = True
        End If
    End Set
End Property

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

Public Event CurrentSelectionChanged As EventHandler

Const numButtons As Integer = 5
Dim radioButtons(numButtons - 1) As RadioButton
Dim currentValue As Integer = 1

Private Sub rb_CheckedChanged(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
        Handles rb0.CheckedChanged, rb1.CheckedChanged, _
                    rb2.CheckedChanged, rb3.CheckedChanged, _
                    rb4.CheckedChanged
    Dim rb As RadioButton = CType(sender, RadioButton)
    If rb.Checked Then
        Dim i As Integer = 0
        Dim buttonFound As Boolean = False
        Do While (i < numButtons) And Not buttonFound
            If radioButtons(i).Checked Then
                currentValue = i + 1
                buttonFound = True
                RaiseEvent CurrentSelectionChanged(CObj(Me), e)
            End If
            i += 1
        Loop
    End If
End Sub

Этот обработчик событий будет вызываться два раза при изменении значения: во-первых, когда с предварительно выбранной радио-кнопки снимается флажок, и, во-вторых, когда на новой радио-кнопке устанавливается флажок, т.е. чтобы обеспечить однократную обработку события, используется "If rb.Checked". После определения нового значения код генерирует событие, объявляющее об изменении значения.

Последние штрихи

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

Прямоугольник фокуса

Если пользователь выбрал стандартную радио-кнопку, она обозначается при помощи прямоугольника фокуса (пунктирного прямоугольника вокруг элемента управления). Этот прямоугольник рисуется только вокруг текста радио-кнопки и не охватывает весь элемент управления. Поэтому при отсутствии текста радио-кнопки (см. данный элемент управления опроса) видимого индикатора фокуса элемента управления нет. Это неприемлемо, поскольку пользователь не знает, какой элемент управления был выбран до фактического изменения значения кнопки опроса. Поэтому добавьте собственный прямоугольник фокуса, который показан вокруг всего данного пользовательского элемента управления. К счастью, это относительно просто, поскольку в .NET Framework включен специальный класс System.Windows.Forms.ControlPaint, позволяющий выполнять общие задачи по рисованию элемента управления. Этот класс предоставляет различные совместно используемые методы (статические методы в C#), в частности, метод DrawFocusRectangle, рисующий стандартный прямоугольник фокуса Windows в определенном местоположении. С помощью этого метода и закрытого булевского флажка, установленного в событиях GotFocus и LostFocus, можно переопределить метод OnPaint данного родительского класса (UserControl) и добавить прямоугольник фокуса к кнопкам опроса.

Примечание. После ознакомления с доступными свойствами элемента управления вместо переменной hasFocus рекомендуется использовать свойство ContainsFocus (возвращающее значение True, если элемент управления или любой из его составляющих элементов управления находится в фокусе), однако ContainsFocus вызывает исходные Microsoft Win32® API, что немного снижает производительность. Поскольку процедура OnPaint будет вызываться часто, рекомендуется с помощью небольшого дополнительного кода заменить вызовы Win32 API переменной:

Dim hasFocus As Boolean = False
Dim showFocusRect As Boolean = MyBase.ShowFocusCues
Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
    hasFocus = True
End Sub

Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
    hasFocus = False
End Sub

Private Sub SurveyRadioButtons_ChangeUICues(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.UICuesEventArgs) _
        Handles MyBase.ChangeUICues
    showFocusRect = e.ShowFocus
End Sub

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    Dim focusRect As Rectangle = Me.ClientRectangle
    focusRect.Inflate(-2, -2)
    If hasFocus And showFocusRect Then
        'Draw focus rectangle around control
        ControlPaint.DrawFocusRectangle(e.Graphics, _
            focusRect, Me.ForeColor, Me.BackColor)
    Else
        'erase focus rectangle
        e.Graphics.DrawRectangle(New Pen(Me.BackColor, 1), focusRect)
    End If
    MyBase.OnPaint(e)
End Sub

Protected Overrides Sub OnEnter(ByVal e As System.EventArgs)
    Me.Invalidate() 'Invalidate to force redraw of focus rectangle
End Sub

Protected Overrides Sub OnLeave(ByVal e As System.EventArgs)
    Me.Invalidate() 'Invalidate to force redraw of focus rectangle
End Sub

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

Связывание данных

Разработчики, использующие различные версии Visual Basic и Visual Basic .NET, могут связывать элементы пользовательского интерфейса, например, элементы управления, непосредственно с источниками данных (это процесс называется связыванием данных). Существует два типа связывания данных, иногда называемые простым и сложным, которые реализуются по-разному. Простое связывание выполняется на уровне свойства, при этом любое поле из источника данных связывается с одним свойством одного из элементов управления (например, со свойством Text элемента управления TextBox). Сложное связывание не выполняется на уровне свойства; оно служит для связывания всего источника данных с элементом управления. Чтобы понять различие между этими двумя типами связывания, рассмотрим несколько общих примеров для каждого связывания: связывание поля "firstname" таблицы с TextBox, который выводит на экран значение этого поля, является простым связыванием, а связывание полного списка авторов (с несколькими столбцами) с элементом управления DataGrid - это сложное связывание.

Поскольку связывание данных является для разработчиков общим методом, необходимо убедиться, что оно правильно поддерживается любым созданным элементом управления; по крайней мере, простое связывание данных в .NET настолько легко, что ошибиться практически невозможно. Для поддержки простого связывания данных фактически не нужно делать ничего; по умолчанию любое из свойств элемента управления может быть правильно связано с источником данных автоматически. Реальных ограничений на связывание свойств не существует, что позволяет при необходимости связать с данными свойство Tab index элемента управления. Однако на практике имеется лишь несколько свойств, которые целесообразно связывать. Чтобы облегчить работу разработчика, параметры настройки связывания данных в нескольких выбранных свойствах могут быть предоставлены непосредственно в окне Properties (в категории Data в разделе DataBindings). Для связывания любого другого свойства должна быть выбрана опция Advanced. В частности, в элементе управления TextBox свойства Text и Tag предоставлены в качестве общих свойств, связанных с данными. В новом созданном пользовательском элементе управления только свойство Tag может быть связано непосредственно в окне свойств, если не добавлены атрибуты, определяющие те свойства, которые, вероятно, будут связаны с данными. Наиболее вероятно, что в элементе управления SurveyRadioButtons свойство CurrentSelection будет связываться с данными, поэтому добавим к нему атрибут, чтобы оно появилось в разделе Data окна Properties:

<System.ComponentModel.Category("Appearance"), _
  System.ComponentModel.Bindable(True)> _
Public Property CurrentSelection() As Integer
    Get
        Return currentValue
    End Get
    Set(ByVal Value As Integer)
        If Value > 0 And Value < numButtons Then
            radioButtons(Value - 1).Checked = True
        End If
    End Set
End Property

Базовая машина Microsoft Windows Forms может обрабатывать простое связывание данных более эффективно, если у связанного с данными свойства имеется соответствующее событие Changed, называющееся <Property>Changed. При наличии этого события Windows Forms может использовать его, чтобы определить, когда свойство изменится, и обновить в это время основной источник данных. В противном случае источник данных обновляется независимо от изменения свойства. В случае с данным элементом управления событие CurrentSelectionChanged обработает простое связывание данных свойства CurrentSelection наиболее эффективно.

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

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

Резюме

Путем объединения нескольких элементов управления в один пользовательский элемент управления можно создать сложную комбинацию кода и размещения, которая может быть легко упакована и распространена. Этот стиль элемента управления полезен для простых операций, таких как объединение Label и TextBox, или для сложных операций, например, для полного контроля ввода адресов. Независимо от использования этого стиля элемента управления он наиболее близок по форме к модели разработки элементов управления ActiveX для Visual Basic 5.0 и 6.0.

Примечание. Если для определенных потребностей элемент управления SurveyRadioButtons не достаточно гибкий, можно использовать другой элемент управления. Для этого может быть реализован более гибкий элемент управления, в котором можно сконфигурировать число выборов, несмотря на более сложный код и необходимость выбора в коде между отрисовкой собственных радио-кнопок (снова с помощью класса ControlPaint) и динамическим созданием набора обычных радио-кнопок и их добавлением на поверхность элемента управления. Если требуется поддержка переменного числа выборов, рекомендуется использовать сложное связывание данных для определения набора возможных выборов. (Чтобы определить число радио-кнопок, данные можно связать с набором возможных выборов и одновременно связать свойство Value с другим источником данных для сохранения выбранного ответа.)


Может пригодится:


Автор: Дункан Маккензи
Прочитано: 7149
Рейтинг:
Оценить: 1 2 3 4 5

Комментарии: (0)

Добавить комментарий
Ваше имя*:
Ваш email:
URL Вашего сайта:
Ваш комментарий*:
Код безопастности*:

Рассылка новостей
Рейтинги
© 2007, Программирование Исходники.Ру