Я не могу вырваться из своего ClipRectangle и хочу плакать

#c# #winforms #custom-controls #system.drawing

#c# #winforms #пользовательские элементы управления #system.drawing

Вопрос:

При переопределении метода OnPaint пользовательского элемента управления мне предоставляется PaintEventArgs, который содержит графический объект и ClipRectangle. На мой вкус ClipRectangle слишком мал, и поэтому я хочу либо расширить его, скажем, на 100 пикселей, либо полностью проигнорировать.

Что бы я ни делал, кажется, я не могу вырваться из ClipRectangle. До сих пор я пытался:

 e.ClipRectangle.Inflate(100, 0);
e.Graphics.ResetClip();
e.Graphics.SetClip(new Rectangle(x, y, w, h));
  

Родительский элемент управления вызывает Invalidate(true) (чтобы принудительно аннулировать все дочерние элементы управления дочерних элементов).

Я также немного погуглил и заглянул на сайт Боба Пауэлла, но ничего не могу найти.

ResetClip сбрасывает область обрезки для графического объекта до бесконечно большого прямоугольника, но графический объект бодро продолжает использовать предоставленный ClipRectangle.

Справка.

Комментарии:

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

2. Я думаю, вы правы в том, что вызывающий пользователь приложил руку к указанию того, какая область будет нарисована. Дело в том, что я вызывающий, и я вызываю Invalidate как обычно без ограничений.

3. Windows API передает область клипа в WM_PAINT, если происходит перерисовка, потому что требуется перерисовать только часть элемента управления, например, если вы свернули окно, которое лишь частично закрывало элемент управления. Я предполагаю, что ClipRectangle просто предоставляет вам более удобный вид области clip, но я бы не ожидал, что там будет область clip, если вы вызываете Invalidate . Этот элемент управления частично покрыт другими элементами управления? Можете ли вы показать простой код воспроизведения?

4. Кстати, 1 только за название.

Ответ №1:

Резюме: Звучит так, как будто у вас есть дочерний элемент управления с пользовательским методом рисования. Вы хотите нарисовать элемент управления в области, которая больше, чем границы самого элемента управления. Например, если вы разместите свой элемент управления в (10, 10, 100, 100), вы хотите нарисовать область в (0, 10, 110, 100).

Вы точно не можете этого сделать, HDC элемента управления, который передается методу paint, предназначен для собственного дескриптора окна этого элемента управления. Он обрезан операционной системой, и AFAIK нет способа рисовать за его пределами с помощью HDC, заданного методу paint.

Что вы можете сделать: Один из вариантов — переопределить метод create родительского окна и удалить стиль окна WS_CLIPCHILDREN (0x02000000), затем нарисовать дочерний элемент в родительском методе paint.

Другой вариант — просто расширить область дочернего окна, чтобы оно охватывало область, которую вы хотите нарисовать. Если вы также переопределите метод OnPaintBackground вашего элемента управления, вы можете запретить background paint очищать рендеринг родительского элемента. Это проблематично, хотя, поскольку родительский элемент обрезает область дочернего элемента, а не обновляет ее. Таким образом, вам все равно нужно удалить родительский WS_CLIPCHILDREN, чтобы он заработал.

Я уверен, что есть другие возможности, все из которых в основном приводят к тому же результату, вы не можете этого сделать.

Комментарии:

1. У меня есть пользовательский родительский элемент управления и пользовательский дочерний элемент управления, который содержит стандартные элементы управления. Ваш пост помог мне отследить проблему. ClipRectangle был в порядке: пользовательский дочерний элемент управления обрезал свои текстовые поля. Вырваться не было решением, было рассуждение, почему это было отсечение в первую очередь. Спасибо, что наставили меня на правильный путь!

Ответ №2:

Rectangle — это структура, а не класс — он передается как значение, поэтому вызов e.ClipRectangle.Inflate(100, 0); не может помочь даже теоретически.

Следующее, что вам нужно знать, это то, что e.Graphics связано с контекстом аппаратного устройства, поэтому Windows не позволит вам расширять область рисования. Решение состоит в том, чтобы получить контекст устройства родительского окна, а затем рисовать там, где вы хотите. Вот как это сделать:

 [DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

IntPtr dcPtr = GetDC(this.ParentForm.Handle);
Graphics g = Graphics.FromHdc(dcPtr);

// your code here

// don't forget to release it
g.Dispose();
ReleaseDC(this.ParentForm.Handle, dcPtr);