Как нарисовать дочерние элементы управления и пользовательские рисунки в ожидаемом порядке? - PullRequest
0 голосов
/ 20 июля 2011

У меня есть контроль с нестандартными чертежами, назовем его Surface.

Я хочу встроить некоторые элементы управления winforms в этот Surface. Но я также хочу нарисовать несколько пользовательских чертежей (соединителей), в зависимости от расположения элементов управления, размера и т. Д.

Я попытался взять на себя ответственность за то, чтобы эти соединители были подключены к дочерним элементам управления, поэтому любой из моих дочерних элементов управления был просто больше по размеру и включал все необходимые чертежи. Я использовал Parent.InvokePaint вызов для рисования родительского фона, когда это было необходимо (и я также пробовал прозрачный фон).

Недостаток : когда родительский фон закрашивается внутри дочернего события OnPaint, он стирает все остальные элементы управления, уже нарисованные. На рисунке ниже показан результат. Цветные области - это дочерние элементы управления.

enter image description here

Я также пытался возложить ответственность за отрисовку соединителей в Surface и добавить дочерние элементы управления в список Surface.Controls.

Недостаток : дочерние элементы управления всегда рисуются поверх моего пользовательского чертежа (что происходит в Surface.OnPaint), потому что их рисование происходит когда-то позже в очереди.

enter image description here

Мои требования:

Мне нужны работоспособные элементы управления внутри моего Surface (получение любого события мыши и клавиатуры, фокусировка и т. Д.). Каждый элемент управления будет иметь несколько пользовательских коннекторов для отображения контекста на поверхности. И когда элементы управления перекрываются, я хочу, чтобы все отображалось в ожидаемом порядке.

Как этого добиться?

Ответы [ 3 ]

1 голос
/ 21 июля 2011

Я нашел простое решение своей проблемы.

Я только что использовал свойство Region дочернего элемента управления (всплывающее окно) для настройки пути отсечения:

GraphicsPath path = new GraphicsPath();
// add some shapes to path here, describing the control shape
// ...
this.Region = new Region(path);

Я не зналпользовательское обрезание пути перед.

0 голосов
/ 24 сентября 2018

@ Dimitry - спасибо за информацию C #.Этот вопрос действительно помог мне начать помещать пользовательский рисунок в пользовательский элемент управления.Тем не менее, я использую Visual Basic .NET, так что здесь есть некоторые подробности и перспективы VB.Zorder, в котором графика размещена на поверхности, также имеет решающее значение.Это потому, что нет способа изменить z-порядок объектов Drawing2D, таких как .GrahicsPath и .FillRectangle;только Zorder of Controls может быть изменен.Чтобы скрыть часть существующего элемента управления и нарисовать над ним стрелку, выполните следующие действия.Весь метод должен быть помещен в событие Paint элемента управления;Затем необходимо позаботиться о том, чтобы событие Paint запускалось всякий раз, когда элемент управления перерисовывается (это уже другая история!):

  1. Создание объекта Graphics, присоединенного к элементу управления
  2. Обновите элемент управления
  3. Заполните прямоугольник, чтобы скрыть часть существующей информации элемента управления.
  4. Создайте и установите нужную форму в качестве обтравочной маски (в приведенном ниже примере кода - стрелка)
  5. Заполните форму желаемым цветом / изображением или любым другим необходимым размером.

В приведенном ниже примере, примененном в пользовательском элементе управления на основе ListView, создается стрелка и помещается наслева от заголовка.Он скрывает ненужный CheckBox, оставляет нужные поля зрения и выглядит так:

![ListView overlaid with Custom Arrow that obscures an unwanted CheckBox.
Прошло несколько лет с тех пор, как вы опубликовали свой вопрос и предыдущийответ.Возможно, хорошая информация, которая теперь есть в справке .NET, была добавлена ​​с тех пор.Найдите это здесь с дополнительной информацией и ссылками.


Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)       'This is traps the default OnPaint for a Component, so it starts by calling the Base's Paint Event
    MyBase.OnPaint(e)

    'Add your custom paint code here - in this example, a Green Arrow
    AddArrowOverHeaderCheckBox()        
End Sub
Private Sub AddArrowOverHeaderCheckBox()
    'Adds an Arrow that is placed over the Checkbox of the 0th row, of a ListView, (over a Row that is used as a Header in this Custom Control).

    'Create a Rectangle into which to put the arrow
    Dim G As Graphics = Me.CreateGraphics               'Attach a Graphics Object to the Base
    Dim lvsi As ListViewItem.ListViewSubItem = Me.Items(0).SubItems(0)   'Get the first row; it is being used as a header in this case
    Dim CheckBoxWidth As Integer = Me.Bounds.X          'Calculate the size of the Control's CheckBox
    Dim CheckBoxHeight As Integer = lvsi.Bounds.Height
    Dim InflateBy As Integer = 1                        'Optional amount to resize the area in which the Arrow is to be placed
    Me.Refresh()                                        'Refresh the ListViewTab so that it does not overpaint the arrow that is about to be created at the End Sub
    Dim I As Integer = 0
    I = (CheckBoxHeight + 1) Mod 2
    Dim R As Rectangle = New Rectangle(lvsi.Bounds.X + Me.Margin.Left, lvsi.Bounds.Top, CheckBoxWidth, CheckBoxHeight + I) 'Make sure the height is an Odd number, to make a tidy arrow
    'myBrush.Color = Color.Red
    R.Inflate(InflateBy * 2, InflateBy)                  'Create a covering rectangle that is larger than the CheckBox on the Left of the CheckView Item in both dimensions
    Dim myBrush As New System.Drawing.SolidBrush(Me.BackColor)
    G.FillRectangle(myBrush, R)                         'Draw a filled Rectangle over the CheckBox in the Background colour for the ListViewTab to obscure the CheckBox
    Dim RW = R.Width
    Dim RH = R.Height
    I = CSng(RH) * 0.5 \ 2                              'Set a proportion of the arrow's width for its shaft, here 50%
    If RH Mod 2 <> 0 Then RH += 1                       'Ensure that the Height is an Odd number to make the Arrow axially symmetric
    '                                                   'Create a polygon in an Arrow shape that fits inside the Rectangle
    Dim polyArrow As Point() = {New Point(R.X + 0, I + R.Y), New Point(R.X + RW * 0.4, I + R.Y), New Point(R.X + RW * 0.4, 0 + R.Y), New Point(R.X + RW, RH * 0.5 + R.Y),
                                New Point(R.X + RW * 0.4, RH + R.Y), New Point(R.X + RW * 0.4, RH - I + R.Y), New Point(R.X + 0, RH - I + R.Y)}
    Dim path As New Drawing2D.GraphicsPath()            'Create a Path shaped like an Arrow & sized proportionately to the size of the required Rectangle
    path.AddPolygon(polyArrow)                          'Turn the Path into a polygon
    Dim [reg] As New [Region](path)                     'Define a Graphics Region of the shape of the Arrow
    G.SetClip([reg], combineMode:=Drawing2D.CombineMode.Replace) 'Redefine the area; clipping will now prevent any drawing outside the area of the Arrow
    myBrush.Color = Color.Green
    G.FillRectangle(myBrush, R)                         'Draw a filled Green Rectangle, clipped to the Path shape
    'Dim pen As Pen = Pens.Black                         'Optionally: Draw a fine boundary in black
    'G.DrawPath(pen, path)                               'Optionally: Draw a fine boundary in black
    G.SetClip(Me.ClientRectangle)                       'Remove the Clip from the Graphics area - in case G is re-used
    myBrush.Dispose()                                   'Tidy up
    G.Dispose()
End Sub

0 голосов
/ 20 июля 2011

Когда вам нужно настроить z-порядок элементов управления в WinForm, вы можете использовать элементы управления 'SendToFront и SendToBack.Лучшее решение - перейти к контейнеру элемента управления.В свойстве Controls контейнера есть метод SetChildIndex, который может независимо устанавливать z-порядок содержащихся в нем элементов управления.

...