theskiguy Ответов: 1

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


Я работал над своим первым настраиваемым пользовательским элементом управления. Пользовательский элемент управления наследует UserControl. Пользовательский элемент управления содержит комбинацию нарисованных прямоугольников, кругов и изображения значка. Я хотел бы отображать всплывающую подсказку, когда пользователь наводит указатель мыши на часть изображения элемента управления. Изображение было создано с помощью drawicon. Затем в моем элементе управления есть событие перемещения мыши, которое определяет, когда мышь находится над значком. Вся эта логика работает правильно. У меня проблема с тем, что после того, как всплывающая подсказка исчезает, она стирает часть элемента управления, над которой зависал. Я пытаюсь выяснить, как правильно перекрасить элемент управления, но мне не повезло.

Что я уже пробовал:

Вот как я рисую контроль:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)

        Dim rect As System.Drawing.Rectangle = e.ClipRectangle
        Dim g As Graphics = e.Graphics
        g.SmoothingMode = SmoothingMode.HighQuality

        'Draw the Machine Bed
        DrawBed(g, rect)

        'Draw and position the Machine Head
        DrawHead(g, rect, _HeadPosInInches)

    End Sub

    Private Sub DrawBed(ByVal g As Graphics, ByVal CtlRect As System.Drawing.Rectangle)

        'Draws the bed

        'Size the bed based on the control size
        _BedWidth = CInt(CtlRect.Width * _BedWidthFactor)
        _BedHeight = CInt(CtlRect.Height * _BedHeightFactor)

        'Center the bed within the control
        _BedYBorder = CInt((CtlRect.Height - _BedHeight) / 2)
        _BedXBorder = CInt(CtlRect.Width - _BedWidth)

        'Create a new rect for the bed
        Dim BedXLoc As Integer = 0

        If HeadTravelDir = 1 Then
            BedXLoc = _BedXBorder
        Else
            BedXLoc = 0
        End If

        Dim BedRect As New Rectangle(BedXLoc, _BedYBorder, _BedWidth, _BedHeight)

        'Fill the Bed
        Using Brush1 As New SolidBrush(_BedColor)
            g.FillRectangle(Brush1, BedRect)
        End Using

        'Create the Bed border
        Using Pen1 As New Pen(Color.Black, 1)
            g.DrawRectangle(Pen1, BedRect)
        End Using

    End Sub


Вот как я рисую изображение значка на экране:

Dim TargetX As Integer = 0
Dim TargetY As Integer = 0
Dim TargetWidth As Integer = _BedYBorder
Dim TargetHeight As Integer = _BedYBorder

If _HeadTravelDir = 1 Then
   TargetX = HeadRect.X + HeadRect.Width + 5
Else
   TargetX = HeadRect.X - 5 - _BedYBorder
End If

Dim DashboardIcon As New Icon("U:\photos\DashboardIcons\Question.ico")
Dim TargetRect As New Rectangle(TargetX, TargetY, TargetWidth, TargetHeight)
IconRect = TargetRect
g.DrawIcon(DashboardIcon, TargetRect)


Вот как я обнаруживаю, когда мышь находится над изображением значка:

Private Sub ctlGantry_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove

        'Checks to see if the mouse if over the icon
        If IconRect.Contains(e.Location) Then
            Debug.WriteLine(e.Location.ToString & " is Over the Icon for " & Me.Name)
            _IconMouseOver = True
            'Me.ToolTip1.Show("test", Me)

        Else
            _IconMouseOver = False
            'Me.ToolTip1.Hide(Me)
            'Me.Invalidate()
        End If
    End Sub


Я попытался отобразить всплывающую подсказку как в событиях mousemove, так и в событиях mousehover, но получаю аналогичные результаты. Если я попытаюсь сделать недействительным, когда указатель мыши не находится над значком, я получу сильное мерцание. Фоновый элемент управления также не перерисовывается, когда я перемещаю мышь в другое положение над значком. Я не уверен, связано ли это с той же проблемой, но у меня также есть проблема, когда значок больше не появляется снова после того, как я наведу курсор на элемент управления в течение нескольких секунд, а затем вернусь и вернусь. Если я перекрашу элемент управления и попробую еще раз, всплывающая подсказка появится снова. Я очень близок к тому, чтобы завершить этот элемент управления, но сначала мне нужно это выяснить. Любые идеи?

Graeme_Grant

Вы предоставляете в случае UserControl_Paint [если UserControl является холст] и вызова пользовательских элементов управления.Метод Invalidate () для принудительного перекрашивания при обновлении пользовательского интерфейса?

theskiguy

- Вы правы. См. улучшенный вопрос, который теперь включает в себя некоторый код, показывающий картину элемента управления. Что касается недействительности, то проблема, с которой я сталкиваюсь, заключается в определении подходящего времени для ее вызова. Если я вызываю invalidate только тогда, когда мышь не находится над значком, элемент управления не будет перекрашиваться, если я перемещу мышь в другое положение на значке. Если я перемещаю invalidate в начало события перемещения мыши, мой пользовательский элемент управления полностью исчезает, даже если я перестаю перемещать мышь, когда она находится над значком.

Graeme_Grant

Я не вижу проблемы с кодом, который вы предоставили. Поэтому вместо этого я провел небольшой тест, и все работает так, как ожидалось. Надеюсь, это поможет.

1 Ответов

Рейтинг:
10

Graeme_Grant

Вот рабочий образец для вас:

Public Class UserControl1

    Public Sub New()

        InitializeComponent()
        AddTootip()
        Recalc()

    End Sub

    Private Tooltip As ToolTip

    Private Sub AddTootip()

        Tooltip = New ToolTip() With
        {
            .AutoPopDelay = 5000,
            .InitialDelay = 1000,
            .ReshowDelay = 500,
            .ShowAlways = True
        }
        Tooltip.SetToolTip(Me, "Test tooltip 1 2 3...")

    End Sub

    Private boxes(9) As Rectangle

    Private Sub UserControl1_Paint(sender As Object, e As PaintEventArgs) Handles MyBase.Paint

        Dim gr = e.Graphics
        Dim br = New SolidBrush(Color.Red)

        For index = 0 To 9
            gr.FillRectangle(br, boxes(index))
        Next

    End Sub

    Private Sub UserControl1_MouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.MouseMove

        For index = 0 To 9
            If boxes(index).Contains(e.Location) Then
                Tooltip.ToolTipTitle = "Box " + CStr(index + 1)
                Exit Sub
            End If
        Next
        Tooltip.ToolTipTitle = "UserControl background..."

    End Sub

    Private Sub UserControl1_Resize(sender As Object, e As EventArgs)
        Recalc()
    End Sub

    Private Sub Recalc()

        Dim hoffset = Height / 11
        Dim woffset = Width / 11
        Dim sz = New Size(woffset, hoffset)

        For index = 0 To 9
            Dim pt = New Point(index * woffset, index * hoffset)
            boxes(index) = New Rectangle(pt, sz)
        Next

        Invalidate()

    End Sub

End Class


theskiguy

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

В качестве примечания у меня было несколько синтаксических ошибок в вашем примере, которые я должен был устранить, поэтому я решил обратить на них ваше внимание, чтобы вы могли обновить свой код. Мне пришлось обновить синтаксис для оператора "With"в вашем подразделе AddToolTip. Мне также пришлось объявить переменные "Index" в mouse_move, Recalc и paint subs. Спасибо за помощь. Если я наконец выясню, что вызвало мою проблему, я дам вам знать. Спасибо.

Graeme_Grant

Извините, я перешел с VB на C# пару лет назад... Эти синтаксические ошибки могут быть вашей версией Visual Studio. Это было сделано на VS2017 и успешно скомпилировано. Для более старых версий, чем VS2016 (?), вам может потребоваться добавить подчеркивания для продолжения строки...

Удачи вам в вашем проекте. :)