Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Упражнение 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, как напоминает всплывающая подсказка
  • Получится следующий результат


увеличить изображение

Здесь есть несколько существенных недостатков, которые следует устранить:

  1. Второе окно можно создавать во многих экземплярах
  2. Первое окно со вторым никак не связано и каждое из них закрывается самостоятельно
  3. Пиктограмма вторичных окон не отображается в панели задач (мы специально ввели в конструктор окна настройку this.ShowInTaskbar = false;)

Во первых, зачем пользователю много экземпляров одного и того же окна. Во вторых, хоть окна не имеют системных кнопок минимизации, все равно их можно минимизировать все сразу через операционную систему командой " Показать рабочий стол". А в этом случае вновь сделать видимыми вторичные окна не удасться и придется их закрывать через Диспетчер задач (либо через оболочку Visual Studio останавливать процесс - работу приложения).

Для устранения указанных недостатков нужно выполнить следующее:

  1. Назначить первое окно главным, чтобы при его закрытии закрывалось приложение в целом
  2. Блокировать создание вторичных окон, если одно из них уже существует, и попутно активировать вторичное окно, если оно заслонено другими окнами или скрыто
  • Для назначения окна Window1 главным добавьте в его конструктор следующий код
public Window1(){ // Инициализация разметочной части InitializeComponent(); // Корректировка заголовка окна this.Title += " =\" Так голодают буржуины! \" "; // Всплывающая подсказка this.ToolTip = " Вызывайте дочернее окно\n" + " двойным щелчком мыши\n" + " или контекстным меню..."; // Сделать главным окном приложения Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; Application.Current.MainWindow = this; }
  • Запустите приложение и убедитесь, что теперь все дочерние окна при закрытии первичного окна тоже закрываются вместе с приложением

Для блокирования повторного создания дочерних окон применим два способа:

  1. В первом способе обернем в классе Window1 ссылку wnd2 на дочернее окно в свойство Wnd2 и будем ее обнулять в обработчике Closed окна Window2. В первом окне перед созданием дочернего будем эту ссылку проверять - если ненулевая, значит окно существует и новое создавать нельзя
  2. Второй способ намного проще, так как не требует жесткой зависимости кода между окнами введением дополнительного общего поля или свойства. Можно просто воспользоваться свойством Application.Current.Windows типа WindowCollection приложения и в первом окне перед созданием дочернего проверять, отсутствует ли в коллекции существующих окон приложения интересующее нас окно. Любое созданное окно приложения сразу заносится в эту коллекцию, а при закрытии между событиями Closing и Closed немедленно удаляется из нее. Единственное, что потребуется определить в дочернем окне, так это свойство Name, по которому мы будем искать это окно в коллекции приложения

Итак, реализуем сказанное...






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.