В один момент возникла задача сделать TabControl по отрисованному дизайну, с вкладками с левой стороны. Сложность была в том, что проект был уже написан с использованием winform, и переделывать его целиком не хотелось. Попытался реализовать эту задачу средствами класического TabControl, но встретился со множеством проблем, связанных с этим.
Первой проблемой стало то, что если спозиционировать вкладки с левой стороны, то мы получаем следующую картину:
Но мне нужно было, чтобы надписи шли гаризонтально. Изучив чуть глубже данный контрол, решил воспользоваться параметром DrawMode=OwnerDrawFixed. Все надписи стерлись, и на кнопке стало возможным писать и рисовать. Но остался фон самой кнопки, который полностью закрасить не удалось.
Следующим шагом поменял Appearance c Normal на Buttons, был еще вариант FlatButtons, но через конструктор его поставить не удалось, а выставление в коде ни на что не повлияло.
В режиме Buttons вылезла такая ерунда:
На картинке видно, что между кнопками и набором TabPages появилось расстояние. Отуда оно взялось и каким параметром регулируется, мне выяснить так и не удалось.
Еще некоторое время я изучал существующие платные и бесплатные библиотеки контролов на наличие возможности изменения под себя вкладок TabControl, но они либо предлагали использовать заранее созданные стили, либо позволяли максимум поменять цвет.
В итоге намучившись с ним, я решил написать свой контрол, взяв за основу стандартный. Целью стало скрыть стандартные вкладки и на смену им поставить свои, завязав их на контрол.
Постараюсь подробно описать все, что для этого пришлось сделать.
Шаг 1
Для начала в проекте нужно создать новый котрол. Для этого в панели Solution Explorer кликаем правой кнопкой по проекту, далее Add->Component, в открывшейся панели вводим имя нового контрола (у меня это NewTabControl)
Шаг 2
После создания открываем код созданного контрола. В коде делаем следующие правки:
дописываем
using System.Windows.Forms;
using System.Drawing;
Создаем три класса, наследуя их от классов стандартных контролов.
Класс нового TabControl
public partial class NewTabControl: System.Windows.Forms.TabControl
Класс нового контрола
public class NewTabPanel: System.Windows.Forms.Panel
Класс одной вкладки
public class PanelTP: System.Windows.Forms.Panel
Теперь нам нужно перезагрузить следующий метод в классе NewTabControl:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x1328 && !DesignMode) m.Result = (IntPtr)1;
else base.WndProc(ref m);
}
Данное действие позволит нам скрыть стандартные вкладки.
Теперь нам нужно связать все классы между собой. Не буду описывать весь код, его я приложу к данной статье. Опишу только самые интересные моменты.
Шаг 3
Компонуем все элементы в классе NewTabPanel:
private void InitializeComponent()
{
this.panel2 = new System.Windows.Forms.Panel(); //Панель с вкладками
this.tabControl = new NewTabControl();
this.Controls.Add(this.tabControl);
this.Controls.Add(this.panel2);
this.Size = new System.Drawing.Size(311, 361);
this.panel2.Dock = System.Windows.Forms.DockStyle.Left;
this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
tabControl.ControlAdded += new ControlEventHandler(tc_ControlAdded); //Событие на создание новой вкладки
tabControl.ControlRemoved += new ControlEventHandler(tc_ControlRemoved); //Событие удаления вкладки
tabControl.Selected += new TabControlEventHandler(tc_Selected); //Событие выделения вкладки
}
Шаг 4
Теперь можно задать формат, как будет выглядеть сама вкладка.
На данном этапе вы можете сами расположить текст, картинку или любой другой элемент на будущей вкладке. А также задать размер и фон самой вкладки.
У себя я вывожу иконку и название вкладки.
В классе PanelTP создаем метод:
private void InitializeComponent()
{
this.Height = 27;
this.Width = 128;
this.BackgroundImage = Tabpanel.Properties.Resources.tab_c_74;
this.Click += new EventHandler(Select_Item);
PictureBox Icon;
Icon = new PictureBox();
Icon.Width = 25;
Icon.Height = 26;
Icon.Left = 3;
Icon.Top = 5;
Icon.Image = Tabpanel.Properties.Resources.green_dialbut_611;
this.Controls.Add(Icon);
Label lname;
lname = new Label();
lname.Width = 95;
lname.Height = 25;
lname.Left = 28;
lname.Top = 5;
lname.Font = new System.Drawing.Font("Times New Roman", 8f, FontStyle.Regular);
lname.Text = this.name;
lname.Click += new EventHandler(Select_Item);
this.Controls.Add(lname);
}
Шаг 5
Не буду описывать методы, обрабатывающие события, они подробно описаны в приложенном проекте. Перейду к применению.
После того как мы все сохранили, на панели Toolbox появятся новые компоненты
Теперь мы можем его разместить в нашей форме как захотим.
Чтобы добавить вкладку используем:
newTabPanel1.TabPages.Add("TabName");
Чтобы удалить:
newTabPanel1.TabPages.Remove(newTabPanel1.TabPages[id])
Где id — это номер вкадки
При такой реализации TabControl вы всегда сможете настроить сортировку между вкладками или скрыть выбранную вкладку, сделать вкладку любого размера и оформления, сделать панель с вкладками соврачиваемой и разворачиваемой.
По такому же принципу вы можете создать любой свой контрол скомпоновав и запрограммировав его из имеющихся.
Возможно, для кого-то я описал очевидные вещи, но надеюсь, найдутся и те, кому данная статья будет полезна.
Исходники проекта можно скачать тут.
Бинарник тут.
Возможно вам также будет интересна моя статья Как подключить сторонний браузер в приложении на C#
Custom controls are specialized controls in .NET derived from Control class. In this article, we explain the step by step process of creating a custom control in C# WinForms application.
Table of Contents
- Custom Control in C# Winforms
-
- Usercontrols
- Inherited controls
- Owner-drawn controls
- C# Custom Button Control
-
- How to Use the Custom Control in C#
- Summary
Custom Control in C# Winforms
We can have the following types of Custom Controls in C# or VB.NET
Usercontrols
Usercotrols are the simpler form of custom controls which are derived from the class
System.Windows.Forms.UserControl. In most cases, UserControls form a compositional approach in which multiple controls are grouped together in a single user interface element.
Example, Login control with username and password text boxes.
Inherited controls
Inherited controls are an extension of an existing control. First you find the preexisting.NET control that most closely matches your desired functionality. Then you inherit that class with extended features such as additional behaviours and properties.
Example of Inherited controls are Cutom button control(explained below) , ExtendedRichTextBox Control, etc.
Owner-drawn controls
Owner-drawn controls normally draw user interfaces from beginning using GDI+ routines. They inherit the System.Windows.Forms.Control class. Because they are built up from scratch the Owner-drawn controls need the most effort to make , but they can offer the most adaptable user interface.
Extender providers
These widgets are a fantastic method of creating an adaptable user interface because they extend the functionality of existing controls on a form.
C# Custom Button Control
In this post, we are going to see how can we creating custom controls. As an example, we will create a custom button control. The example is a Specialized C# button control which changes its look and feels when mouse hovers over it.
If not active the button will appear as shown in the below screenshot.
When you hover mouse over the button(focus on the button) the look and feel of the button changes as below and the cursor type also change to hand type.
Now let’s see the steps involved in creating this special button control.
- Launch Visual Studio and Create a Windows control library project Name it as SpecialButton. By default, it will create a Usercontrol class.Delete the same since we are not creating a user control here.
- Now right click on the project and Select Add New item.Select Custom Control from the list.Name the control as SpecialButton(you can choose whatever name you want).
- Go to source code window and add the following code.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace SpecialButton
{
public partial class SpecialButton : Button
{
public SpecialButton()
{
InitializeComponent();
//Set default property values for the button during startup
SetNormalValues();
}
/// <summary>
/// To Set button properties when not active.i.e when button not in focus.
/// </summary>
private void SetNormalValues()
{
this.Font = new Font("Verdana", 8F, FontStyle.Bold);
this.BackColor = Color.Gray;
this.ForeColor = Color.White;
this.Margin = new Padding(4, 1, 4, 1);
this.Padding = new Padding(4);
this.MinimumSize = new Size(150, 35);
this.Cursor = Cursors.Arrow;
}
/// <summary>
/// Set attributes to highlight button when it is under focus/active.
/// Change the cursor also as Hand type
/// </summary>
private void SetValuesOnFocus()
{
//Increase the font size and colors on focus
this.Font = new Font("Verdana", 10F, FontStyle.Bold);
this.BackColor = Color.Green;
this.ForeColor = Color.White;
//Set the cursor to Hand type
this.Cursor = Cursors.Hand;
}
/// <summary>
/// Default handler.Nothing to do here since we don't need to repaint the button.
/// </summary>
/// <param name="pe"></param>
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
/// <summary>
/// Event handler which call SetValuesOnFocus() method to give apecial
/// effect to button while active
/// </summary>
/// <param name="e"></param>
protected override void OnMouseHover(EventArgs e)
{
base.OnMouseHover(e);
SetValuesOnFocus();
}
/// <summary>
/// Event handler which call SetNormalValues() method to set back the button
/// to normal state
/// </summary>
/// <param name="e"></param>
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
SetNormalValues();
}
}
}
- Build the solution successfully.Your custom control library is ready now. Feel free to customize the code if you want more affect.For example the above code works on mouse events only. Forgetting it work on tab key you need to use events like,
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
SetValuesOnFocus();
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
SetNormalValues();
}
For the time being, we will concentrate on the above-mentioned code with mouse events to understand the basics of creating custom control in C#.
How to Use the Custom Control in C#
- Create a normal windows form project. Reference the above created SpecialButton Control library dll.
- You can see the SpecialButton displayed in the toolbar top left if the project inside the same solution as SpecialButton control library.
- If the control library is an independent solution and Test project is part of another solution then you need to Add the control to the toolbox.For that go to the bottom of ToolBox and Right click. Select Choose Items from the context menu.Now Go to .NET Framework Components tab.Click the Browse button and choose your library(SpecialButton) from the bin folder.
- Now you can see the custom control listed in the bottom of tool box as below.
- Drag & drop the SpecialButton to your form and set the button name and Text.I named the button as btnSpecialButton and gave caption Test as Test Button. In the button click event write the following code to ensure button work as expected.
private void btnSpecialButton_Click(object sender, EventArgs e)
{
MessageBox.Show("C# Custom button click test");
}
Run your WinForm project Test the button.On mouse enter t the button you get the highlighted effect with Hand cursor.
On Click, you get the message box.
On mouse leave the button go back to default state.
Summary
This post covered the steps to create custom controls in C# WinForms.The code logic is same for Creating Custom controls in VB.NET also.Hope this article was helpful for you.If you have any queries or feedback to share about Creating Custom controls in .NET, write it in the comments section below.
Related Searches: Make Custom control in C# and VB.NET, Create custom control in C# and VB.NET
You may also be interested to read Create Usercontrol in C# WinForms applications
Reader Interactions
Сегодня мы немного поговорим о том как создать собственный элемент формы. Базовый набор VStudio достаточно полон, однако красивый интерфейс (GUI) с данным набором компонентов создать врятли получится. Говорить мы будем о создании элемента формы с нуля, а в качестве непосредственного контрола возьмем элемент ListBox (чтобы было на что ориентироваться и описывать логику)…
В первой части мы больше поговорим не о программинге, а о общей технологии создания своего пользовательского элемента управления.
Сразу стоит отметить, что нужно иметь достаточно хорошее представление о языке C# в целом, чтобы писать свой GUI. Ну или по крайней мере понимать принципы ООП. Приступим…
Нужно понимать что каждый элемент управления это класс, а значит это набор методов и свойств. Если мы говорим о таком элементе управления как ListBox (список элементов), то его базовый набор необходимых элементов можно описать так:
1) Непосредственно список элементов;
2) Выбранный элемент;
Маловато, не правда ли? Однако это база. Имея список и возможность помнить выбранный элемент из списка мы получаем, то что нам нужно. Будем отталкиваться что возможностей стандартного элемента нам не достаточно (например там нельзя назначать картинку элементу списка), поэтому дополним следующий набор свойств (забегая вперед отметим, что если мы создаем новый элемент с нуля, то он будет является наследником класса UserControl, что значит заведомую реализацию всех стандартных свойств (Enabled, ForeColor и т.д.)):
3) Цвет текста обычного и выбранного элемента;
4) Цвет фона обычного и выбранного элемента;
По поводу изображения нужно сразу определиться с тем что мы хотим видеть в конечном итоге. Хотим ли мы видеть одну картинку рядом с каждым элементом, хотим ли мы что бы эта картинка менялась на другую при выборе элемента, или же мы хотим видеть разные картинки рядом с каждым элементом списка. В зависимости от этого стоит продумать где эти картинки хранить. В первом и втором случае, изображения логичней хранить как свойства нашего контрола, в третем же случая, логичней хранить для каждого объекта в списке. Остановимся на варианте №3.
Сам процесс создания контрола на форме — это чистой воды рисование с помощью класса Graphics, поэтому умение рисовать объекты программно очень пригодится. Нужно четко представлять что, как и где должно располагаться. В нашем случае особых сложностей нет, нам придется рисовать лишь прямоугольники, текст и готовые изображения.
Ну пожалуй для начала хватит. Общее представление о классе мы имеем и логику его работы примерно представляем. Отдельно хочется остановится лишь на параметре «Список элементов». Что есть список элементов в обычном ListBox — это набор объектов (List<object>). Что мы хотим видеть в нашем списке элементов? Тоже самое + возможность задавать картинку. Отлично, с этим определились, идем дальше.
Примечание: В очередной раз сделаю оговорку, что в данном примере мы рассматриваем создание элемента с нуля. Класс ListBox всего лишь ориентир на то, что мы хотим получить. Это означает что будет код, который в принципе можно было не писать, использовав наследование от нужного класса.
Опишем скелет нашего класса контрола:
public class MyListBox: UserControl { protected override void Dispose(bool disposing) { base.Dispose(disposing); } //Зачем это нужно опишем во второй части private int _SelectedIndex; public int SelectedIndex { get; set; } //Далее все понятно public Color ItemBorderColor { get; set; } public Color SelectedItemColor { get; set; } public Color ItemBackgroundColor { get; set; } public Color SelectedItemBackgroundColor { get; set; } public int ItemBorderWeight { get; set; } public ObjectCollection Items { get; private set; } public object SelectedItem { get { if (this.SelectedIndex < 0) return null; else return this.Items[this.SelectedIndex]; } } }
Шаблон класса есть. Шаблон интуитивно понятен, кроме класса ObjectCollection, которого у нас нет, и который должен отвечать непосредсвено за элементы списка.
Данный класс в себе должен хранить список объектов с изображениями к ним, а так как это public элемент нашего класса GUI, то он должен быть наследником интерфейсов ICollection и IEnumerator. Далее приведу полный листинг данного класса.
public class ObjectCollection : ICollection, IEnumerator { private struct ItemInCollection { public object Obj; public Image Img; public ItemInCollection(object Obj) { this.Obj = Obj; this.Img = null; } public ItemInCollection(object Obj, Image Img) { this.Obj = Obj; this.Img = Img; } public override string ToString() { return this.Obj.ToString(); } } private int Position = -1; public int Count { get { return this._Items.Count; } } private Object _SyncRoot = null; private bool _IsSynchronized = true; public Object SyncRoot { get { return this._SyncRoot; } } public bool IsSynchronized { get { return this._IsSynchronized; } } public object this[int Idx] { get { if (Idx >= this.Count) return null; return this._Items[Idx].Obj; } } public Image Img(int Idx) { if (Idx >= this.Count) return null; return this._Items[Idx].Img; } private List<ItemInCollection> _Items = new List<ItemInCollection>(); public void Add(object Obj) { this._Items.Add(new ItemInCollection(Obj)); } public void Add(object Obj, Image Img) { this._Items.Add(new ItemInCollection(Obj, Img)); } public void Clear() { this._Items.Clear(); } public void CopyTo(Array A, int Idx) { A = new object[this.Count - Idx]; int ii = 0; for (int i = Idx; i < this.Count; ++i) { A.SetValue(this[i], ii++); } } public bool MoveNext() { this.Position++; return (this.Position < this.Count); } public void Reset() { this.Position = -1; } object IEnumerator.Current { get { return this.Current; } } public object Current { get { try { return this[this.Position]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)this; } }
По большей части код не сложный, так же интуитивно понятен. Но разберем мы его во второй части данной темы, которую полностью уделим программной части нашей задачи.
Лабораторные работы си шарп. Создание пользовательского элемента управления
Приложения для Windows forms
Лабораторная работа 17
Выполнить: Разработайте элемент управления «Бегущая строка», который прокручивает заданный текст в TextBox справа налево.
Пример выполнения:
[Название проекта: Lesson_17Lab1
, название файла L17Lab1.cs
]
✍ Выполнение:
-
1. Создайте новый проект.
2. Для создания пользовательского элемента управления выберите в меню Project (Проект) → Добавить пользовательский элемента управления.
Обратите внимание, что у появившегося окна пользовательского элемента нет заголовка и границ, т.к. мы работаем не с формой, а создаем свой элемент управления, который впоследствии будет располагаться на форме другого проекта.
3. В появившемся окне элемента управления добавьте TextBox (txt
) и Timer. Чтобы текстовое окно сливалось с формой, установите его свойства: BackColor
– в значение Control
, BorderStyle
– в значение None
.
4. Обязательно измените имя элемента управления: вместо UserControl1
введите RunningText
(это имя будет затем использовано при создании экземпляров (копий) элемента управления).
5. Далее необходимо запрограммировать событие Resize
(изменение размера) для нашего пользовательского элемента управления (в противном случае при размещении элемента управления на форме ширина текстового окна будет неизменной). Для этого выделите элемент и в окне свойств перейдите на вкладку с событиями . Затем из списка событий выберите Resize
.
6. Событие Resize происходит каждый раз при изменении размеров нашего элемента управления. Для события необходимо написать следующий код, который увеличивает ширину TextBox на всю доступную ширину элемента управления:
private void RunningText_Resize(object sender, EventArgs e) { txt.Top = 0; txt.Left = 0; txt.Width = Width; posX = txt.Width; }
7. Чтобы новый элемент появился на панели инструментов, запустите проект и затем просто закройте форму. На панели инструментов найдите элемент управления.
8. Перейдите на стандартную форму проекта и расположите на ней элемент управления.
9. Теперь необходимо заставить двигаться надпись (текстовое окно) по элементу, чтобы создать эффект движения строки. Для этого следует в глобальной области описать переменную, которая будет содержать текущую координату Х расположения текстового поля по горизонтали, а в событии Timer поместить следующий код:
public partial class RunningText : UserControl { int posX; … private void timer1_Tick(object sender, EventArgs e) { txt.Top = 0; txt.Left = posX; txt.Text = "Привет"; posX = posX - 20; if (posX< 0) { posX = txt.Width; } }
10. Чтобы запустить таймер, установите свойство Enabled
таймера в True
в событии Load пользовательского элемента :
11. Не забудьте задать для переменной PosX
начальное значение. Это необходимо сделать в процедуре события RunningText_Resize:
12. Перейдите на форму и, если Вы все сделали правильно, то в элементе управления RunningText строка действительно будет бегущей! Запустите и отладьте приложение.
13. Теперь необходимо добавить возможность изменять слово в бегущей строке. Для этого добавьте на стандартную форму проекта текстовое окно (txt2
) и кнопку. По нажатию на кнопку слово, введенное в текстовое окно, должно отображаться в бегущей строке.
14. Для того чтобы можно было выводить любой текст на RunningText, добавим описание глобальной переменной Text
в стандартный класс (меню Проект (Project) → Добавить новый элемент → Класс):
internal class Class1 { public string Text = "Привет!"; }
15. Первоначальное значение (значение по умолчанию) для переменной мы задали «Привет!».
16. Теперь создайте метод GetNewString
в пользовательском элементе, который будет получать новую строку в качестве параметра и вставлять ее в текстовое поле:
public partial class RunningText : UserControl { public void GetNewString(string s) { txt.Text = s; } …
17. Теперь необходимо заменить код в событии Tick таймера (вместо txt.Text ="Привет!"
):
18. Перейдите в стандартную форму и запрограммируйте событие Click кнопки:
Text = txt2.Text; runningText1.GetNewString(Text);
19. Запустите проект.
20. Для того чтобы избежать всю эту длительную процедуру с описанием переменной Текст
в отдельном классе, можно было бы добавить к нашему элементу управления настоящее свойство Текст
. Так и поступим.
21. Перейдите в добавленный класс и удалите там строку с объявлением глобальной переменной:
//public string Text = "Привет!";
22. Для задания нового свойства необходимо добавить в код элемента управления создание нового свойства Text
элемента управления:
public string Text { set { txt.Text = value; txt.Text = Text; } get { return txt.Text; } }
Это описание говорит нам о том, что создается свойство Text для объекта runningText. Метод Get
(получение свойства) позволяет нам “узнать” имеющееся значение текстового поля, а метод Set
(установка свойства) позволяет установить значение свойства из внешнего параметра (value
).
23. Необходимо убрать (закомментировать) назначение значения свойства текстового окна и вызов методы в событии Tick следующим образом:
//txt.Text = Text; //GetNewString(Text);
24. А в событие Load элемента управления добавить начальное значение текстового поля:
25. На стандартной форме измените код для события Click кнопки:
private void button1_Click(object sender, EventArgs e) { runningText1.Text= txt2.Text; }
26. Обратите внимание, теперь свойство Text является непосредственно созданным Вами свойством Вашего элемента управления.
Контрольное задание:
Добавьте к созданному элементу управления следующее новое свойство Speed (Скорость) (скорость передвижения текста бегущей строки) и соответственно измените приложение для его тестирования.
Вопросы для самоконтроля:
1. Что такое пользовательский элемент управления?
2. Для чего необходимы процедуры Get
и Set
?
3. Когда выполняется процедура Set
(Get
)?
Среда разработки для пользовательского элемента управления можно улучшить путем создания пользовательских сопоставленный конструктор.
В этом пошаговом руководстве описывается создание пользовательского конструктора для пользовательского элемента управления. Вы реализуете MarqueeControl
тип и связанный класс конструктора, называется MarqueeControlRootDesigner
.
MarqueeControl
Тип реализует экран, подобный бегущую строку, с анимацией огней и мигающим текстом.
Конструктор для этого элемента управления взаимодействует со средой разработки для предоставления пользовательского интерфейса во время разработки. С помощью пользовательского конструктора можно создать пользовательскую MarqueeControl
реализации с анимацией огней и мигающим текстом во многих сочетаниях. Можно использовать полученный элемент управления в форме, как любой другой элемент управления Windows Forms.
В данном пошаговом руководстве представлены следующие задачи.
-
Создание проекта
-
Создание проекта библиотеки элементов управления
-
Ссылки на проект пользовательского элемента управления
-
Определение пользовательского элемента управления и его конструктора
-
Создание экземпляра пользовательского элемента управления
-
Настройка проекта для отладки во время разработки
-
Реализация пользовательского элемента управления
-
Создание дочернего элемента управления для элемента управления
-
Создание MarqueeBorder дочерний элемент управления
-
Создание пользовательского конструктора тени и свойства фильтра
-
Обработка изменений компонентов
-
Добавление команд конструктора в пользовательский конструктор
-
Создание пользовательского редактора UITypeEditor
-
Тестирование элемента управления в конструкторе
Когда вы закончите, пользовательский элемент управления будет выглядеть примерно следующим образом:
Полный пример кода, см. в разделе как: Создание элемента управления Windows Forms, используются преимущества функций разработки.
Note
Отображаемые диалоговые окна и команды меню могут отличаться от описанных в справке в зависимости от текущих параметров или выпуска. Чтобы изменить параметры, выберите в меню Сервис пункт Импорт и экспорт параметров . Дополнительные сведения см. в разделе Персонализация интегрированной среды разработки Visual Studio.
Предварительные требования
Для выполнения данного пошагового руководства требуется:
- Разрешения, необходимые для создания и выполнения проектов приложений Windows Forms на компьютере, на котором установлена Visual Studio.
Создание проекта
Первым шагом является создание проекта приложения. Этот проект будет использован для построения приложения, на котором размещается пользовательский элемент управления.
Создание проекта
- Создайте проект приложения Windows Forms с именем «MarqueeControlTest» (файл > New > проекта > Visual C# или Visual Basic > классический рабочий стол > Windows Forms Application).
Создание проекта библиотеки элементов управления
Следующим шагом является создание проекта библиотеки элементов управления. Вы создадите новый пользовательский элемент управления и его соответствующего конструктора.
Чтобы создать проект библиотеки элементов управления
-
Добавьте проект библиотеки элементов управления Windows Forms в решение. Назовите проект «MarqueeControlLibrary.»
-
С помощью обозревателе решений, удалите элемент управления проекта по умолчанию, удаляя исходный файл с именем «UserControl1.cs» или «UserControl1.vb» в зависимости от выбранного языка. Дополнительные сведения см. в разделе Как Удалить, удаление и исключить элементы.
-
Добавьте новый UserControl элемент
MarqueeControlLibrary
проекта. Предоставить новый исходный файл базовым именем «MarqueeControl.» -
С помощью обозревателе решений, создайте новую папку в
MarqueeControlLibrary
проекта. Дополнительные сведения см. в разделе Как Добавление новых элементов проекта. Назовите новую папку «Design». -
Щелкните правой кнопкой мыши разработки папку и добавьте новый класс. Присвойте файлу источника, базовым именем «MarqueeControlRootDesigner.»
-
Необходимо использовать типы из сборки System.Design, поэтому добавьте этот справочник, чтобы
MarqueeControlLibrary
проекта.Note
Чтобы использовать сборки System.Design, ваш проект должен быть предназначен для полной версии платформы .NET Framework, а не клиентский профиль .NET Framework. Чтобы изменить целевую платформу, см. в разделе как: определить целевую версию .NET Framework.
Ссылки на проект пользовательского элемента управления
Вы воспользуетесь MarqueeControlTest
проекта для тестирования пользовательского элемента управления. Тестовый проект узнает об пользовательского элемента управления при добавлении в проект ссылку на MarqueeControlLibrary
сборки.
Для ссылки на проект пользовательского элемента управления
- В
MarqueeControlTest
, добавьте в проект ссылку наMarqueeControlLibrary
сборки. Обязательно используйте проекты вкладке добавить ссылку диалоговое окно, вместо ссылки наMarqueeControlLibrary
сборки напрямую.
Определение пользовательского элемента управления и его конструктора
Пользовательский элемент управления, производной от UserControl класса. Благодаря этому ваш элемент управления для размещения других элементов управления, и он предоставляет немало функций по умолчанию элемент управления.
Пользовательский элемент управления будет иметь сопоставленный пользовательский конструктор. Это позволяет создать уникальный интерфейс разработки, предназначенная специально для пользовательского элемента управления.
Связывание элемента управления с конструктором посредством DesignerAttribute класса. Поскольку вы разрабатываете всей поведение времени разработки пользовательского элемента управления, пользовательский конструктор будет реализовывать IRootDesigner интерфейс.
Для определения пользовательского элемента управления и его конструктора
-
Откройте
MarqueeControl
исходный файл в редактор кода. В верхней части файла импортируйте следующие пространства имен:using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.Design;
-
Добавить DesignerAttribute для
MarqueeControl
объявление класса. Это связывает пользовательский элемент управления с помощью его конструктора.[Designer( typeof( MarqueeControlLibrary.Design.MarqueeControlRootDesigner ), typeof( IRootDesigner ) )] public class MarqueeControl : UserControl {
-
Откройте
MarqueeControlRootDesigner
исходный файл в редактор кода. В верхней части файла импортируйте следующие пространства имен:using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System.Drawing.Design; using System.Windows.Forms; using System.Windows.Forms.Design;
-
Измените объявление
MarqueeControlRootDesigner
наследование DocumentDesigner класса. Применить ToolboxItemFilterAttribute для указания взаимодействия конструктора с элементов.Примечание определение
MarqueeControlRootDesigner
класса заключается в пространство имен с именем «MarqueeControlLibrary.Design.» Это объявление помещает конструктор в специальном пространстве имен зарезервированы для типов, связанных с проектированием.namespace MarqueeControlLibrary.Design { [ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", ToolboxItemFilterType.Require)] [ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)] [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] public class MarqueeControlRootDesigner : DocumentDesigner {
-
Определение конструктора для
MarqueeControlRootDesigner
класса. Вставить WriteLine инструкции в теле конструктора. Это будет полезно для отладки.public MarqueeControlRootDesigner() { Trace.WriteLine("MarqueeControlRootDesigner ctor"); }
Создание экземпляра пользовательского элемента управления
Чтобы посмотреть, пользовательское поведение времени разработки элемента управления, поместите экземпляр элемента управления в форме в MarqueeControlTest
проекта.
Для создания экземпляра пользовательского элемента управления
-
Добавьте новый UserControl элемент
MarqueeControlTest
проекта. Предоставить новый исходный файл базовым именем «DemoMarqueeControl.» -
Откройте
DemoMarqueeControl
файл редактор кода. В верхней части файла, импортироватьMarqueeControlLibrary
пространство имен:
Imports MarqueeControlLibrary
using MarqueeControlLibrary;
-
Измените объявление
DemoMarqueeControl
наследованиеMarqueeControl
класса. -
Выполните построение проекта.
-
Откройте
Form1
в конструкторе Windows Forms. -
Найти компоненты MarqueeControlTest вкладке элементов и откройте его. Перетащите
DemoMarqueeControl
из элементов на форму. -
Выполните построение проекта.
Настройка проекта для отладки во время разработки
При разработке пользовательского взаимодействия во время разработки, его будет необходимо отлаживать элементы управления и компоненты. Есть простой способ настроить проект для отладки во время разработки. Дополнительные сведения см. в разделе Пошаговое руководство: Отладка пользовательских Windows Forms элементы управления во время разработки.
Чтобы настроить проект для отладки во время разработки
-
Щелкните правой кнопкой мыши
MarqueeControlLibrary
проекта и выберите свойства. -
В диалоговом окне «Страницы свойств MarqueeControlLibrary» выберите Отладка страницы.
-
В действие при запуске выберите запуск внешней программы. Вы будете отладка отдельного экземпляра Visual Studio, поэтому нажмите кнопку с многоточием () чтобы перейти в интегрированной среде разработки Visual Studio. Имя исполняемого файла — devenv.exe, и если вы установили в расположение по умолчанию, его путь — 9.0\Common7\IDE\devenv.exe %programfiles%\Microsoft Visual Studio.
-
Нажмите кнопку ОК, чтобы закрыть диалоговое окно.
-
Щелкните правой кнопкой мыши
MarqueeControlLibrary
проекта и выберите «Назначить запускаемым проектом», чтобы включить конфигурацию отладки.
Контрольная точка
Теперь вы готовы отладки поведения времени разработки пользовательского элемента управления. Выяснив, что среда отладки установлено правильно, вы проверите связь между пользовательского элемента управления и конструктором.
Для тестирования среды отладки и связи конструктора
-
Откройте
MarqueeControlRootDesigner
исходный файл в редактор кода и поместите точку останова на WriteLine инструкции. -
Нажмите клавишу F5, чтобы запустить сеанс отладки. Обратите внимание на то, что создается новый экземпляр Visual Studio.
-
В новом экземпляре Visual Studio откройте решение «MarqueeControlTest». Решение можно легко найти, выбрав последние проекты из файл меню. Файл решения «MarqueeControlTest.sln» отображаются как последних использовавшихся файлов.
-
Откройте
DemoMarqueeControl
в конструкторе. Обратите внимание, что отладочный экземпляр Visual Studio получает фокус, и выполнение остановится в точке останова. Нажмите клавишу F5, чтобы продолжить сеанс отладки.
На этом этапе все, что находится в месте, для разработки и отладки пользовательского элемента управления и его связанные конструктора. В оставшейся части этого пошагового руководства будет сосредоточиться на деталях реализации таких возможностей элемента управления и конструктора.
Реализация пользовательского элемента управления
MarqueeControl
Является UserControl несложной настройки. Он предоставляет два метода: Start
, которая запускает анимацию бегущей строки, и Stop
, который останавливает анимацию. Так как MarqueeControl
содержит дочерними элементами, реализующими IMarqueeWidget
интерфейс, Start
и Stop
перечисление каждого дочернего элемента управления и вызов StartMarquee
и StopMarquee
методы, соответственно, на все дочерние элементы управления реализующий IMarqueeWidget
.
Внешний вид MarqueeBorder
и MarqueeText
элементов управления в зависимости от структуры, поэтому MarqueeControl
переопределяет OnLayout метода и вызывает PerformLayout на дочерние элементы управления этого типа.
Это объем MarqueeControl
настроек. Во время выполнения функции обеспечиваются MarqueeBorder
и MarqueeText
элементы управления и функции разработки реализуются MarqueeBorderDesigner
и MarqueeControlRootDesigner
классы.
Для реализации пользовательского элемента управления
-
Откройте
MarqueeControl
исходный файл в редактор кода. РеализуйтеStart
иStop
методы.public void Start() { // The MarqueeControl may contain any number of // controls that implement IMarqueeWidget, so // find each IMarqueeWidget child and call its // StartMarquee method. foreach( Control cntrl in this.Controls ) { if( cntrl is IMarqueeWidget ) { IMarqueeWidget widget = cntrl as IMarqueeWidget; widget.StartMarquee(); } } } public void Stop() { // The MarqueeControl may contain any number of // controls that implement IMarqueeWidget, so find // each IMarqueeWidget child and call its StopMarquee // method. foreach( Control cntrl in this.Controls ) { if( cntrl is IMarqueeWidget ) { IMarqueeWidget widget = cntrl as IMarqueeWidget; widget.StopMarquee(); } } }
-
Переопределите метод OnLayout .
protected override void OnLayout(LayoutEventArgs levent) { base.OnLayout (levent); // Repaint all IMarqueeWidget children if the layout // has changed. foreach( Control cntrl in this.Controls ) { if( cntrl is IMarqueeWidget ) { Control control = cntrl as Control; control.PerformLayout(); } } }
Создание дочернего элемента управления для элемента управления
MarqueeControl
Будет размещаться два вида дочернего элемента управления: MarqueeBorder
управления и MarqueeText
элемента управления.
-
MarqueeBorder
: Этот элемент управления рисует «lights» вокруг его границ. Индикаторы flash в последовательности, чтобы они отображались движутся вокруг границы. Скорость, с которой свет flash управляется свойствомUpdatePeriod
. Несколько других пользовательских свойств определяют другие характеристики внешнего вида элемента управления. Два метода,StartMarquee
иStopMarquee
, контролировать, когда анимация начинается и останавливается. -
MarqueeText
: Этот элемент управления рисует мигающую строку. Как иMarqueeBorder
элемента управления, скорость мигания текст управляетсяUpdatePeriod
свойство.MarqueeText
Элемент управления также имеетStartMarquee
иStopMarquee
методы общих сMarqueeBorder
элемента управления.
Во время разработки MarqueeControlRootDesigner
позволяет эти типы двух элементов управления для добавления MarqueeControl
в любом сочетании.
Общие возможности двух элементов управления представлены в интерфейсе IMarqueeWidget
. Это позволяет MarqueeControl
определять все дочерние элементы управления и обрабатывать их особым образом.
Для реализации анимации, будет использоваться BackgroundWorker объектов из System.ComponentModel пространства имен. Можно использовать Timer объектов, но когда множество IMarqueeWidget
присутствуют объекты, возможно, единственный UI-поток может справиться с анимацией.
Для создания дочернего элемента управления для элемента управления
-
Добавить новый элемент класса для
MarqueeControlLibrary
проекта. Предоставить новый исходный файл базовым именем «IMarqueeWidget.» -
Откройте
IMarqueeWidget
исходный файл в редактор кода и замените объявление сclass
дляinterface
:// This interface defines the contract for any class that is to // be used in constructing a MarqueeControl. public interface IMarqueeWidget {
-
Добавьте следующий код, чтобы
IMarqueeWidget
интерфейс для предоставления двух методов и свойства, анимацией бегущей строки:// This interface defines the contract for any class that is to // be used in constructing a MarqueeControl. public interface IMarqueeWidget { // This method starts the animation. If the control can // contain other classes that implement IMarqueeWidget as // children, the control should call StartMarquee on all // its IMarqueeWidget child controls. void StartMarquee(); // This method stops the animation. If the control can // contain other classes that implement IMarqueeWidget as // children, the control should call StopMarquee on all // its IMarqueeWidget child controls. void StopMarquee(); // This method specifies the refresh rate for the animation, // in milliseconds. int UpdatePeriod { get; set; } }
-
Добавьте новый пользовательский элемент управления элемент
MarqueeControlLibrary
проекта. Предоставить новый исходный файл базовым именем «MarqueeText.» -
Перетащите BackgroundWorker компонент из элементов на вашей
MarqueeText
элемента управления. Этот компонент позволитMarqueeText
обновление асинхронно элемента управления. -
В окне «Свойства» задайте BackgroundWorker компонента
WorkerReportsProgress
и WorkerSupportsCancellation свойстваtrue
. Эти параметры позволяют BackgroundWorker компонент периодически вызывать ProgressChanged событий и отменить асинхронное обновление. Дополнительные сведения см. в разделе компонента BackgroundWorker. -
Откройте
MarqueeText
исходный файл в редактор кода. В верхней части файла импортируйте следующие пространства имен:using System; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System.Drawing; using System.Threading; using System.Windows.Forms; using System.Windows.Forms.Design;
-
Измените объявление
MarqueeText
наследование Label и реализоватьIMarqueeWidget
интерфейса:[ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)] public partial class MarqueeText : Label, IMarqueeWidget {
-
Объявите переменные экземпляра, соответствующие этим свойствам и инициализируйте их в конструкторе.
isLit
Поля определяет, является ли текст для рисования с помощью цветаLightColor
свойство.// When isLit is true, the text is painted in the light color; // When isLit is false, the text is painted in the dark color. // This value changes whenever the BackgroundWorker component // raises the ProgressChanged event. private bool isLit = true; // These fields back the public properties. private int updatePeriodValue = 50; private Color lightColorValue; private Color darkColorValue; // These brushes are used to paint the light and dark // colors of the text. private Brush lightBrush; private Brush darkBrush; // This component updates the control asynchronously. private BackgroundWorker backgroundWorker1; public MarqueeText() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); // Initialize light and dark colors // to the control's default values. this.lightColorValue = this.ForeColor; this.darkColorValue = this.BackColor; this.lightBrush = new SolidBrush(this.lightColorValue); this.darkBrush = new SolidBrush(this.darkColorValue); }
-
Реализовать интерфейс
IMarqueeWidget
.StartMarquee
ИStopMarquee
методы вызывают BackgroundWorker компонента RunWorkerAsync и CancelAsync методы для запуска и остановки анимации.Category И Browsable атрибуты применяются к
UpdatePeriod
свойство, поэтому она отображается в пользовательском разделе окна свойств, под названием «Область».public virtual void StartMarquee() { // Start the updating thread and pass it the UpdatePeriod. this.backgroundWorker1.RunWorkerAsync(this.UpdatePeriod); } public virtual void StopMarquee() { // Stop the updating thread. this.backgroundWorker1.CancelAsync(); } [Category("Marquee")] [Browsable(true)] public int UpdatePeriod { get { return this.updatePeriodValue; } set { if (value > 0) { this.updatePeriodValue = value; } else { throw new ArgumentOutOfRangeException("UpdatePeriod", "must be > 0"); } } }
-
Реализация методов доступа свойства. Будет предоставлять два свойства для клиентов:
LightColor
иDarkColor
. Category И Browsable атрибуты применяются к этим свойствам, поэтому они отображаются в пользовательском разделе окна свойств, под названием «Область».[Category("Marquee")] [Browsable(true)] public Color LightColor { get { return this.lightColorValue; } set { // The LightColor property is only changed if the // client provides a different value. Comparing values // from the ToArgb method is the recommended test for // equality between Color structs. if (this.lightColorValue.ToArgb() != value.ToArgb()) { this.lightColorValue = value; this.lightBrush = new SolidBrush(value); } } } [Category("Marquee")] [Browsable(true)] public Color DarkColor { get { return this.darkColorValue; } set { // The DarkColor property is only changed if the // client provides a different value. Comparing values // from the ToArgb method is the recommended test for // equality between Color structs. if (this.darkColorValue.ToArgb() != value.ToArgb()) { this.darkColorValue = value; this.darkBrush = new SolidBrush(value); } } }
-
Реализуйте обработчики для BackgroundWorker компонента DoWork и ProgressChanged события.
DoWork Обработчик событий бездействует в течение указанного числа миллисекунд
UpdatePeriod
затем вызывает ProgressChanged до момента, когда код останавливает анимацию путем вызова CancelAsync.ProgressChanged Обработчик событий Выбор текста состоянии светлые и темные, чтобы дать внешний вид мигать.
// This method is called in the worker thread's context, // so it must not make any calls into the MarqueeText control. // Instead, it communicates to the control using the // ProgressChanged event. // // The only work done in this event handler is // to sleep for the number of milliseconds specified // by UpdatePeriod, then raise the ProgressChanged event. private void backgroundWorker1_DoWork( object sender, System.ComponentModel.DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; // This event handler will run until the client cancels // the background task by calling CancelAsync. while (!worker.CancellationPending) { // The Argument property of the DoWorkEventArgs // object holds the value of UpdatePeriod, which // was passed as the argument to the RunWorkerAsync // method. Thread.Sleep((int)e.Argument); // The DoWork eventhandler does not actually report // progress; the ReportProgress event is used to // periodically alert the control to update its state. worker.ReportProgress(0); } } // The ProgressChanged event is raised by the DoWork method. // This event handler does work that is internal to the // control. In this case, the text is toggled between its // light and dark state, and the control is told to // repaint itself. private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) { this.isLit = !this.isLit; this.Refresh(); }
-
Переопределить OnPaint методу для включения анимации.
protected override void OnPaint(PaintEventArgs e) { // The text is painted in the light or dark color, // depending on the current value of isLit. this.ForeColor = this.isLit ? this.lightColorValue : this.darkColorValue; base.OnPaint(e); }
-
Нажмите клавишу F6 для построения решения.
Создание MarqueeBorder дочерний элемент управления
MarqueeBorder
Элемент управления является немного более сложны, чем MarqueeText
элемента управления. Он имеет дополнительные свойства и анимации в OnPaint несколько сложнее. В принципе, это довольно похоже на MarqueeText
элемента управления.
Так как MarqueeBorder
элемент управления может иметь дочерние элементы управления, его необходимо учитывать Layout события.
Создание элемента управления MarqueeBorder
-
Добавьте новый пользовательский элемент управления элемент
MarqueeControlLibrary
проекта. Предоставить новый исходный файл базовым именем «MarqueeBorder.» -
Перетащите BackgroundWorker компонент из элементов на вашей
MarqueeBorder
элемента управления. Этот компонент позволитMarqueeBorder
обновление асинхронно элемента управления. -
В окне «Свойства» задайте BackgroundWorker компонента
WorkerReportsProgress
и WorkerSupportsCancellation свойстваtrue
. Эти параметры позволяют BackgroundWorker компонент периодически вызывать ProgressChanged событий и отменить асинхронное обновление. Дополнительные сведения см. в разделе компонента BackgroundWorker. -
В окне «Свойства» нажмите кнопку события. Присоединять обработчики для DoWork и ProgressChanged события.
-
Откройте
MarqueeBorder
исходный файл в редактор кода. В верхней части файла импортируйте следующие пространства имен:using System; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System.Drawing; using System.Drawing.Design; using System.Threading; using System.Windows.Forms; using System.Windows.Forms.Design;
-
Измените объявление
MarqueeBorder
наследование Panel и реализоватьIMarqueeWidget
интерфейс.[Designer(typeof(MarqueeControlLibrary.Design.MarqueeBorderDesigner ))] [ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", ToolboxItemFilterType.Require)] public partial class MarqueeBorder : Panel, IMarqueeWidget {
-
Объявите два перечисления для управления
MarqueeBorder
состояние элемента управления:MarqueeSpinDirection
, который определяет направление, в которой свет «вращение» границы, иMarqueeLightShape
, который определяет форму индикаторы (квадратные или циклических). Поместите эти объявления доMarqueeBorder
объявление класса.// This defines the possible values for the MarqueeBorder // control's SpinDirection property. public enum MarqueeSpinDirection { CW, CCW } // This defines the possible values for the MarqueeBorder // control's LightShape property. public enum MarqueeLightShape { Square, Circle }
-
Объявите переменные экземпляра, соответствующие этим свойствам и инициализируйте их в конструкторе.
public static int MaxLightSize = 10; // These fields back the public properties. private int updatePeriodValue = 50; private int lightSizeValue = 5; private int lightPeriodValue = 3; private int lightSpacingValue = 1; private Color lightColorValue; private Color darkColorValue; private MarqueeSpinDirection spinDirectionValue = MarqueeSpinDirection.CW; private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square; // These brushes are used to paint the light and dark // colors of the marquee lights. private Brush lightBrush; private Brush darkBrush; // This field tracks the progress of the "first" light as it // "travels" around the marquee border. private int currentOffset = 0; // This component updates the control asynchronously. private System.ComponentModel.BackgroundWorker backgroundWorker1; public MarqueeBorder() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); // Initialize light and dark colors // to the control's default values. this.lightColorValue = this.ForeColor; this.darkColorValue = this.BackColor; this.lightBrush = new SolidBrush(this.lightColorValue); this.darkBrush = new SolidBrush(this.darkColorValue); // The MarqueeBorder control manages its own padding, // because it requires that any contained controls do // not overlap any of the marquee lights. int pad = 2 * (this.lightSizeValue + this.lightSpacingValue); this.Padding = new Padding(pad, pad, pad, pad); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); }
-
Реализовать интерфейс
IMarqueeWidget
.StartMarquee
ИStopMarquee
методы вызывают BackgroundWorker компонента RunWorkerAsync и CancelAsync методы для запуска и остановки анимации.Так как
MarqueeBorder
элемент управления может содержать дочерние элементы управления,StartMarquee
метод перечисляет все дочерние элементы управления и вызовыStartMarquee
на те, которые реализуютIMarqueeWidget
.StopMarquee
Метод имеется похожая реализация.public virtual void StartMarquee() { // The MarqueeBorder control may contain any number of // controls that implement IMarqueeWidget, so find // each IMarqueeWidget child and call its StartMarquee // method. foreach (Control cntrl in this.Controls) { if (cntrl is IMarqueeWidget) { IMarqueeWidget widget = cntrl as IMarqueeWidget; widget.StartMarquee(); } } // Start the updating thread and pass it the UpdatePeriod. this.backgroundWorker1.RunWorkerAsync(this.UpdatePeriod); } public virtual void StopMarquee() { // The MarqueeBorder control may contain any number of // controls that implement IMarqueeWidget, so find // each IMarqueeWidget child and call its StopMarquee // method. foreach (Control cntrl in this.Controls) { if (cntrl is IMarqueeWidget) { IMarqueeWidget widget = cntrl as IMarqueeWidget; widget.StopMarquee(); } } // Stop the updating thread. this.backgroundWorker1.CancelAsync(); } [Category("Marquee")] [Browsable(true)] public virtual int UpdatePeriod { get { return this.updatePeriodValue; } set { if (value > 0) { this.updatePeriodValue = value; } else { throw new ArgumentOutOfRangeException("UpdatePeriod", "must be > 0"); } } }
-
Реализация методов доступа свойства.
MarqueeBorder
Элемент управления имеет несколько свойств, внешний вид.[Category("Marquee")] [Browsable(true)] public int LightSize { get { return this.lightSizeValue; } set { if (value > 0 && value <= MaxLightSize) { this.lightSizeValue = value; this.DockPadding.All = 2 * value; } else { throw new ArgumentOutOfRangeException("LightSize", "must be > 0 and < MaxLightSize"); } } } [Category("Marquee")] [Browsable(true)] public int LightPeriod { get { return this.lightPeriodValue; } set { if (value > 0) { this.lightPeriodValue = value; } else { throw new ArgumentOutOfRangeException("LightPeriod", "must be > 0 "); } } } [Category("Marquee")] [Browsable(true)] public Color LightColor { get { return this.lightColorValue; } set { // The LightColor property is only changed if the // client provides a different value. Comparing values // from the ToArgb method is the recommended test for // equality between Color structs. if (this.lightColorValue.ToArgb() != value.ToArgb()) { this.lightColorValue = value; this.lightBrush = new SolidBrush(value); } } } [Category("Marquee")] [Browsable(true)] public Color DarkColor { get { return this.darkColorValue; } set { // The DarkColor property is only changed if the // client provides a different value. Comparing values // from the ToArgb method is the recommended test for // equality between Color structs. if (this.darkColorValue.ToArgb() != value.ToArgb()) { this.darkColorValue = value; this.darkBrush = new SolidBrush(value); } } } [Category("Marquee")] [Browsable(true)] public int LightSpacing { get { return this.lightSpacingValue; } set { if (value >= 0) { this.lightSpacingValue = value; } else { throw new ArgumentOutOfRangeException("LightSpacing", "must be >= 0"); } } } [Category("Marquee")] [Browsable(true)] [EditorAttribute(typeof(LightShapeEditor), typeof(System.Drawing.Design.UITypeEditor))] public MarqueeLightShape LightShape { get { return this.lightShapeValue; } set { this.lightShapeValue = value; } } [Category("Marquee")] [Browsable(true)] public MarqueeSpinDirection SpinDirection { get { return this.spinDirectionValue; } set { this.spinDirectionValue = value; } }
-
Реализуйте обработчики для BackgroundWorker компонента DoWork и ProgressChanged события.
DoWork Обработчик событий бездействует в течение указанного числа миллисекунд
UpdatePeriod
затем вызывает ProgressChanged до момента, когда код останавливает анимацию путем вызова CancelAsync.ProgressChanged Обработчик событий увеличивает позицию свет «базовый», из которого определяется состояние света и тени другие источники света, и вызовы Refresh метод для перевода элемента управления окрашивание.
// This method is called in the worker thread's context, // so it must not make any calls into the MarqueeBorder // control. Instead, it communicates to the control using // the ProgressChanged event. // // The only work done in this event handler is // to sleep for the number of milliseconds specified // by UpdatePeriod, then raise the ProgressChanged event. private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; // This event handler will run until the client cancels // the background task by calling CancelAsync. while (!worker.CancellationPending) { // The Argument property of the DoWorkEventArgs // object holds the value of UpdatePeriod, which // was passed as the argument to the RunWorkerAsync // method. Thread.Sleep((int)e.Argument); // The DoWork eventhandler does not actually report // progress; the ReportProgress event is used to // periodically alert the control to update its state. worker.ReportProgress(0); } } // The ProgressChanged event is raised by the DoWork method. // This event handler does work that is internal to the // control. In this case, the currentOffset is incremented, // and the control is told to repaint itself. private void backgroundWorker1_ProgressChanged( object sender, System.ComponentModel.ProgressChangedEventArgs e) { this.currentOffset++; this.Refresh(); }
-
Реализовать вспомогательные методы,
IsLit
иDrawLight
.IsLit
Метод определяет цвет источника света в заданной позиции. «Включен» огни рисуются с помощью цветаLightColor
свойство и тех, которые находятся в «темной» рисуются с помощью цветаDarkColor
свойство.DrawLight
Метод рисует свет, используя соответствующий цвет, форму и положение.// This method determines if the marquee light at lightIndex // should be lit. The currentOffset field specifies where // the "first" light is located, and the "position" of the // light given by lightIndex is computed relative to this // offset. If this position modulo lightPeriodValue is zero, // the light is considered to be on, and it will be painted // with the control's lightBrush. protected virtual bool IsLit(int lightIndex) { int directionFactor = (this.spinDirectionValue == MarqueeSpinDirection.CW ? -1 : 1); return ( (lightIndex + directionFactor * this.currentOffset) % this.lightPeriodValue == 0 ); } protected virtual void DrawLight( Graphics g, Brush brush, int xPos, int yPos) { switch (this.lightShapeValue) { case MarqueeLightShape.Square: { g.FillRectangle(brush, xPos, yPos, this.lightSizeValue, this.lightSizeValue); break; } case MarqueeLightShape.Circle: { g.FillEllipse(brush, xPos, yPos, this.lightSizeValue, this.lightSizeValue); break; } default: { Trace.Assert(false, "Unknown value for light shape."); break; } } }
-
Переопределить OnLayout и OnPaint методы.
OnPaint Метод рисует огни вдоль края
MarqueeBorder
элемента управления.Так как OnPaint метод зависит от размеров
MarqueeBorder
элемента управления, необходимо вызвать его при каждом изменении макета. Для этого в Переопределите OnLayout и вызвать Refresh.protected override void OnLayout(LayoutEventArgs levent) { base.OnLayout(levent); // Repaint when the layout has changed. this.Refresh(); } // This method paints the lights around the border of the // control. It paints the top row first, followed by the // right side, the bottom row, and the left side. The color // of each light is determined by the IsLit method and // depends on the light's position relative to the value // of currentOffset. protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(this.BackColor); base.OnPaint(e); // If the control is large enough, draw some lights. if (this.Width > MaxLightSize && this.Height > MaxLightSize) { // The position of the next light will be incremented // by this value, which is equal to the sum of the // light size and the space between two lights. int increment = this.lightSizeValue + this.lightSpacingValue; // Compute the number of lights to be drawn along the // horizontal edges of the control. int horizontalLights = (this.Width - increment) / increment; // Compute the number of lights to be drawn along the // vertical edges of the control. int verticalLights = (this.Height - increment) / increment; // These local variables will be used to position and // paint each light. int xPos = 0; int yPos = 0; int lightCounter = 0; Brush brush; // Draw the top row of lights. for (int i = 0; i < horizontalLights; i++) { brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush; DrawLight(g, brush, xPos, yPos); xPos += increment; lightCounter++; } // Draw the lights flush with the right edge of the control. xPos = this.Width - this.lightSizeValue; // Draw the right column of lights. for (int i = 0; i < verticalLights; i++) { brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush; DrawLight(g, brush, xPos, yPos); yPos += increment; lightCounter++; } // Draw the lights flush with the bottom edge of the control. yPos = this.Height - this.lightSizeValue; // Draw the bottom row of lights. for (int i = 0; i < horizontalLights; i++) { brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush; DrawLight(g, brush, xPos, yPos); xPos -= increment; lightCounter++; } // Draw the lights flush with the left edge of the control. xPos = 0; // Draw the left column of lights. for (int i = 0; i < verticalLights; i++) { brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush; DrawLight(g, brush, xPos, yPos); yPos -= increment; lightCounter++; } } }
Создание пользовательского конструктора тени и свойства фильтра
MarqueeControlRootDesigner
Класс предоставляет реализацию для корневого конструктора. В дополнение к этому конструктору, работающему над MarqueeControl
, вам потребуется пользовательский конструктор, связанный с MarqueeBorder
элемента управления. Этот конструктор поддерживает пользовательское поведение, которое подходит в контексте пользовательского корневого конструктора.
В частности MarqueeBorderDesigner
будет «затенять» и выполните фильтрацию по некоторые свойства MarqueeBorder
элемента управления, изменив их взаимодействие со средой разработки.
Перехват вызовов к методу доступа свойства компонента называется «затемнения». Он позволяет конструктору для отслеживания значения, заданного пользователем и при необходимости передавать это значение в создаваемый компонент.
В этом примере Visible и Enabled будут затенены MarqueeBorderDesigner
, что предотвращает создание пользователя MarqueeBorder
управления скрытые и отключенные во время разработки.
Конструкторы можно также добавлять и удалять свойства. В этом примере Padding свойство будет удалено во время разработки, так как MarqueeBorder
управления программным образом задается заполнение на основе размера огней, указанных LightSize
свойство.
Базовый класс для MarqueeBorderDesigner
является ComponentDesigner, который содержит методы, которые можно изменить атрибуты, свойства и события, предоставляемые элементом управления во время разработки:
-
PreFilterProperties
-
PostFilterProperties
-
PreFilterAttributes
-
PostFilterAttributes
-
PreFilterEvents
-
PostFilterEvents
При изменении общего интерфейса компонента с помощью этих методов, необходимо соблюдать эти правила:
-
Добавление или удаление элементов в
PreFilter
только методы -
Изменение существующих элементов в
PostFilter
только методы -
Всегда следует сначала вызвать базовую реализацию
PreFilter
методы -
Всегда вызывайте базовую реализацию последней в
PostFilter
методы
Выполнение этих правил обеспечивает все конструкторы в среде разработки согласованное представление всех разрабатываемых компонентов.
ComponentDesigner Класс предоставляет словарь для управления значения замещенных свойств, что снимает необходимость в создании переменных конкретного экземпляра.
Для создания пользовательского конструктора тени и фильтрацию свойств
-
Щелкните правой кнопкой мыши разработки папку и добавьте новый класс. Присвойте файлу источника, базовым именем «MarqueeBorderDesigner.»
-
Откройте
MarqueeBorderDesigner
исходный файл в редактор кода. В верхней части файла импортируйте следующие пространства имен:using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System.Windows.Forms; using System.Windows.Forms.Design;
-
Измените объявление
MarqueeBorderDesigner
наследование ParentControlDesigner.Так как
MarqueeBorder
элемент управления может содержать дочерние элементы управления,MarqueeBorderDesigner
наследует от ParentControlDesigner, который обрабатывает взаимодействие родители потомки.namespace MarqueeControlLibrary.Design { [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] public class MarqueeBorderDesigner : ParentControlDesigner {
-
Переопределить базовую реализацию PreFilterProperties.
protected override void PreFilterProperties(IDictionary properties) { base.PreFilterProperties(properties); if (properties.Contains("Padding")) { properties.Remove("Padding"); } properties["Visible"] = TypeDescriptor.CreateProperty( typeof(MarqueeBorderDesigner), (PropertyDescriptor)properties["Visible"], new Attribute[0]); properties["Enabled"] = TypeDescriptor.CreateProperty( typeof(MarqueeBorderDesigner), (PropertyDescriptor)properties["Enabled"], new Attribute[0]); }
-
Реализуйте свойства Enabled и Visible. Эти реализации теневые свойства элемента управления.
public bool Visible { get { return (bool)ShadowProperties["Visible"]; } set { this.ShadowProperties["Visible"] = value; } } public bool Enabled { get { return (bool)ShadowProperties["Enabled"]; } set { this.ShadowProperties["Enabled"] = value; } }
Обработка изменений компонентов
MarqueeControlRootDesigner
Класс предоставляет пользовательский интерфейс во время разработки для вашей MarqueeControl
экземпляров. Большая часть функций разработки наследуется от DocumentDesigner класса; ваш код будет реализовать два изменения: обработки компонентов и добавление команд конструктора.
Как пользователям при проектировании их MarqueeControl
экземпляров, корневой конструктор будет отслеживать изменения MarqueeControl
и его дочерним элементам. Среда разработки предлагает служба IComponentChangeServiceдля отслеживания изменений состояния компонентов.
Получить ссылку на эту службу путем запроса среды с GetService метод. Если запрос выполнен успешно, конструктор можно присоединить обработчик для ComponentChanged событий и выполнения всех задач, необходимых для поддержания согласованного состояния во время разработки.
В случае использования MarqueeControlRootDesigner
, будет вызывать Refresh метод на каждом IMarqueeWidget
объект, содержащийся в MarqueeControl
. Это приведет к IMarqueeWidget
объект окрашивание соответствующим образом при его родительского элемента, такие как свойства Size изменяются.
Обработка изменений компонентов
-
Откройте
MarqueeControlRootDesigner
исходный файл в редактор кода и переопределить Initialize метод. Вызвать базовую реализацию Initialize и запрашивать IComponentChangeService.base.Initialize(component); IComponentChangeService cs = GetService(typeof(IComponentChangeService)) as IComponentChangeService; if (cs != null) { cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); }
-
Реализуйте OnComponentChanged обработчик событий. Проверьте тип компонента отправки, и если это
IMarqueeWidget
, вызовите его Refresh метод.private void OnComponentChanged( object sender, ComponentChangedEventArgs e) { if (e.Component is IMarqueeWidget) { this.Control.Refresh(); } }
Добавление команд конструктора в пользовательский конструктор
Команда конструктора является команды меню, связанная с обработчиком событий. Команды конструктора, добавляемые в контекстное меню компонента во время разработки. Дополнительные сведения см. в разделе DesignerVerb.
Вы добавите две команды в конструкторы: Запуск теста и остановить тестовый. Эти команды можно просмотреть его поведение во время выполнения MarqueeControl
во время разработки. Эти команды будут добавлены к MarqueeControlRootDesigner
.
При запуска теста — вызывается, обработчик событий команды вызовет StartMarquee
метод MarqueeControl
. При Остановить тест будет вызван, будет вызывать обработчик событий команды StopMarquee
метод MarqueeControl
. Реализация StartMarquee
и StopMarquee
методы вызывать эти методы в элементах управления, которые реализуют IMarqueeWidget
, поэтому любые содержащиеся IMarqueeWidget
элементы управления также будет участвовать в тесте.
Добавление команд конструктора в пользовательский конструктор
-
В
MarqueeControlRootDesigner
добавьте обработчики событий с именемOnVerbRunTest
иOnVerbStopTest
.private void OnVerbRunTest(object sender, EventArgs e) { MarqueeControl c = this.Control as MarqueeControl; c.Start(); } private void OnVerbStopTest(object sender, EventArgs e) { MarqueeControl c = this.Control as MarqueeControl; c.Stop(); }
-
Подключите эти обработчики событий с соответствующими командами конструктора.
MarqueeControlRootDesigner
наследует DesignerVerbCollection от своего базового класса. Вы создадите два новых DesignerVerb объектов и добавления их в эту коллекцию в Initialize метод.this.Verbs.Add( new DesignerVerb("Run Test", new EventHandler(OnVerbRunTest)) ); this.Verbs.Add( new DesignerVerb("Stop Test", new EventHandler(OnVerbStopTest)) );
Создание пользовательского редактора UITypeEditor
При создании пользовательского взаимодействия во время разработки для пользователей, часто желательно изменить взаимодействие с окном свойств. Это можно сделать, создав UITypeEditor. Дополнительные сведения см. в разделе Как Создание редактора типов пользовательского интерфейса.
MarqueeBorder
Элемент управления предоставляет несколько свойств в окне «Свойства». Два из этих свойств MarqueeSpinDirection
и MarqueeLightShape
, представлены перечислениями. Чтобы проиллюстрировать использование редактора типов пользовательского интерфейса, MarqueeLightShape
свойство будет иметь сопоставленный UITypeEditor класса.
Создание редактора типов пользовательского интерфейса
-
Откройте
MarqueeBorder
исходный файл в редактор кода. -
В определении
MarqueeBorder
класса, объявите класс с именемLightShapeEditor
, наследуемый от класса UITypeEditor.// This class demonstrates the use of a custom UITypeEditor. // It allows the MarqueeBorder control's LightShape property // to be changed at design time using a customized UI element // that is invoked by the Properties window. The UI is provided // by the LightShapeSelectionControl class. internal class LightShapeEditor : UITypeEditor {
-
Объявите IWindowsFormsEditorService переменная экземпляра с именем
editorService
.private IWindowsFormsEditorService editorService = null;
-
Переопределите метод GetEditStyle . Эта реализация возвращает DropDown, который среда разработки способ отображения
LightShapeEditor
.public override UITypeEditorEditStyle GetEditStyle( System.ComponentModel.ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; }
-
Переопределите метод EditValue . Эта реализация запрашивает среду проектирования для IWindowsFormsEditorService объекта. Если успешно, он создает
LightShapeSelectionControl
. DropDownControl Метод вызывается для запускаLightShapeEditor
. Возвращаемое значение из этого вызова возвращается в среду разработки.public override object EditValue( ITypeDescriptorContext context, IServiceProvider provider, object value) { if (provider != null) { editorService = provider.GetService( typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService; } if (editorService != null) { LightShapeSelectionControl selectionControl = new LightShapeSelectionControl( (MarqueeLightShape)value, editorService); editorService.DropDownControl(selectionControl); value = selectionControl.LightShape; } return value; }
Создание элемента управления представления для пользовательского редактора UITypeEditor
MarqueeLightShape
Свойство поддерживает два типа простых фигур:Square
иCircle
. Вы создадите пользовательский элемент управления, используемая исключительно для графическое отображение этих значений в окне «Свойства». Этот пользовательский элемент управления будет использоваться ваш UITypeEditor для взаимодействия в окне «Свойства».
Чтобы создать элемент управления для пользовательского редактора типов
-
Добавьте новый UserControl элемент
MarqueeControlLibrary
проекта. Предоставить новый исходный файл базовым именем «LightShapeSelectionControl.» -
Перетащите два Panel управляет из элементов на
LightShapeSelectionControl
. Присвойте им названияsquarePanel
иcirclePanel
. Расположите их рядом друг с другом. Задайте Size свойства обоих Panel элементы управления (60, 60). Задайте Location свойствоsquarePanel
управления (8, 10). Задайте Location свойствоcirclePanel
управления (80, 10). Наконец, установите Size свойствоLightShapeSelectionControl
для (150, 80). -
Откройте
LightShapeSelectionControl
исходный файл в редактор кода. В верхней части файла, импортировать System.Windows.Forms.Design пространство имен:
Imports System.Windows.Forms.Design
using System.Windows.Forms.Design;
-
Реализуйте Click обработчики событий для
squarePanel
иcirclePanel
элементов управления. Эти методы вызывают CloseDropDown для окончания UITypeEditor сеанса редактирования.private void squarePanel_Click(object sender, EventArgs e) { this.lightShapeValue = MarqueeLightShape.Square; this.Invalidate( false ); this.editorService.CloseDropDown(); } private void circlePanel_Click(object sender, EventArgs e) { this.lightShapeValue = MarqueeLightShape.Circle; this.Invalidate( false ); this.editorService.CloseDropDown(); }
-
Объявите IWindowsFormsEditorService переменная экземпляра с именем
editorService
.
Private editorService As IWindowsFormsEditorService
private IWindowsFormsEditorService editorService;
-
Объявите
MarqueeLightShape
переменная экземпляра с именемlightShapeValue
.private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
-
В
LightShapeSelectionControl
конструктор, присоединение Click обработчики событий дляsquarePanel
иcirclePanel
элементов управления Click события. Кроме того, задайте перегрузки конструктора, которая назначаетMarqueeLightShape
значение из среды разработки дляlightShapeValue
поля.// This constructor takes a MarqueeLightShape value from the // design-time environment, which will be used to display // the initial state. public LightShapeSelectionControl( MarqueeLightShape lightShape, IWindowsFormsEditorService editorService ) { // This call is required by the designer. InitializeComponent(); // Cache the light shape value provided by the // design-time environment. this.lightShapeValue = lightShape; // Cache the reference to the editor service. this.editorService = editorService; // Handle the Click event for the two panels. this.squarePanel.Click += new EventHandler(squarePanel_Click); this.circlePanel.Click += new EventHandler(circlePanel_Click); }
-
В Dispose метода отсоединения Click обработчики событий.
protected override void Dispose( bool disposing ) { if( disposing ) { // Be sure to unhook event handlers // to prevent "lapsed listener" leaks. this.squarePanel.Click -= new EventHandler(squarePanel_Click); this.circlePanel.Click -= new EventHandler(circlePanel_Click); if(components != null) { components.Dispose(); } } base.Dispose( disposing ); }
-
В обозревателе решений нажмите кнопку Показать все файлы. Откройте файл LightShapeSelectionControl.Designer.cs или LightShapeSelectionControl.Designer.vb и удалить определение по умолчанию Dispose метод.
-
Реализуйте свойство
LightShape
.// LightShape is the property for which this control provides // a custom user interface in the Properties window. public MarqueeLightShape LightShape { get { return this.lightShapeValue; } set { if( this.lightShapeValue != value ) { this.lightShapeValue = value; } } }
-
Переопределите метод OnPaint . Эта реализация будет рисовать квадрат с заливкой и круг. Он также выделяет выбранное значение путем рисования границы вокруг одной формы или другого.
protected override void OnPaint(PaintEventArgs e) { base.OnPaint (e); using( Graphics gSquare = this.squarePanel.CreateGraphics(), gCircle = this.circlePanel.CreateGraphics() ) { // Draw a filled square in the client area of // the squarePanel control. gSquare.FillRectangle( Brushes.Red, 0, 0, this.squarePanel.Width, this.squarePanel.Height ); // If the Square option has been selected, draw a // border inside the squarePanel. if( this.lightShapeValue == MarqueeLightShape.Square ) { gSquare.DrawRectangle( Pens.Black, 0, 0, this.squarePanel.Width-1, this.squarePanel.Height-1); } // Draw a filled circle in the client area of // the circlePanel control. gCircle.Clear( this.circlePanel.BackColor ); gCircle.FillEllipse( Brushes.Blue, 0, 0, this.circlePanel.Width, this.circlePanel.Height ); // If the Circle option has been selected, draw a // border inside the circlePanel. if( this.lightShapeValue == MarqueeLightShape.Circle ) { gCircle.DrawRectangle( Pens.Black, 0, 0, this.circlePanel.Width-1, this.circlePanel.Height-1); } } }
Тестирование элемента управления в конструкторе
На этом этапе вы можете создавать MarqueeControlLibrary
проекта. Проверьте реализацию, создав элемент управления, который наследует от MarqueeControl
класс и использовать его в форме.
Чтобы создать пользовательскую реализацию MarqueeControl
-
Откройте
DemoMarqueeControl
в конструкторе Windows Forms. Это создает экземпляр классаDemoMarqueeControl
введите и отображает его в экземплярMarqueeControlRootDesigner
типа. -
В элементовоткройте компоненты MarqueeControlLibrary вкладки. Вы увидите
MarqueeBorder
иMarqueeText
элементов управления, доступных для выбора. -
Перетащите экземпляр
MarqueeBorder
управленияDemoMarqueeControl
область конструктора. ПрикрепитеMarqueeBorder
управления родительскому элементу управления. -
Перетащите экземпляр
MarqueeText
управленияDemoMarqueeControl
область конструктора. -
Постройте решение.
-
Щелкните правой кнопкой мыши
DemoMarqueeControl
и в контекстном меню выберите запуска теста параметр для запуска анимации. Нажмите кнопку Остановить тест для остановки анимации. -
Откройте форму Form1 в конструкторе.
-
Разместите два Button элементов управления формы. Присвойте им названия
startButton
иstopButton
и измените Text значения свойств запустить и остановить, соответственно. -
Реализуйте Click обработчики событий для обоих Button элементов управления.
-
В элементовоткройте компоненты MarqueeControlTest вкладки. Вы увидите
DemoMarqueeControl
доступны для выбора. -
Перетащите экземпляр
DemoMarqueeControl
на Form1 область конструктора. -
В Click вызывать обработчики событий,
Start
иStop
методыDemoMarqueeControl
.
Private Sub startButton_Click(sender As Object, e As System.EventArgs)
Me.demoMarqueeControl1.Start()
End Sub 'startButton_Click
Private Sub stopButton_Click(sender As Object, e As System.EventArgs)
Me.demoMarqueeControl1.Stop()
End Sub 'stopButton_Click
private void startButton_Click(object sender, System.EventArgs e)
{
this.demoMarqueeControl1.Start();
}
private void stopButton_Click(object sender, System.EventArgs e)
{
this.demoMarqueeControl1.Stop();
}
- Задайте
MarqueeControlTest
проект в качестве запускаемого проекта и запустите его. Отобразится форма вашейDemoMarqueeControl
. Нажмите кнопку запустить кнопки для запуска анимации. Вы увидите текст мигающий и источники света, перемещение вокруг границы.
Следующие шаги
MarqueeControlLibrary
Показан пример простой реализации пользовательских элементов управления и связанные конструкторы. В этом примере можно делать более сложным несколькими способами:
-
Изменить значения свойств для
DemoMarqueeControl
в конструкторе. Добавить дополнительныеMarqueBorder
управляет и прикрепите их к родительским экземплярам для создания эффекта вложенных. Эксперимент с различными параметрами дляUpdatePeriod
и свойств, связанных с свет. -
Создание собственных реализаций
IMarqueeWidget
. Можно, например, создать мигания «neon знак» или знак анимированных с несколькими образами. -
Дальнейшая настройка поведения во время разработки. Попробуйте затенить больше свойств, чем Enabled и Visible, и можно добавить новые свойства. Добавьте новые команды конструктора для упрощения общих задач, таких как закрепление дочерних элементов управления.
-
Лицензии
MarqueeControl
. Дополнительные сведения см. в разделе Как Лицензирование компонентов и элементов управления. -
Контролировать порядок сериализации элементов управления и способ создания кода для них. Дополнительные сведения см. в разделе динамического кода Создание и компиляция исходного.
См. также
- UserControl
- ParentControlDesigner
- DocumentDesigner
- IRootDesigner
- DesignerVerb
- UITypeEditor
- BackgroundWorker
- Практическое руководство. Создание элемента управления Windows Forms, используются преимущества функций времени разработки
- Расширения поддержки времени разработки
- Пользовательские конструкторы