Чтение онлайн

ЖАНРЫ

C# для профессионалов. Том II

Ватсон Карли

Шрифт:

Преобразования координат

В этом разделе мы рассматриваем реализацию вспомогательных методов, которые были использованы в примере

CapsEditor
, чтобы выполнить преобразование координат. Это методы
WorldYCoordinateToLineIndex
и
LineIndexToWorldCoordinates
, на которые мы ссылались в предыдущем разделе, а также некоторые другие методы.

Первое.

LineIndexToWorldCoordinates
получает заданный индекс строки и определяет мировые координаты верхнего левого угла строки с помощью известных ширины поля и высоты строки:

private Point LineIndexToWorldCoordinates(int index) {

 Point TopLeftCorner =

new Point((int)margin, (int)(lineHeight*index + margin));

 return TopLeftCorner;

}

Мы

также используем метод, который делает приблизительно обратное преобразование в
OnPaint
.
WorldYCoordinateToLineIndex
определяет индекс строки, но он принимает в расчет только вертикальную мировую координату. Это связано с тем, что метод используется для определения индекса строки, соответствующего верху и низу области вырезания:

private int WorldYCoordinateToLineIndex(int у) {

 if (у < margin) return -1;

 return (int)((y - margin)/lineHeight);

}

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

WorldYCoordinateToLineIndex
этот метод берет в расчет позиции x- и y- координат. Он возвращает -1, если нет строки текста с заданными координатами.

private int WorldCocrdinatesToLineIndex(Point position) {

 if (!documentHasData) return -1;

 if (position.Y < margin || position.X < margin) return -1;

 int index = (int) (position.Y - margin) / (int) this.lineHeight;

 // проверить, что позиция находится не ниже документа

 if (index >= documentLines.Count) return -1;

 // теперь проверим, что горизонтальная позиция располагается

 // внутри строки

 TextLineInformation theLine =

(TextLineInformation)documentLines[index];

 if (position.X > margin * theLine.Width)

 return -1;

 // все хорошо. Можно вернуть ответ.

 return index;

}

Наконец, иногда необходимо делать преобразование между индексом строки и координатами страницы, а не мировыми координатами. Это делает следующий метод:

private Point LineIndexToPageCoordinates(int index) {

 return LineIndexToWorldCoordinates(index) + new Size(AutoScrollPosition);

}

private int PageCoordinatesToLineIndex(Point position) {

 return WorldCoordinatesToLineIndex(position - new Size(AutoScrollPosition));

}

Эти методы сами по себе не кажутся особенно интересными, они иллюстрируют общую технику, которую, по всей видимости, вам придется часто использовать. Применяя GDI+, мы иногда оказываемся в ситуации где заданы некоторые координаты (например, координаты места, где пользователь щелкнул мышью) и требуется определить, какой элемент изображен в этом месте. Или наоборот, для заданного определенного элемента вывода необходимо приблизительно определить, где он должен быть выведен. Следовательно, при создании приложений GDI+ может оказаться полезным умение написать методы, эквивалентные методам преобразований координат, проиллюстрированным здесь.

Ответ на ввод пользователя

До сих пор, за исключением пеню File в примере

CapsEditor
, все, что делались в этой главе, было односторонним, так как приложение общалось с пользователем, выводя информацию на экране. Почти любое программное обеспечение работает в обоих направлениях — пользователь также может общаться с программным обеспечением. Мы собираемся теперь добавить эту возможность в
CapsEditor
.

Заставить приложение GDI+ ответить на пользовательский ввод в действительности значительно проще, чем писать код для рисования на экране, и мы в главе 9 уже рассмотрели, как обрабатывать ввод пользователя. По сути для этого необходимо переопределить методы из класса

Form
, которые вызываются из соответствующего обработчика событий почти так же, как вызывается
OnPaint
, когда инициируется событие
Paint
.

Для случая обнаружения, когда пользователь щелкнул мышью или переместил мышь, функции которые можно будет переопределить включают следующие:

Метод Когда вызывается
OnClick(EventArgs
е)
Сделан щелчок мышью
OnDoublеСlick(EventArgs е)
Сделан двойной щелчок мышью
OnMouseDown(MouseEventArgs е)
Нажата левая кнопка мыши
OnMouseHover(MouseEventArgs е)
Мышь остается неподвижной после перемещения
OnMouseMove(MouseEventArgs е)
Мышь перемещается
OnMouseUp(MouseEventArgs e)
Левая кнопка мыши отпущена

Если требуется определить, когда пользователь вводит с клавиатуры какой-то текст, то, вероятно, можно будет переопределить следующие методы.

OnKeyDown(KeyEventArgs е)
Клавиша нажата
OnKeyPress(KeyPressEventArgs е)
Клавиша нажата и отпущена
OnKeyUp(KeyEventArgs e)
Нажатая клавиша отпущена

Отметим, что некоторые из этих событий перекрываются. Например, нажатие пользователем кнопки мыши порождает событие

MouseDown
. Если кнопка немедленно снова освобождается, то это порождает событие
MouseUp
и событие
Click
. Также некоторые из этих методов получают аргумент, который выводится из аргумента
EventArgs
и поэтому может использоваться для предоставления дополнительных данных об определенном событии.
MouseEventArgs
имеет два свойства —
X
и
Y
, которые задают координаты мыши во время нажатия кнопки.
KeyEventArgs
и
KeyPressEventArgs
имеют свойства, указывающие, какая клавиша или клавиши имеют отношение к событию.

Затем разработчик должен продумать логику последующих действий. Только одно замечание. Вполне вероятно, что приложение GDI+ потребует создания большего объема логики, чем приложение

Windows.Forms
. Это связано с тем, что в приложении
Windows.Forms
приходится отвечать на высокоуровневые события (например,
TextChanged
для текстового поля). При использовании GDI+ события являются более базовыми — пользователь щелкает мышью или нажимает клавишу h. Действие, которое предпринимает приложение, скорее всего зависит от последовательности событий, а не от одного события. Например, в Word for Windows, чтобы выбрать некоторый текст, пользователь обычно щелкает левой кнопкой мыши, перемещает мышь и освобождает левую кнопку мыши. Если пользователь просто нажмет, а затем освободит левую кнопку мыши, Word не выделит никакой текст, он просто переместит курсор текста в место, где был курсор мыши. Поэтому в точке, где пользователь нажимает левую кнопку мыши, нельзя еще сказать, что пользователь собирается делать. Приложение будет получать событие
MouseDown
, но, предположим, что требуется, чтобы приложение вело себя так же, как это делает Word for Windows. Нам ничего не остается, кроме как записать, что произошел щелчок мыши с курсором в определенной позиции. Когда будет получено событие
MouseMove
, вы захотите проверить в только что сделанных записях, не нажата ли в данный момент левая кнопка, и если это так, то выделить текст, поскольку пользователь его выбрал. Когда пользователь освобождает левую кнопку мыши (в методе
OnMouseUp
), происходит проверка, было ли выполнено какое-либо перемещение, пока мышь была нажата, а далее нужно действовать соответственно. Только в этой точке последовательность завершается.

Другой момент нашего рассмотрения связан с перекрытием некоторых событий, так как в этой ситуации возникает необходимость выбора события, на которое должен ответить код.

Золотое правило состоит в тщательном продумывании логики каждой комбинации движения мыши или щелчка и события клавиатуры, которое может инициировать пользователь, а также обеспечении того, чтобы приложение отвечало способом, который интуитивно понятен и согласован с обычно ожидаемым поведением приложений в каждом случае. Большая часть работы здесь будет состоять в обдумывании, а не в кодировании, хотя кодирование потребуется достаточно кропотливое, поэтому может понадобиться принять во внимание множество комбинаций ввода пользователя. Например, что должно делать приложение, если пользователь начинает вводить текст, когда одна из кнопок мыши нажата? Это покажется невероятной комбинацией, но рано или поздно какой-то пользователь попробует так сделать.

Поделиться с друзьями: