Invalidate c что это
Определения обработчиков достаточно стандартны:
protected void OpenFileDialog FileOk(object Sender, CancelEventArgs e)
protected void menuFileOpen Click(object sender, EventArgs e) fileOpenDialog.ShowDialog();
protected void menuFileExit Click(object sender, EventArgs e) Close();
Далее рассмотрим метод LoadFile(). Он обрабатывает открытие и чтение файла (а также обеспечивает возбуждение события Paint для принудительной перерисовки с новым файлом).
private void LoadFile(string FileName)
StreamReader sr = new StreamReader(FileName); string nextLine; documentLines.Clear(); nLines = 0;
TextLineInformation nextLineInfo; while ( (nextLine = sr.ReadLine()) != null)
nextLineInfo = new TextLineInformation(); nextLineInfo.Text = nextLine; documentLines.Add(nextLineInfo); ++nLines;
documentHasData = (nLines>0) ? true : false; CalculateLineWidths();
Text = standardTitle + — + FileName;
Большая часть этой функции представляет собой просто стандартный код чтения файла (см. главу 25). Отметим, что после того, как файл открыт, мы последовательно добавляем его строки в ArrayList по имени documentLines, поэтому в итоге этот массив содержит все строки файла в том порядке, как они были прочитаны. После считывания файла устанавливается флаг documentHasData, который говорит о том, есть ли в документе содержимое, подлежащее отображению. Следующая задача — вычислить, где именно все должно отображаться, а, покончив с этим, потребуется определить, какой размер клиентской области понадобится для отображения всего файла — т.е. размер документа, необходимый для настройки линеек прокрутки. И, наконец, устанавливается текст заголовка и вызывается Invalidate(). Это один из важнейших методов, предоставленных Microsoft, поэтому в следующем разделе мы первым делом поговорим о нем, прежде чем рассматривать код методов CalculateLineWidths() и CalculateDocumentSize().
Invalidate() — член класса System.Windows.Forms.Form. Он помечает клиентскую область окна как недействительную, а потому подлежащую перерисовке, и затем убеждается в том, что было возбуждено событие Paint. Метод Invalidate() имеет пару перегрузок: можно передать ему прямоугольник, указывающий точно (в страничных координатах) позицию и размер перерисовываемой области, или же, если не передавать никаких параметров, то недействительной помечается вся клиентская область.
Если нам известно, что какая-то часть требует перерисовки, почему нельзя просто вызвать OnPaint() либо какой-то другой метод, который выполнит ее непосредственно? Ответ состоит в том, что вообще прямой вызов процедур рисования считается дурным тоном программирования. Если ваш код решает что-то перерисовать, он должен вызвать Invalidate(). Ниже описаны причины.
□ Перерисовка — это почти всегда наиболее ресурсоемкая задача приложения GDI+, а потому выполнение ее посреди другой работы задерживает ее ход. В данном примере, если бы мы напрямую вызвали метод, выполняющий перерисовку из LoadFile(), это означало бы, что LoadFile() не вернет управление до тех пор, пока не завершится рисование. В это время наше приложение не могло бы реагировать ни на какие другие события. С другой стороны, вызов Invalidate() просто заставляет Windows возбудить событие Paint перед тем, как немедленно вернуть управление из LoadFile(). После этого Windows может свободно просматривать события, подлежащие обработке. Внутреннее устройство этого механизма таково, что события в виде сообщений находятся в специальной очереди сообщений. Windows периодически просматривает эту очередь, и если в ней есть сообщения, выбирает одно из них и вызывает соответствующий обработчик события. Хотя событие Paint может быть единственным, находящимся в очереди (поэтому OnPaint() будет вызван немедленно), в более сложных приложениях очередь может содержать и другие события, чей приоритет выше, чем у нашего события Paint. В частности, если пользователь решит выйти из приложения, то это будет помечено как сообщение, известное под именем WM QUIT.
□ Если у вас есть более сложное, многопоточное приложение, вероятно, вы решите, чтобы только один его поток обрабатывал рисование. Применение Invalidate() для маршрутизации всех событий рисования через очередь сообщений обеспечивает хорошую возможность гарантировать, что один и тот же поток выполнит все рисование, независимо то того, какой поток его потребовал. (Это будет любой поток, отвечающий за очередь сообщений — тот, который запустил Application.Run().)
□ Существует еще одна причина, связанная с производительностью. Предположим, что есть два разных запроса на рисование части экрана, которые поступают почти одновременно. Может быть, код только что модифицировал документ и желает гарантировать его отображение, в то время как пользователь только что переместил другое окно, которое накрывало часть клиентской области нашего окна. За счет вызова Invalidate() мы предоставляем для Windows шанс узнать об этом. Затем Windows может объединить эти два события Paint, комбинируя недействительные области, чтобы собственно рисование произошло только один раз.
□ Код, ответственный за перерисовку, вероятно, будет одним из сложнейших частей приложения, особенно если используется изощренный пользовательский интерфейс. Люди, которым придется сопровождать наш код в течение ряда лет, будут благодарны, если мы поместим весь код рисования в одном месте и сделаем его насколько возможно простым — иногда это легче сделать, если к нему ведет не слишком много путей из других частей программы.
В результате всего этого приходим к выводу, что лучше сосредоточить весь код рисования в процедуре OnPaint() либо в других методах, вызванных из данного. Однако необходимо сохранять разумный баланс: если вы захотите заменить единственный символ на экране, и вы точно знаете, что это не затронет ничего другого, что уже было нарисовано, то вы можете принять решение, что не стоит перегружать систему дополнительным Invalidate(), а лучше просто написать отдельную процедуру рисования.
В очень сложных приложениях вы можете даже написать целый класс, который отвечает за рисование экрана. Несколько лет назад, когда MFC б1ла стандартной технологией разработки GDI-ориентированных приложений, библиотека MFC следовала именно такой модели — с применением класса C++ CView, отвечавшего за перерисовку. Однако даже в этом случае класс имел одну функцию-член OnDraw(), которая служила точкой входа для большинства запросов рисования.
Вычисление размеров элементов и размеров документа
В этом разделе мы вернемся к примеру CapsEditor и рассмотрим методы CalculateLineWidths() и CalculateDocumentSize(), вызываемые из LoadFile().
private void CalculateLineWidths()
Graphics dc = this.CreateGraphics();
foreach (TextLineInformation nextLine in documentLines)
nextLine.Width = (uint)dc.MeasureString(nextLine.Text, mainFont).Width;
Этот метод просто проходит по каждой прочитанной строке и использует метод Graphics.MeasureString() для вычисления того, сколько горизонтального пространства экрана ей требуется. Это значение сохраняется для последующего применения, потому что MeasureString() — дорогостоящая по вычислительным ресурсам операция. Если пример CapsEditor недостаточно прост, чтобы легко обработать высоту и местоположение каждого элемента, этот метод почти наверняка придется реализовать, чтобы также вычислить все эти величины.
Теперь, когда мы знаем размер каждого элемента на экране и можем вычислить местоположение каждого из них, у нас есть возможность получить реальный размер документа. Его высота определяется как количество строк, умноженное на высоту каждой строки. Ширина должна быть найдена в процессе итерации по всем строкам, чтобы найти самую длинную. Как высоту, так и ширину следует дополнить небольшими полями, чтобы документ выглядел более привлекательно.
Ниже приведен код метода, вычисляющего размер документа.
private void CalculateDocumentSize()
documentSize = new Size(100, 200);
Функция InvalidateRect (winuser.h)
Функция InvalidateRect добавляет прямоугольник в область обновления указанного окна. Область обновления представляет часть клиентской области окна, которую необходимо перерисовыть.
Синтаксис
BOOL InvalidateRect( [in] HWND hWnd, [in] const RECT *lpRect, [in] BOOL bErase );
Параметры
Дескриптор для окна, область обновления которого была изменена. Если этот параметр имеет значение NULL, система делает недействительными и перерисовывает все окна, а не только окна для этого приложения, и отправляет сообщения WM_ERASEBKGND и WM_NCPAINT перед возвратом функции. Не рекомендуется задавать для этого параметра значение NULL .
Указатель на структуру RECT , содержащую клиентские координаты прямоугольника, добавляемого в область обновления. Если этот параметр имеет значение NULL, в регион обновления добавляется вся клиентская область.
Указывает, следует ли удалять фон в регионе обновления при обработке области обновления. Если этот параметр имеет значение TRUE, фон удаляется при вызове функции BeginPaint . Если этот параметр имеет значение FALSE, фон остается неизменным.
Возвращаемое значение
Если функция выполняется успешно, возвращается ненулевое значение.
Если функция выполняется неудачно, возвращается нулевое значение.
Комментарии
Недействительные области накапливаются в области обновления до тех пор, пока регион не будет обработан при следующем сообщении WM_PAINT или пока регион не будет проверен с помощью функции ValidateRect или ValidateRgn .
Система отправляет WM_PAINT сообщение в окно всякий раз, когда его область обновления не пуста и в очереди приложений для этого окна нет других сообщений.
Если параметр bErase имеет значение TRUE для любой части области обновления, фон удаляется во всем регионе, а не только в указанной части.
Примеры
Требования
| Минимальная версия клиента | Windows 2000 Professional [только классические приложения] |
| Минимальная версия сервера | Windows 2000 Server [только классические приложения] |
| Целевая платформа | Windows |
| Header | winuser.h (включая Windows.h) |
| Библиотека | User32.lib |
| DLL | User32.dll |
| Набор API | ext-ms-win-ntuser-draw-l1-1-0 (появилось в Windows 8) |
Invalidate();
— Скомпилировать приложение и запустить его на выполнение.
— Проверить алгоритм работы приложения-контейнера ActiveX: работу диалогового окна с кнопкой и текстовым полем с выводом строки.
— Вставить в контейнер диаграмму Excel.
Для этого запустить приложение Cont.
Выбрать команду Edit->Insert New Object. Появится диалоговое окно для внедрения или связывания объектов.
Выбрать новый объект Диаграмма Microsoft Excel и нажать OK. Диаграмма будет вставлена в контейнер.
Для отмены редактирования на месте нажать Esc. Диаграмма уменьшится и сдвинется влево. Все объекты помещаются в прямоугольник 200×200 пикселей.
labServ «Создание приложения-сервера»
Цель работы — создание приложения в виде сервера ActiveX. В качестве задачи возьмем приложение labCont, посвященное созданию диалогового окна с кнопкой и текстовым полем.
Для создания приложения–сервера ActiveX необходимо выполнить следующие этапы.
Создание проекта
Создать новый проект: задать имя проекта labServ, на 1-ом этапе выбрать MDI-приложение, на 2-ом этапе выбрать Full server, остальные – без изменений по умолчанию, щелкнув на Finish.
Сравнить меню приложения labServ с приложением lab3_2:
— Вызвать окно View->Resource View и открыть папку labServ.rc, затем — Menu. Появились два новых меню. Первое меню IDR_labServTYPE_SRVR_IP (SRVR – это согласные от слова server, IP – (in place) редактирование на месте). Это означает, что объект, содержащийся в контейнере, может быть отредактирован на месте. Для редактирования объединяются меню контейнера (например, из п.12.2 IDR_labContTYPE_CNTR_IP) и первое меню сервера. Второе меню IDR_labServTYPE_ SRVR_EMB (EMB – (embedded) встроенный) используется для редактирования в отдельном окне сервера.
— Щелкнуть дважды на втором меню. Появляется окно с командами нового меню. Это окно будет выводиться во время редактирования объекта в отдельном окне, т.е. в окне сервера. Открыть меню File. Появились новые 2 команды : Update вместо Save – модификация объекта; Save Copy As вместо Save As – копирование изменений объекта в файл, а не сохранение всего документа. Объект считается не самостоятельным документом, а объектом, внедренным в другой документ.
Включение новой команды Dialog в меню
— Открыть редактор меню, щелкнув на команде View->Resource View и открыв папку labServ.rc, затем – Menu. Дважды щелкнуть на строке IDR_labServTYPE. Выводятся команды нового меню.
— Добавить новую команду Dialog. Новая команда должна быть между командами View и Windows. Щелкнуть на пустой команде Type Here, ввести строку Dialog и нажать Enter. Перетащить с помощью мыши команду меню Dialog в позицию после View. Открыть окно Properties для новой команды меню Dialog, щелкнув сначала правой кнопкой на пункте Dialog, а затем — на Properties. Проверить наличие в свойстве Caption значения Dialog. Установить свойству Popup значение false и проверить идентификатор ID_Dialog.
— Подключить команду Dialog к приложению. Для этого открыть редактор меню, щелкнув на вкладке Resource View и открыв папку labServ.rc, затем – Menu. Дважды щелкнуть на строке IDR_labServTYPE. Выводятся команды меню. Щелкнуть правой кнопкой на команде Dialog и в открывшемся контекстном меню выбрать команду Add Event Handler (добавить обработчик события). Откроется окно Event Handler Wizard – labServ. Выбрать в списке Message Type сообщение Command, а в списке Class list – ClabServView. В поле Function handler name появится имя функции-обработчика OnDialog(). Щелкнуть на кнопке Add and Edit, и в окне редактора кода откроется шаблон созданной функции.
Создание ресурса диалогового окна
— Создать шаблон диалогового окна. Для этого выполнить команду Project-> Add Resource и в появившемся окне Add Resource дважды щелкнуть на элементе Dialog в списке Resource Type (или выбрать Dialog и щелкнуть на New). Появляется шаблон диалогового окна с именем IDD_DIALOG1 и панели инструментов Dialog Editor и Toolbox. В окне имеются 2 кнопки: OK и Сancel.
— Установить язык Русский. Для этого в окне Resource View открыть папку Dialog и щелкнуть на идентификаторе IDD_DIALOG1. В появившемся окне свойств Properties щелкнуть на свойстве Language и в списке справа выбрать значение Русский.
— Добавить в диалоговое окно элементы управления: текстовое поле и кнопку. Щелкнуть сначала на инструменте Edit Control (текстовое поле) панели инструментов Toolbox, а затем — в выбранной области диалогового окна. Появляется текстовое поле с надписью Sample edit box. Изменить размеры текстового поля, щелкнув в текстовом поле и пользуясь появившимися маркерами размеров (рамка с квадратами). Аналогично добавить кнопку, щелкнув на инструменте Button (кнопка), а затем в области диалогового окна. Появляется кнопка с надписью Button1.
— Изменить надписи элементов управления. Требуется изменить надпись кнопки Button1 на Output. Для изменения свойств диалогового окна открыть окно Properties (свойства), щелкнув правой кнопкой в свободной области окна, затем – на Properties. Щелкнуть на кнопке и ввести в качестве свойства Caption текст Output. Новая надпись вводится в кнопку.
— Проверить в свойстве ID идентификатор IDC_BUTTON1, присвоенный кнопке редактором диалоговых окон. Аналогично проверить идентификатор IDC_EDIT1, присвоенный текстовому полю.
Создание класса диалогового окна
— Открыть шаблон диалогового окна при отсутствии его. Для этого щелкнуть на вкладке Resource View или выполнить команду View->Resource View и в появившемся окне Resource View открыть папку labCont.rc, затем – Dialog и дважды щелкнуть на идентификаторе IDD_DIALOG1.
— Щелкнуть правой кнопкой мыши на пустом месте диалогового окна и выполнить команду контекстного меню Add Class. Откроется окно мастера MFC Class Wizard.
— Ввести в поле Class name имя нового класса CDlg, в списке Base class выбрать имя базового класса диалоговых окон CDialog, проверить наличие в поле Dialog ID идентификатора IDD _ DIALOG1, и затем щелкнуть на кнопке Finish.
Связывание элементов управления диалогового окна с
функциями-обработчиками сообщений
Требуется связать сообщение кнопки Output с функцией – обработчиком.
— Открыть окно Class View и щелкнуть правой кнопкой на имени класса CDlg. Появится окно свойств Properties.
— Щелкнуть на кнопке Event (с изображением желтой молнии) и в появившемся списке идентификаторов раскрыть узел IDC_ BUTTON1 (щелкнуть на символе +). Появляется список сообщений
— Выбрать элемент BN_CLICKED, а в комбинированном списке справа от идентификатора щелкнуть на команде OnBnClickedButton1. Прототип новой функции-обработчика OnBnClickedButton1() будет добавлен в файл Dlg.h, а ее текст – в файл Dlg.cpp. Функция OnBnClickedButton1() будет вызываться при нажатии кнопки Output.
Связывание переменных класса с элементами окна
— Запустить мастер создания переменных класса Add Member Variable Wizard. Для этого открыть окно Class View и выделить имя класса CDlg. Затем выполнить команду Project->Add Variable. Ибо щелкнуть правой кнопкой на имени класса Dlg и выполнить команду Add Add->Variable.
— В появившемся окне Add Member Variable Wizard-labServ выполнить следующее: установить флажок Control variable; в списке Control ID выбрать идентификатор текстового поля IDC_EDIT1; в списке Category выбрать значение value; в списке Variable type появится CString; ввести в поле Variable name строку m_text; в списке Access проверить наличие элемента public; и наконец, щелкнуть на кнопке Finish. Появляется содержимое файла Dlg.cpp с функциями класса CDlg.
— Задать содержимое текстового поля в функции-обработчике OnBnClickedButton1():
void CDlg :: OnBnClickedButton1 ()
m_text = “Диалоговое окно”;
— Обновить текстовое поле. Для чтения или записи содержимого элемента используется вызов функции UpdateData ():
void CDlg :: OnBnClickedButton1 ()
m_text = “Диалоговое окно”;
UpdateData (false);
Вызов функции UpdateData(false) с параметром false заносит в текстовое поле значение переменной m_text, вызов с параметром true присваивает переменной m_text содержимое текстового поля. Итак, строка перенесена в текстовое поле.
Переопределение функции для кнопки ОК
— Связать сообщение кнопки ОК с функцией-обработчиком.
Для этого открыть окно Class View и щелкнуть на имени класса CDlg. В окне свойств Properties щелкнуть на кнопке Event (События) и в появившемся списке идентификаторов раскрыть узел IDOK (щелкнуть на символе +). Появляется список сообщений. Выбрать элемент BN_CLICKED, а в комбинированном списке справа от идентификатора щелкнуть на команде OnBnClickedOk. Прототип новой функции-обработчика OnBnClickedOk() будет добавлен в файл Dlg.h, а ее текст – в файл Dlg.cpp. Функция OnBnClickedOk() будет вызываться при нажатии кнопки OK. На экране появится текст функции-обработчика OnBnClickedOk ().
— Занести в переменную m_text содержимое текстового поля. Для этого вызывается функция UpdateData (true):
void CDlg :: OnBnClickedOk ()
UpdateData (true);
Отображение диалогового окна
— Сообщить классу вида о существовании класса диалогового окна. Для этого включить в него файл заголовков класса CDlg — Dlg.h:
# include “Dlg.h”
— Создать объект класса Dlg в классе вида:
void ClabServView :: OnDialog ()
— Отобразить диалоговое окно на экране. Для этого вызвать функцию DoModal():
void ClabServView :: OnDialog ()
int result = dlg. DoModal(); //вызов диалогового окна и возврат
— Проверить нажатие кнопки ОК:
void ClabServView :: OnDialog ()
int result = dlg. DoModal();
if (result = = IDOK)
Получить указатель на документ:
void ClabServView :: OnDialog ()
if (result == IDOK)
ClabServDoc* pDoc = GetDocument();
ASSERT_VALID (pDoc);
— Создать в документе объект класса c именем StringData:
class ClabServDoc :: public COleServerDoc
CString StringData;
— Поместить текст из переменной m_text в объект StringData:
void ClabServView :: OnDialog ()
if (result == IDOK)
pDoc -> StringData = dlg. m_text;
— Предусмотреть вызов Invalidate(). Вывод данных – в функции OnDraw (). Но сначала надо предусмотреть стирание прежней строки перед перерисовкой новой. Для этого – вызов функции класса вида Invalidate(). Эта функция и вызывает OnDraw ().
void ClabServView :: OnDialog ()
pDoc -> StringData = dlg. m_text;
Invalidate() — для чего он нужен?

Ребят, подскажите. Я уверен, что заблуждаюсь (иначе не было бы этого метода), но зачем он нужен, если есть Paint у PictureBox ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
public partial class Form1 : Form { Bitmap bmp; int p; public Form1() { InitializeComponent(); bmp = new Bitmap(canva.Width, canva.Height); p = 0; } public Bitmap draw(Bitmap bmp, int p) { for (int i = 0; i 20; i++) for (int j = 0; j 20; j++) { switch (p) { case 0: bmp.SetPixel(i + 1, j + 1, Color.Black); break; case 1: bmp.SetPixel(i + 1, j + 1, Color.Red); break; } } return bmp; } private void button2_Click(object sender, EventArgs e) { if (p == 0) p = 1; else p = 0; //canva.Invalidate(); в любом случае вызывается Paint } protected override void OnPaint(PaintEventArgs e) { //base.OnPaint(e); //canva.Image = draw(bmp, p); } private void canva_Paint(object sender, PaintEventArgs e) { /* if (p == 0) p = 1; else p = 0; */ canva.Image = draw(bmp, p); } }
И еще пару вопросов:
1) для чего используется base.OnPaint(e) ?
2) Как перерисовывать только область PictureBox? C помощью Invalidate,где в качестве параметра передавать область PictureBox ?