Страница 1 из 1

KB317479 Как нарисовать резиновую ленту ...

СообщениеДобавлено: 30.05.2006 (Вт) 14:06
Viper
HOW TO: Draw a Rubber Band or Focus Rectangle in Visual Basic .NET

(Как нарисовать резиновую ленту или прямоугольник фокуса в Visual Basic .NET)

PSS ID Number: 317479
Последнее изменение статьи 2.11.2004

Информация в этой статье применима к:

Microsoft .NET Framework Class Libraries 1.0
Microsoft .NET Framework Class Libraries 1.1
Microsoft Visual Basic .NET (2002)
Microsoft Visual Basic .NET (2003)

Данная статья ранее опубликована как Q317479
Версию этой статьи для Microsft Visual Studio .NET C# смотрите под номером 314945.

ОБЩАЯ ИНФОРМАЦИЯ

Резиновая лента или прямоугольник фокуса - это прямоугольник, который следует за указателем мыши, пока вы держите нажатой левую кнопку мыши. Эта техника обычно используется для определения границ выделенной зоны при работе с указателем мыши. В интерфейсе графического устройства (GDI) этот прямоугольник обычно реализуется при помощи растровых операция (ROPs). Однако, методы System.Drawing основаны на GDI+ (преемник GDI), который не поддерживает ROPs.

Эта статья показывает другой подход для реализации прямоугольника фокуса в .NET Framework.
В GDI прямоугльник фокуса часто рисуется при помощи ROP кодов. В частности, часто используются ROP2 коды R2_XORPEN и R2_NOT. Когда вы используете один из этих ROP2 кодов, вы можете стирать предыдущую линию, рисуя еще раз в той же позиции. Эта методика известна также как эффект исключающего ИЛИ (XOR).


Пример кода

Поскольку ROPs не доступны в GDI+ и в System.Drawing, вы должны использовать другой способ рисования обратимых линий при помощи этих средств. Например, вы можете средства PInvoke для взаимодействия с GDI. Однако, решение использующее только управляемый код доступно при использовании статического метода ControlPaint::DrawReversibleFrame(). Следующий код, написанный в Visual Basic .NET и готовый для вставки в класс формы в приложение по умолчанию Visual Basic .NET, демонстрирует этот способ:
Код: Выделить всё
Public Class Form1
   Inherits System.Windows.Forms.Form
   
   Dim bHaveMouse As Boolean
   Dim ptOriginal As Point
   Dim ptLast As Point

+[Windows Form Designer generated code]

   ' Конвертируем и нормализуем точки и рисуем обратимую рамку.
   Private Sub MyDrawReversibleRectangle(ByVal p1 As Point,ByVal p2 As Point)
      Dim rc As Rectangle
      ' Конвертируем точки в экранные координаты.
      p1 = PointToScreen(p1)
      p2 = PointToScreen(p2)
      ' Нормализуем прямоугольник.
      If (p1.X < p2.X) Then
         rc.X = p1.X
         rc.Width = p2.X - p1.X
      Else
         rc.X = p2.X
         rc.Width = p1.x - p2.X
      End If
      If (p1.Y < p2.Y) Then
         rc.Y = p1.Y
         rc.Height = p2.Y - p1.Y
      Else
         rc.Y = p2.Y
         rc.Height = p1.Y - p2.Y
      End If
      ' Рисуем обратимую рамку.
      ControlPaint.DrawReversibleFrame(rc, Color.Red, FrameStyle.Dashed)
   End Sub

   ' Вызывается при нажатии на левую кнопку мыши.
   Public Sub MyMouseMove(ByVal sender As System.Object,ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
      ' Запоминаем, что мы обрабатываем движение мыши.
      bHaveMouse = True
      ' Запоминаем "стартовую точку" прямоугольника резиновой ленты.
      ptOriginal.X = e.X
      prOriginal.Y = e.Y
      ' Специальное значение, указывающее на отсутстствие
      ' прямоугольника, который нужно стереть.
      ptLast.X = -1
      ptLast.Y = -1
   End Sub

   ' Вызывается, когда левая кнопка мыши отпускается.
   Public Sub MyMouseUp(ByVal sender As System.Object, ByVal e As System.Windows.MouseEventArgs) Handles MyBase.MouseUp
      ' Устанавливаем внутренний флаг, чтобы знать, что мы более не обрабатываем движение мыши.
      bHaveMouse = False
      ' Если мы рисовали раньше, рисуем еще раз в этой же позиции
      ' для удаления линий.
      if (ptLast.X <> -1) Then
         Dim ptCurrent As Point
         ptCurrent.X = e.X
         ptCurrent.Y = e.Y
         MyDrawReversibleRectangle(ptOriginal, ptLast)
      End If
      ' Устанавливаем флаги, указывающие на отсутствие линий для обращения.
      ptLast.X = -1
      ptLast.Y = -1
      ptOriginal.X = -1
      ptOriginal.Y = -1
   End Sub

   ' Вызывается при перемещении мыши.
   Public Sub MyMouseMove(ByVal sender As System.Object, ByVal e As System.Windows.MouseEventArgs) Handles MyBase.MouseMove
      Dim ptCurrent As Point
      ptCurrent.X = e.X
      ptCurrent.Y = e.Y
      ' Если мы обрабатываем движение мыши, то рисуем наши линии.
      If (bHaveMouse) Then
         ' Если мы рисовали раньше, рисуем еще раз в этой же позиции
         ' для удаления линий.
         If (ptLast.X <> -1) Then
            MyDrawReversibleRectangle(ptOriginal, ptLast)
         End If
         ' Обновляем послюднюю точку
         ptLast = ptCurrent
         ' Рисуем новые линии.
         MyDrawReversibleFrame(ptOriginal, ptCurrent)
      End If
   End Sub

   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      bHaveMouse = False
   End Sub
End Class


Заметьте, что это решение доступно только для вывода на экран. Для рисования обратимых линий на графических объектах вы должны либо взаимодействовать с GDI, либо вызывать Bitmap::LockBits() и непосредственно манипулировать с битами изображения.


Примечания переводчика:

1. Подвигло меня на этот перевод участившиеся топики на тему "как сделать selection tool в VB.NET".
2. Несмотря на то, что указано, что это применимо к VB.NET 2002 и VB.NET 2003, все успешно применяется и в VB.NET 2005 поскольку ничего нового в NET Framework в этой части не появилось.
3. Помимо использования функции ControlPaint.DrawReversibleFrame() можно также использовать функции ControlPaint.DrawReversibleLine() для рисования обратимых отрезков и ControlPaint.FillReversibleRectangle(). А вот сделать что-либо подобное для других фигур (например для круга) невозможно


Примечания модератора:

1. Товарисч, ну не надо же такие хард-брейки текста оставлять...

СообщениеДобавлено: 30.05.2006 (Вт) 15:05
alibek
Хм... Мне интересны мотивы разработчиков, которые исключили растровые операции из функционала .NET...

СообщениеДобавлено: 30.05.2006 (Вт) 15:41
tyomitch
А потому что там пикселей нет, нет и ROP-ов.

СообщениеДобавлено: 30.05.2006 (Вт) 16:08
alibek
Ужас.
А если я хочу выводить что-нибудь с точностью до физического пиксела?

СообщениеДобавлено: 30.05.2006 (Вт) 16:13
tyomitch
А низя. MS всё собирается оградить программистов от особенностей оборудования, таких как разрешение и цветность.

СообщениеДобавлено: 30.05.2006 (Вт) 16:26
alibek
Мдя...
А как мне указать, округлять пикселы как дискретные значения или как метрические?

P.S. Как только соберусь изучать .NET, так обязательно какая-нибудь фигня про него вылезает, начисто отбивающая желание его изучать.

СообщениеДобавлено: 30.05.2006 (Вт) 16:34
Viper
Глянул через ILDisasm как реализованы вышеупомянутые методы DrawReversibleFrame и DrawReversibleLine. Как и предполагалось, там вовсю юзаются такие функции GDI как SetROP, SelectObject, CreatePen и так далее... Видать не судьба дать обычному проггеру возможность доступа к ROP нормальным путем... Самим то можно... :(

СообщениеДобавлено: 30.05.2006 (Вт) 16:43
BV
alibek писал(а):P.S. Как только соберусь изучать .NET, так обязательно какая-нибудь фигня про него вылезает, начисто отбивающая желание его изучать.


Аналогично :)

СообщениеДобавлено: 30.05.2006 (Вт) 17:07
Konst_One
ну его этот .Net , назад к VC++ :lol:

СообщениеДобавлено: 30.05.2006 (Вт) 17:42
FaKk2
alibek
Значит неправильно подходишь к делу :)

СообщениеДобавлено: 30.05.2006 (Вт) 18:17
alibek
FaKk2, знаешь, почему на обычном листе в клетку (с клеткой в 5мм) 2 сантиметра -- это четыре клетки, но пять линий?

СообщениеДобавлено: 30.05.2006 (Вт) 19:10
FaKk2
alibek
Ты интересуешься почему пять линий образуют четыре клетки, или почему четыре клетки это 2 сантиметра, несмотря на пять линий?

СообщениеДобавлено: 30.05.2006 (Вт) 19:19
Amed
Второе.

СообщениеДобавлено: 30.05.2006 (Вт) 19:21
FaKk2
Amed
Наверно, потому что так нарисовали. Или я чего то не улавливаю?

СообщениеДобавлено: 30.05.2006 (Вт) 19:43
alibek
Я наверное непонятно выразился.

Есть принципиальная разница между линейкой и монитором, на котором нарисована линейка. На мониторе имеются физические пикселы (дискретная единица), которые отображают метрическую величину.
Поэтому на сантиметровой линейке на отрезке между 0 и 2 сантиметра находится 21 миллиметровое деление, а не 20 (хотя этот отрезок имеет длину 20 мм).
Поэтому для меня в некоторых случаях очень важно иметь доступ к физическим пикселам.

Хм... Перечитал, все-равно непонятно выразился.
Ладно, попробую еще раз, когда подумаю над формулировкой.
Но доступ к физическим пикселам важен. По крайней мере до тех пор, пока не появились мониторы на 300 DPI, в которых отдельные пикселы будут не различимы глазом.

СообщениеДобавлено: 30.05.2006 (Вт) 20:28
FaKk2
Понятно.

СообщениеДобавлено: 31.05.2006 (Ср) 1:54
tyomitch
alibek, зато GDI+ обещает позволять одним и тем же кодом рисовать на мониторе и на плоттере :-D
Плюс, всё нарисованное может быть сохранено в EMF+-метафайл, и потом воспроизведено в любом устройстве, на любом разрешении.
Наверное поэтому же они, черти, запретили FloodFill :twisted: