C# WPF – Разработка WPF пользовательских компонентов
Недавно пришла мысль в голову: пора переходить на использование WPF! Ну и, соответственно, изучить надо его, чтобы также «виртуозно» использовать, как и WinForms. Заодно и более старшим, консервативным, коллегам доказать, что WPF использовать более эффективно и продуктивно. (И ПО на WPF работает «шутстрее».)
Вот и решил я этой публикацией «мешок зайцев настрелять»:
— Сравнить разработку компонента на WinForms (Ссылка на статью) и WPF;
— Доказать коллегам, что WPF — это продуктивно и эффективно;
— И сделать небольшой простой урок по освоению WPF (особенно для тех, кто привык к WinForms).
В прошлой публикации ссылка я рассказал о разработке компонента на WinForms. Поэтому, цель следующая: сделать такой же «по-смыслу» компонент, но на WPF. В целом, подход такой же и советы для WinForms работают и на WPF.
Забегая вперёд, получился следующий компонент, представленный на рисунке ниже.
Компонент (далее — «dpkEditWPF»), так же как и его «собрат» на WinForms является «partial» (разделён на несколько файлов (partial class), для удобства разработки).
У него также есть Свойство «Значение слова ДПК» (32 разряда) и «Текстовая метка» (чтобы там время отображать). Событие клика по «битовой ячейке» с номером бита также имеется, но только оно стало «маршрутизируемым».
/*Общедоступные свойства, события, генераторы событий*/
public partial class DpkWordEditWPF
{
///
/// Значение слова ДПК (32 разряда)
///
uint _dpkValue;
///
/// Свойство - Значение слова ДПК (32 разряда)
///
public uint DpkValue { get { return _dpkValue; } set { _dpkValue = value; Paint(); InvalidateVisual(); } }
///
/// Текстовая метка (добавляется к текстовому значению слова ДПК)
///
string _txtMark;
///
/// Свойство - Текстовая метка (добавляется к текстовому значению слова ДПК)
///
public string TextMark { get { return _txtMark; } set { _txtMark = value; Paint(); InvalidateVisual(); } }
/******/
///
/// Событие - клик по значению
///
public event ReturnEventHandler ClickByValue;
///
/// Генератор события клик по значению
///
/// номер бита (0-31)
void OnClickByValue(int index)
{
if (ClickByValue != null)
ClickByValue(this, new ReturnEventArgs(index));
}
}
Расчёт размеров dpkEditWPF также — относительный, на основе текущих размеров.
///
/// Установка пропорций
///
void SetProportions()
{
_hPropTextMark = 0.2;
_hPropAddress = 0.2;
_hPropAddressBinVal = 0.2;
_hPropData = 0.2;
_hPropDataBinVal = 0.2;
}
///
/// Установка размеров
///
void SetSizes()
{
/*TextMark*/
_heightTextMark = RenderSize.Height * _hPropTextMark;
/*Address*/
_heightAddress = RenderSize.Height * _hPropAddress;
/*AddressBinValue*/
_heightAddressBinVal = RenderSize.Height * _hPropAddressBinVal;
_widthCellAddressBV = RenderSize.Width / 8.0;
/*Data*/
_heightData = RenderSize.Height * _hPropData;
/*DataBinValue*/
_heightDataBinVal = RenderSize.Height * _hPropDataBinVal;
_widthCellDataBV = RenderSize.Width / 24.0;
/*points*/
_ptTextMark = new Point(0,0);
_ptAddress = new Point(0,_heightTextMark);
_ptAddressBinVal = new Point(0, _heightTextMark + _heightAddress);
_ptData = new Point(0, _heightTextMark + _heightAddress + _heightAddressBinVal);
_ptDataBinVal = new Point(0, _heightTextMark + _heightAddress + _heightAddressBinVal + _heightDataBinVal);
}
Отрисовка компонента также разделена на несколько визуальных буферов и соответственно -процедур отрисовки.
///
/// визуальный Буфер значения слова ДПК + текстовая метка
///
DrawingVisual _imgTextMark;
///
/// визуальный буфер значения адреса
///
DrawingVisual _imgAddress;
///
/// визуальный буфер двоичного значения адреса (кликабельные ячейки)
///
DrawingVisual _imgAddressBinVal;
///
/// визуальный буфер значения данных
///
DrawingVisual _imgData;
///
/// визуальный буфер двоичного значения данных (кликабельные ячейки)
///
DrawingVisual _imgDataBinVal;
///
/// Отрисовка области текстового значения Адреса
///
void PaintAddress()
{
if (_imgAddress == null) return;
using (DrawingContext dc = _imgAddress.RenderOpen())
{
dc.DrawRectangle(Brushes.LightBlue, new Pen(Brushes.DarkGray, 1), new Rect(_ptAddress, new Size(RenderSize.Width, _heightAddress)));
string str = "Адрес: 0x" + (_dpkValue & 0xFF).ToString("X").PadLeft(2, '0');
DrawTxt(dc, str, _ptAddress, new Size(RenderSize.Width, _heightAddress), Brushes.Black);
}
}
Реализация клика по ячейке двоичного значения («битовой ячейке» с номером бита) реализована с помощью обработчика MouseUp, в котором генерируется событие клика по ячейке.
///
/// Обработка клика по ячейке
///
void DpkWordEditWPF_ClickByValue(object sender, ReturnEventArgs e)
{
uint mask = (uint)0x1 << e.Result;
if ((_dpkValue & mask) > 0)
_dpkValue &= (~mask);
else
_dpkValue |= mask;
Paint();
InvalidateVisual();
}
///
/// обработка клика мыши (превращение в клик по ячейке)
///
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
Point curPt = e.GetPosition(this);
/*Клик в области адреса*/
if ((curPt.X >= _ptAddressBinVal.X) && (curPt.X <= (_ptAddressBinVal.X+RenderSize.Width)))
if ((curPt.Y >= _ptAddressBinVal.Y) && (curPt.Y <= (_ptAddressBinVal.Y + _heightAddressBinVal)))
{
if ((curPt.X % _widthCellAddressBV) == 0) return;
int index = (int)(curPt.X / _widthCellAddressBV);
OnClickByValue(index);
return;
}
/*клик в области данных*/
if ((curPt.X >= _ptDataBinVal.X) && (curPt.X <= (_ptDataBinVal.X + RenderSize.Width)))
if ((curPt.Y >= _ptDataBinVal.Y) && (curPt.Y <= (_ptDataBinVal.Y + _heightDataBinVal)))
{
if ((curPt.X % _widthCellDataBV) == 0) return;
int index = (int)(curPt.X / _widthCellDataBV);
OnClickByValue(index + 8);
return;
}
}
Масштабирование и вывод на экран сделаны по тому же принципу, что и в компоненте на WinForms. Масштабирование — вызов отрисовки в буфер и вывод на экран. Вывод на экран — отрисовка визуальных буферов.
///
/// первая отрисовка при появлении
///
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
_imgTextMark = new DrawingVisual();
_imgAddress = new DrawingVisual();
_imgAddressBinVal = new DrawingVisual();
_imgData = new DrawingVisual();
_imgDataBinVal = new DrawingVisual();
Paint();
InvalidateVisual();
}
///
/// обработка вывода на экран
///
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
/*режим дизайнера*/
_dpkValue = 0xFFa42312;
Paint();
}
drawingContext.DrawDrawing(_imgTextMark.Drawing);
drawingContext.DrawDrawing(_imgAddress.Drawing);
drawingContext.DrawDrawing(_imgAddressBinVal.Drawing);
drawingContext.DrawDrawing(_imgData.Drawing);
drawingContext.DrawDrawing(_imgDataBinVal.Drawing);
}
Компонент написан на MS Visual Studio 2010, .Net Framework 4. Ссылка на проект: Ссылка на проект
В целом код получился компактнее, чем на WinForms и работает «шустрее». Два тестовых проекта были запущены на машине со следующими характеристиками: Windows XP SP2, процессор 1-ядерный, 1 Гб ОЗУ.
WPF продемонстрировал работу без фризов при изменении размеров формы, WinForms — в свою очередь, изрядно «подтормаживал» при тех же манипуляциях.
P.S. Надеюсь, скоро все проекты на WPF будем на заводе делать.
P.P. S. Трудно бороться с консерватизмом!