Главная страница
Случайная страница
КАТЕГОРИИ:
АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Упражнение 3. Применение объекта ImageDrawing
Объект ImageDrawing принимает графические данные в виде битовой карты (точечного рисунка). Для создания изображения необходимо создать экземпляр ImageDrawing и установить значения его свойств ImageDrawing.ImageSource и ImageDrawing.Rect. Свойство ImageDrawing.ImageSource задает изображение для рисования, а свойство ImageDrawing.Rect задает положение и размер каждого изображения.
Если нужно отобразить один рисунок, то его сразу можно присоединить к объекту отображения Image, как мы это делали в предыдущем упражнении. Если же требуется нарисовать несколько рисунков сразу, то необходимо организовать конвейер формирования рисунков. Вначале создаются отдельные слои с помощью ImageDrawing, затем они передаются в накопитель DrawingGroup, где располагаются в Z -последовательности в порядке их добавления. В таком порядке каждый новый слой будет перекрывать все предыдущие и располагаться ближе к пользователю. Затем этот слоеный объект передается в рисовальщик DrawingImage, который и присоединяется, в конечном итоге, к элементу отображения Image.
Объект DrawingGroup как составной объект рисования, может принимать не только точечные рисунки. В первом упражнении мы использовали его для накопления векторных рисунков геометрии, порожденных объектом GeometryDrawing. Он также способен принимать текстовые данные от GlyphRunDrawing, или медийные данные звука и видео от объекта VideoDrawing. Объект DrawingGroup является единственным типом базового объекта Drawing, который позволяет определять свою собственную область отсечения. Но об этом чуть позже, а сейчас приступим к рассмотрению объекта ImageDrawing.
Класс ImageDrawing легко спутать с важным классом DrawingImage, названия которых так похожи. Но это нас не должно путать, если мы представим изготовление готового рисунка как последовательность операций на конвейере. Это будет выглядеть примерно так:
GeometryDrawing
| ->
| DrawingGroup
| ->
| DrawingImage
| ->
| Image
| ImageDrawing
| GlyphRunDrawing
| VideoDrawing
| - Добавьте к решению проект типа WPF Application с именем WpfApp3 и назначьте его стартовым
- Добавьте к проекту командой Project/New Folder папку с именем Images
- В панели Solution Explorer вызовите контекстное меню для папки Images и скопируйте в нее командой Add/Existing Item из прилагаемого каталога Source пять файлов с рисунками (не забудьте изменить фильтр диалогового окна Add Existing Item на All Files):
- market 031.jpg
- market 032.jpg
- market 034.jpg
- market 039.jpg
- market 040.jpg
- В панели Solution Explorer выделите все пять рисунков одновременно и в панели Properties установите для них свойства
- Build Action=Content
- Copy to Output Directory=Copy if newer
В этом упражнении мы рассмотрим два способа использования объекта ImageDrawing - с помощью разметки на XAML и с помощью кода C#. Оба способа работают совершенно одинаково и для того, чтобы это подчеркнуть, мы с помощью них реализуем одну и ту же задачу. Попутно рассмотрим решение некоторых мелких вопросов, таких, как предотвращение повторного запуска созданного окна приложения, назначение главного окна и формирование всплывающей подсказки.
Вначале создадим главное окно приложения Window1, в котором примененим объект ImageDrawing в кодовой части Window1.xaml.cs. Затем построим дочернее окно Window2, в котором используем объект ImageDrawing в дискрипторной части Window2.xaml.
- Заполните файл разметки Window1.xaml следующим дескрипторным кодом
< Window x: Class=" WpfApp3.Window1" xmlns=" https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns: x=" https://schemas.microsoft.com/winfx/2006/xaml" Title=" Главное окно Window1: Работа объекта ImageDrawing через процедурный код" Loaded=" Window_Loaded" MouseDoubleClick=" Show_Window2" SizeToContent=" WidthAndHeight" ResizeMode=" NoResize" > < Window.ContextMenu> < ContextMenu> < MenuItem Header=" Создать дочернее окно" Click=" Create_Window2" /> < /ContextMenu> < /Window.ContextMenu> < /Window> Эта разметка настраивает окно и создает контекстное меню с одним пунктом. Вызов дочернего окна, которое будет иллюстрировать разметочный способ применения объекта ImageDrawing, предусмотрим в обработчиках событий MouseDoubleClick окна и Click контекстного меню. Такое дублирование одной и той же задачи создания дочернего окна выбрано потому, чтобы испытать разные способы предотвращения повторного запуска уже существующего окна.
Событие Loaded срабатывает после загрузки окна в оперативную память, поэтому в его обработчик удобно поместить код создания объектов содержимого окна. Атрибуты SizeToContent и ResizeMode дескриптора < Window> делают окно подстраиваемым под содержимое (при первом появлении) и неизменяемое в размерах пользователем, соответственно. Контекстное меню прикрепляется к объекту окна и будет вызываться в любой его точке, кроме заголовка.
- Щелкните правой кнопкой мыши на каждом из событий Loaded, MouseDoubleClick, Click и выполните команду Navigate to Event Handler, чтобы создать в кодовой части обработчики с уже заготовленными именами
- В файле Window1.xaml.cs заполните обработчик события Loaded следующим кодом
private void Window_Loaded(object sender, RoutedEventArgs e){ // Размеры всех рисунков одинаковы const int WIDTH = 348, HEIGHT = 232; // Создаем накопитель рисунков DrawingGroup DrawingGroup drawingGroup = new DrawingGroup(); // Левый верхний ImageDrawing pict1 = new ImageDrawing(); pict1.Rect = new Rect(0, 0, WIDTH, HEIGHT); pict1.ImageSource = new BitmapImage(new Uri(@" Images\market 040.jpg", UriKind.Relative)); drawingGroup.Children.Add(pict1); // Правый верхний ImageDrawing pict2 = new ImageDrawing(); pict2.Rect = new Rect(350, 0, WIDTH, HEIGHT); pict2.ImageSource = new BitmapImage(new Uri(@" Images\market 039.jpg", UriKind.Relative)); drawingGroup.Children.Add(pict2); // Левый нижний ImageDrawing pict3 = new ImageDrawing(); pict3.Rect = new Rect(0, 234, WIDTH, HEIGHT); pict3.ImageSource = new BitmapImage(new Uri(@" Images\market 034.jpg", UriKind.Relative)); drawingGroup.Children.Add(pict3); // Правый нижний ImageDrawing pict4 = new ImageDrawing(); pict4.Rect = new Rect(350, 234, WIDTH, HEIGHT); pict4.ImageSource = new BitmapImage(new Uri(@" Images\market 032.jpg", UriKind.Relative)); drawingGroup.Children.Add(pict4); // Передать рисовальщику DrawingImage drawingImageSource = new DrawingImage(drawingGroup); // Заморозить DrawingImage для лучшей производительности drawingImageSource.Freeze(); // Передать элементу отображения Image image = new Image(); image.Stretch = Stretch.None; image.Source = drawingImageSource; // Контейнер Border для присоединения к содержимому окна Border border = new Border(); border.Background = Brushes.White; border.BorderBrush = Brushes.White; border.BorderThickness = new Thickness(2); // Толщина рамки border.Margin = new Thickness(10); // Внешний отступ-поле border.Child = image; // Отдать родителю this.Background = Brushes.Blue; this.Content = border; } - Дополните конструктор окна Window1 следующим кодом
public Window1(){ // Инициализация разметочной части InitializeComponent(); // Корректировка заголовка окна this.Title += " =\" Так голодают буржуины! \" "; // Всплывающая подсказка this.ToolTip = " Вызывайте дочернее окно\n" + " двойным щелчком мыши\n" + " или контекстным меню..."; } Здесь применено экранирование двойных кавычек в строковой константе. В определении всплывающей подсказки использован управляющий символ новой строки.
- Запустите приложение - первое окно будет выглядеть так
увеличить изображение
Обратите внимание, что окно не имеет системных кнопок минимизации и максимизации, а только кнопку закрытия. Это поведение обеспечивается настройкой в разметке окна с помощью атрибута ResizeMode=" NoResize".
При наведении курсора на окно работает всплывающая подсказка, напоминающая способы создания дочернего окна. Но пока обработчики, предназначенные для этой функциональности, у нас пустые, да и само окно еще нужно заготовить.
- Добавьте к текущему (выделенному в панели Solution Explorer) проекту WpfApp3 командой Project/Add Window новое окно WPF с именем Window2.xaml
- Наполните файл Window2.xaml следующей разметкой
< Window x: Class=" WpfApp3.Window2" xmlns=" https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns: x=" https://schemas.microsoft.com/winfx/2006/xaml" Title=" Дочернее окно Window2: Работа объекта ImageDrawing через разметку" SizeToContent=" WidthAndHeight" ResizeMode=" NoResize" Background=" Green" > < Border BorderBrush=" White" BorderThickness=" 2" Margin=" 10" Background=" White" > < Image Stretch=" None" > < Image.Source> < DrawingImage> < DrawingImage.Drawing> < DrawingGroup> <! -- Рис.1 - левый верхний (x, y, width, height) --> < ImageDrawing Rect=" 0, 0, 348, 232" ImageSource=" Images\market 031.jpg" /> <! -- Рис.2 - правый верхний (x, y, width, height) --> < ImageDrawing Rect=" 350, 0, 348, 232" ImageSource=" Images\market 034.jpg" /> <! -- Рис.3 - левый нижний (x, y, width, height) --> < ImageDrawing Rect=" 0, 234, 348, 232" ImageSource=" Images\market 039.jpg" /> <! -- Рис.4 - правый нижний (x, y, width, height) --> < ImageDrawing Rect=" 350, 234, 348, 232" ImageSource=" Images\market 040.jpg" /> < /DrawingGroup> < /DrawingImage.Drawing> < /DrawingImage> < /Image.Source> < /Image> < /Border> < /Window> - Дополните конструктор класса Window2 следующим кодом
public Window2(){ // Инициализация разметочной части InitializeComponent(); // Корректировка заголовка окна this.Title += " =\" Так голодают буржуины! \" "; // Дочернее окно не отображать в панели задач ОС this.ShowInTaskbar = false; } - Заполните обработчики щелчка и контекстного меню в классе Window1 следующим кодом
Window wnd2; private void Show_Window2(object sender, MouseButtonEventArgs e){ wnd2 = new Window2(); wnd2.Show(); } private void Create_Window2(object sender, RoutedEventArgs e){ wnd2 = new Window2(); wnd2.Show(); } - Запустите приложение и вызовите окно Window2 через окно Window1, как напоминает всплывающая подсказка
- Получится следующий результат
увеличить изображение
Здесь есть несколько существенных недостатков, которые следует устранить:
- Второе окно можно создавать во многих экземплярах
- Первое окно со вторым никак не связано и каждое из них закрывается самостоятельно
- Пиктограмма вторичных окон не отображается в панели задач (мы специально ввели в конструктор окна настройку this.ShowInTaskbar = false;)
Во первых, зачем пользователю много экземпляров одного и того же окна. Во вторых, хоть окна не имеют системных кнопок минимизации, все равно их можно минимизировать все сразу через операционную систему командой " Показать рабочий стол". А в этом случае вновь сделать видимыми вторичные окна не удасться и придется их закрывать через Диспетчер задач (либо через оболочку Visual Studio останавливать процесс - работу приложения).
Для устранения указанных недостатков нужно выполнить следующее:
- Назначить первое окно главным, чтобы при его закрытии закрывалось приложение в целом
- Блокировать создание вторичных окон, если одно из них уже существует, и попутно активировать вторичное окно, если оно заслонено другими окнами или скрыто
- Для назначения окна Window1 главным добавьте в его конструктор следующий код
public Window1(){ // Инициализация разметочной части InitializeComponent(); // Корректировка заголовка окна this.Title += " =\" Так голодают буржуины! \" "; // Всплывающая подсказка this.ToolTip = " Вызывайте дочернее окно\n" + " двойным щелчком мыши\n" + " или контекстным меню..."; // Сделать главным окном приложения Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; Application.Current.MainWindow = this; } - Запустите приложение и убедитесь, что теперь все дочерние окна при закрытии первичного окна тоже закрываются вместе с приложением
Для блокирования повторного создания дочерних окон применим два способа:
- В первом способе обернем в классе Window1 ссылку wnd2 на дочернее окно в свойство Wnd2 и будем ее обнулять в обработчике Closed окна Window2. В первом окне перед созданием дочернего будем эту ссылку проверять - если ненулевая, значит окно существует и новое создавать нельзя
- Второй способ намного проще, так как не требует жесткой зависимости кода между окнами введением дополнительного общего поля или свойства. Можно просто воспользоваться свойством Application.Current.Windows типа WindowCollection приложения и в первом окне перед созданием дочернего проверять, отсутствует ли в коллекции существующих окон приложения интересующее нас окно. Любое созданное окно приложения сразу заносится в эту коллекцию, а при закрытии между событиями Closing и Closed немедленно удаляется из нее. Единственное, что потребуется определить в дочернем окне, так это свойство Name, по которому мы будем искать это окно в коллекции приложения
Итак, реализуем сказанное...
|