Сохранение рекламных символов наверху для выбранного элемента управления в Winforms Designer
TL; DR: я ищу правильный метод для определения времени рендеринга сопоставленной графики для конкретного элемента управления на поверхности дизайна, чтобы рисунок всегда рисовался перед глифами украшения, когда этот элемент управления выбран.
Этот вопрос касается разработчиков элементов управления для Winforms: когда пользователь помещает элемент управления в область конструктора, я хочу отобразить графику над клиентской областью элемента управления. Мне до некоторой степени удалось сделать это для элемента управления TableLayoutPanel (TLP), переопределив его обработчик событий OnPaint, а затем используя объект e.Graphics, доступный для рисования прямоугольника персикового цвета. Ниже приведено изображение, показывающее результаты: нарисованная графика, охватывающая ширину элемента управления и имеющая высоту 35 пикселей - помните, что это дизайнерский экземпляр элемента управления, размещенный на поверхности конструктора (созданный с помощью BasicLoader):
Однако внутри дизайнера, если я изменяю размер элемента управления, изображение всегда оказывается ниже глифа изменения размера (глифа, на котором есть стрелки Север / Юг и Запад / Восток):
Я пробовал создавать и поддерживать различные логические флаги для подавления сообщения OnPaint при определенных обстоятельствах. Например, я установил флаг, указывающий, что размер элемента управления был только что изменен (чтобы увидеть, как я это сделал, см. Мой недавний вопрос: событие BeginResize/EndResize для элемента управления на поверхности дизайна WinForms), чтобы подавить рисование графики, но это не сработало, потому что событие OnPaint неизбежно возникает после того, как я очистил флаг. Я не хочу обременять этот вопрос подробностями обо всех флагах и местах, которые я пытался использовать / устанавливать, но достаточно сказать, что я кропотливо проводил часы, экспериментируя - безрезультатно. Я пришел к выводу, что должен быть способ получше.
Как я могу гарантировать, что глифы остаются сверху, когда я рисую графику?
Спасибо!
1 ответ
Я могу придумать несколько решений, в том числе следующие:
- Использование заполнения
TableLayoutPanel
- Использование Adorner и Glyph
- Создание настраиваемой панели с заголовком и редактируемым содержимым
Я думаю, что первое решение вам подойдет, но и другие решения тоже кое-что.
Я также могу придумать решение, основанное на NativeWindow
как то, что было реализовано в ErrorProvider
, но это делает пост слишком длинным, в то время как существующие варианты достаточно хороши. Так что я оставляю это на ваше усмотрение, если вы хотите реализовать эту идею.
Решение 1. Использование заполнения TableLayoutPanel
Это решение предназначено как для времени разработки, так и для времени выполнения.
TableLayoutPanel
имеет Padding
Свойство и его механизм компоновки хорошо уважают отступы. Вы можете использовать область заполнения для рендеринга всего, что хотите:
public class MyTLP : TableLayoutPanel
{
public MyTLP()
{
Padding = new Padding(0, 30, 0, 0);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.Orange,
new Rectangle(0, 0, ClientRectangle.Width, Padding.Top));
}
}
Решение 2. Использование Adorner и Glyph
Это решение предназначено только для разработки
Для рендеринга во время разработки я буду обрабатывать его, используя Adorner
а также Glyph
.
Если бы я создавал свой собственный конструктор, весь код принадлежал конструктору элементов управления, но поскольку вы не хотите создавать новый конструктор элементов управления для TableLayoutPanel
, то так же, как я ввел новое настраиваемое действие в списки действий, здесь я получуBehaviorService
и я добавлю украшение в элементы управления во время разработки, и это будет результат:
Поведение очень похоже на другие глифы, он будет автоматически изменен при изменении размера элемента управления, и вам не нужно делать ничего особенного для обработки изменения размера во время разработки.
Обратите внимание: это временное решение, и роспись пока выполняется у дизайнера. Если вам нужно решение во время выполнения, вам нужно совершенно другое решение.
Вот код:
using System;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
public class MyTLP : TableLayoutPanel
{
private IDesignerHost designerHost;
private BehaviorService behaviorService;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (DesignMode && Site != null)
{
designerHost = Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
behaviorService = designerHost?.GetService(typeof(BehaviorService))
as BehaviorService;
if (behaviorService != null)
{
var adorner = new Adorner();
behaviorService.Adorners.Insert(0, adorner);
adorner.Glyphs.Add(new MyTLPGlypg(behaviorService, this));
}
}
}
}
class MyTLPGlypg : Glyph
{
Control control;
BehaviorService behaviorSvc;
public MyTLPGlypg(BehaviorService behaviorSvc, Control control) :
base(new MyBehavior())
{
this.behaviorSvc = behaviorSvc;
this.control = control;
}
public override Rectangle Bounds
{
get
{
var edge = behaviorSvc.ControlToAdornerWindow(control);
var h = 30;
return new Rectangle(edge.X, edge.Y - h, control.Size.Width, h);
}
}
public override Cursor GetHitTest(Point p)
{
//Uncomment if you want to attach a specific behavior
//if (Bounds.Contains(p)) return Cursors.Hand;
return null;
}
public override void Paint(PaintEventArgs pe)
{
pe.Graphics.FillRectangle(Brushes.Orange, Bounds);
}
}
class MyBehavior : Behavior
{
public override bool OnMouseUp(Glyph g, MouseButtons button)
{
//Do something and return true, meand eventhandled
return true;
}
}
Примечание:
Чтобы узнать больше о якорях, глифах и поведении, просмотрите следующие ссылки:
Решение 3. Создание настраиваемой панели с заголовком и редактируемым содержимым.
Вы можете создать настраиваемую панель с заголовком и редактируемым содержимым. Затем во время разработки запретите пользователю отбрасывать контент в части заголовка:
Для этого вам нужно создать новый дизайнер, который включит внутреннюю панель во время разработки, вызвав EnableDesignMode
метод. Затем для внутренней панели вам нужно создать дизайнер, который отключает перемещение, изменение размера и удаляет некоторые свойства из дизайнера.
Я опубликовал подробный ответ здесь: UserControl с заголовком и содержимым - Разрешить удаление элементов управления на панели содержимого и Предотвращение удаления элементов управления в заголовке во время разработки.