#.net #winforms #user-controls
#.net #winforms #пользовательские элементы управления
Вопрос:
У меня есть пользовательский пользовательский элемент управления, у которого есть StatusStrip. Итак, я изменяю размер этого элемента управления, когда пользователь перетаскивает угол этой строки состояния. Однако изменение размера происходит не очень хорошо: во время изменения размера на родительском элементе управления могут наблюдаться временные белые области, и иногда, если изменение размера происходит слишком быстро, пользователь «теряет» элемент управления (который останавливает изменение размера).
Option Infer On
Public Class FloattingGrid
Inherits System.Windows.Forms.UserControl
Dim mouseDownLocation As Nullable(Of Point)
Private Sub StatusStrip1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles StatusStrip1.MouseMove
If mouseDownLocation.HasValue Then
Dim newPosition = Cursor.Position
Dim dx = newPosition.X - mouseDownLocation.Value.X
Dim dy = newPosition.Y - mouseDownLocation.Value.Y
'Dim oldRect = New Rectangle(Me.Location, Me.Size)'
Me.Size = New Size(Me.Width dx, Me.Height dy)
mouseDownLocation = newPosition
If Me.Parent IsNot Nothing Then
'Me.Parent.Invalidate(oldRect) '
Me.Parent.Refresh()
End If
Else
If e.X > Me.Width - 20 Then
If Cursor <> Cursors.SizeNWSE Then Cursor = Cursors.SizeNWSE
Else
If Cursor = Cursors.SizeNWSE Then Cursor = Cursors.Default
End If
End If
End Sub
Private Sub StatusStrip1_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StatusStrip1.MouseLeave
Cursor = Cursors.Default
mouseDownLocation = Nothing
'Me.ResumeLayout() '
End Sub
Private Sub StatusStrip1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles StatusStrip1.MouseDown
If Cursor = Cursors.SizeNWSE Then
'Me.SuspendLayout() '
mouseDownLocation = Cursor.Position
End If
End Sub
Private Sub StatusStrip1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles StatusStrip1.MouseUp
mouseDownLocation = Nothing
'Me.ResumeLayout()'
End Sub
' Private Sub FloattingGrid_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove '
' End Sub '
Private Sub FloattingGrid_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.ResizeRedraw = True
End Sub
End Class
Я думаю, что такое поведение может быть вызвано недействительностью родительского элемента. Есть ли способ просто BeginInvalidate и не ждать, пока родительский элемент сделает недействительным весь регион?
Ответ №1:
BeginInvoke
не имеет ничего общего с рисованием и не имеет ничего общего с реализацией задержки. Все дело в межпоточном доступе, чего вы здесь не делаете. Это неправильное решение.
И в вызове нет ничего плохого Invalidate
. Это просто помечает область как требующую рисования. На самом деле это не приводит к многократному перерисовыванию этой области. Если область, которую вы объявляете недействительной, уже была признана недействительной, это не операция, поэтому она не несет ответственности за замедление чего-либо здесь. Если бы вы хотели, чтобы он был перерисован немедленно, вам нужно было бы вместо этого вызвать что-то вроде Refresh
.
Одна вещь, которую вы могли бы сделать, это запретить родительскому элементу управления пытаться изменить размер самого себя и изменить расположение его дочерних элементов управления, чтобы приспособить новое положение StatusStrip
. Для этого вызовите SuspendLayout
метод, когда вы начинаете изменять размер, и ResumeLayout
метод, когда вы заканчиваете.
Конечно, это не гарантирует решения вашей проблемы. Вы по-прежнему с большой вероятностью увидите задержку и появление белых или черных областей в областях, которые еще не были закрашены. Это происходит в других приложениях и даже при изменении размера Windows. Единственным решением этого является двойная буферизация, перенос всего во временный фоновый буфер, а затем вывод всего завершенного изображения на экран.
Комментарии:
1. фактически, после вызова родительского элемента. Обновить() вместо родительского элемента. Аннулировать (oldRect) белые области исчезли. Однако я всегда теряю контроль при ускорении. Я не изменяю размер StatusStrip, я напрямую изменяю размер UserControl (StatusStrip закреплен внизу)
2. @serhio: Да, вызов
Refresh
приводит к перерисовке немедленно . Вот почему белые области исчезают. Они только белые, потому что Windows еще ничего там не нарисовала. Побочным эффектом этого является то, что компьютер начинает отставать, потому что он не может справиться со всем этим сразу. Вот почему он обычно не рисует эти области, и почему, когда вы заставляете его, кажется, что он теряет контроль, когда вы начинаете быстро перемещаться. Это компромисс, вы должны выбрать, что для вас более приемлемо, или использовать альтернативную стратегию, такую как двойная буферизация (которая является просто уловкой, а не реальным решением).3. одной из причин может быть то, что я слежу за движениями только в StatusStrip_MouseMove, и если изменение размера происходит медленнее, чем движение мыши, мышь покидает область StatusStip и больше не вызывает никаких движений… Другое дело, что Refresh (), похоже, перерисовывает весь родительский элемент, а не только древнюю область управления — это тоже требует времени…
4. @serhio: Это возможно, но это все равно не решит проблемы с рисованием. Ваш код в любом случае странный. Почему вы используете
Nullable
тип, когда невозможно установить значение вnull
? Попробуйте захватить мышь на время события изменения размера.Control.Capture
это свойство, которое вам нужно для этого.5. 1 о захвате. Я сделал StatusStrip1.Capture = true при наведении курсора мыши и = False при наведении курсора мыши. Теперь работает довольно круто!