Windows Presentation Foundation

Windows Presentation Foundation (WPF, кодова назва Avalon) — графічна (презентаційна) підсистема (аналог WinForms), яка починаючи з .NET Framework 3.0 в складі цієї платформи. Має пряме відношення до XAML. WPF разом з .NET Framework 3.0 вбудована в Windows Vista, а також доступна для установки в Windows XP Service Pack 2 і Windows Server 2003.

Це перше реальне оновлення технологічного середовища призначеного для користувача інтерфейсу з часу випуску Windows 95. Воно включає нове ядро для заміни GDI і GDI+, використовувані в Windows Forms. WPF є високорівневим об'єктно-орієнтованим функціональним шаром (англ. framework), що дозволяє створювати двовимірні та тривимірні інтерфейси.

XAML

XAML (скорочення від Extensible Application Markup Language — розширювана мова розмітки застосунків) є мовою розмітки, яку використовують для створення екземплярів об'єктів .NET. Хоча мова XAML — це технологія, що може бути застосовна до багатьох різних предметних областей, її головне призначення — конструювання інтерфейсів користувачів WPF. Інакше кажучи, документи XAML визначають розташування панелей, кнопок та інших елементів керування, що становлять вікна в застосунку WPF. Малоймовірно, що вам доведеться писати код XAML вручну. Замість цього ви використовуєте інструмент, що генерує необхідний код XAML. Існує кілька підмножин XAML:

  • WPF XAML включає елементи, що описують вміст WPF з розряду векторної графіки, елементів керування й документів. У цей час це найважливіше застосування XAML.
  • XPS XAML — частина WPF XAML, що визначає XML-подання відформатованих електронних документів. Вона опублікована як окремий стандарт XML Paper Specification (XPS).
  • Silverlight XAML — підмножина WPF XAML, призначена для Silverlight-застосунків. Можна відвідати сайт https://web.archive.org/web/20121030190821/http://www.silverlight.net/, щоб ознайомитися з деталями.
  • WF XAML включає елементи, що описують уміст Windows Workflow Foundation (WF). Додаткова інформація про WF доступна на сайті https://web.archive.org/web/20081111114953/http://wf.netfx3.com/

Клас Application

У процесі виконання кожний застосунок WPF представлено екземпляром класу System.Windows.Application. Цей клас відслідковує всі відкриті вікна у вашому застосунку, вирішує, коли ваш застосунок повинен бути зупиненим, і ініціює події застосунка, які ви можете обробляти для виконання ініціалізації або очищення. У WPF застосунок проходить через простий життєвий цикл. Одразу після запуску вашого застосунка створюється об'єкт застосунка. У процесі його виконання виникають різні події застосунка, які ви можете відслідковувати. І, нарешті, коли об'єкт застосунка звільняється, ваш застосунок завершується.

Найпростіший спосіб використання класу Application полягає в його створенні вручну. Наступний приклад демонструє абсолютний мінімум: точку входу в застосунок — метод Main (), що створює вікно з іменем Window1 і запускає новий застосунок.

using System;
using System.Windows;
public class Startup
{
    [STAThread] 
    static void Main() 
    { 
        // Створення застосунку. 
        Application app = new Application(); 
        // Створення головного вікна.
        Window1 win = new Window1 () ; 
        // Запуск застосунку і відображення головного вікна.
        арр.Run(win) ; 
    } 
}

Звичайно клас Application залишає ваш застосунок активним до тих пір, доки хоча б одне вікно залишається відкритим. Якщо вам не потрібно таке поводження, ви можете змінити Application.ShutdownMode. Якщо ви створюєте об'єкт Application вручну, вам необхідно установити властивість ShutdownMode перед запуском Run().

Можливі такі варіанти:

  • OnLastWindowClose — поводження за замовчуванням — ваш застосунок виконується доти, доки існує хоча б одне відкрите ним вікно. Якщо ви закриваєте головне вікно, то властивість Application.MainWindow усе ще посилається на об'єкт, що представляє закрите вікно.
  • OnMainWindowClose — це традиційний підхід — застосунок залишається живим тільки доти, поки відкрито головне вікно.
  • OnExplicitShutdown Застосунок ніколи не завершується (навіть якщо всі вікна закриті), поки ви навмисно не викличете Application.Shutdown(). Цей підхід може бути виправданий, якщо ваш застосунок є інтерфейсом для задачі, що виконується досить довго, або якщо ви хочете використовувати складішу логіку, щоб вирішити, коли ваш застосунок повинен закритися (і тоді ви викликаєте метод Application.Shutdown()).

Незалежно від того, який спосіб зупину ви використовуєте, ви завжди можете викликати метод Application.Shutdown () для негайного завершення роботи застосунку.

Події класу Application

Початково файл App.xaml.cs не містить ніякого коду. Хоч код не обов'язковий, ви можете додати код обробки подій застосунка. Клас Application надає невеликий набір корисних подій. Нижче перераховані найважливіші з них.

  • Startup — відбувається після виклику методу Application.Run () і безпосередньо перед показом головного вікна (якщо ви передаєте головне вікно методу Run()).
  • Exit — відбувається, коли застосунок зупиняється з будь-якої причини, безпосередньо перед поверненням з методу Run(). Ви не можете в цей момент скасувати зупин, хоча код вашого методу Main() може повторно запустити застосунок. Ви можете використовувати подію Exit для установки цілочисельного коду виходу, що повертається методом Run().
  • SessionEnding — відбувається після завершення сеансу Windows, наприклад, коли користувач виходить із системи або зупиняє комп'ютер. (Можна визначити, що саме відбулося, перевіривши властивість SessionEndingCancelEventArgs. ReasonSessionEnding). Також можна скасувати зупин, надавши SessionEndingCancelEventArgs.Cancel значення true. Якщо цього не робити, то WPF викличе метод Application.Shutdown() після завершення оброблювача події.
  • Activated — відбувається, коли активізується одне з вікон застосунку. Це трапляється, коли ви перемикаєтеся з іншої програми Windows на цей застосунок. Також виникає при першому показі вікна.
  • Deactivated — відбувається при деактивізації вікна застосунка. Відбувається, коли ви перемикаєтеся на іншу програму Windows.
  • DispatcherUnhandledExcept — відбувається, коли виникає неопрацьований виняток у будь-якому місці вашого застосунка (у головному потоці застосунка). Реагуючи на цю подію, ви можете протоколювати критичні помилки — можна нейтралізувати виняток і продовжити роботу застосунка, установивши властивість DispatcherUnhandledExceptionEventArgs.Handled в true. Ви повинні почати цей крок тільки в тому випадку, якщо впевнені, що ваш застосунок перебуває в коректному стані і його робота може бути продовжена.

Відмінність класів Window і WinForm та їхня взаємодія один з одним

Вікна (window) є основними елементами в будь-якому настільному застосунку — настільки «основними», що в їхню честь навіть була названа операційна система Windows. І хоча в WPF є модель для створення навігаційних застосунків, що розподіляють задачі по окремих сторінках, вікна залишаються переважною технологією для створення застосунків.

Тим, кому доводилось раніше програмувати із застосуванням набору інструментальних засобів Windows Forms, більша частина пропонованого матеріалу здасться знайомою, тому що клас Window, фактично, є вільнішою моделлю класу Form.

Клас Window успадкований від класу ContentControl. Це означає, що він може містити тільки одного нащадка (яким звичайно є контейнер макета на зразок елемента керування Grid) і що його тло можна зафарбовувати за допомогою кисті шляхом установки властивості Background. Можна ще також використовувати й властивості BorderBrush і BorderThickness для додавання навколо вікна границі, але ця границя додається усередині віконної рамки (тобто по краю клієнтської області). Віконну рамку можна взагалі видаляти і встановлювати на власний розсуд.

Щоб відобразити вікно, необхідно створити екземпляр класу Window і викликати метод Show() або ShowDialog(). Метод ShowDialog() відображає модальне вікно. Модальні вікна не дозволяють користувачеві одержувати доступ до батьківського вікна, блокуючи можливість використання в ньому миші й можливість уведення в ньому яких-небудь даних до тих пір. доки модальне вікно не буде закрито. Також метод ShowDialog() ще й не здійснює повернення до тих пір. доки модальне вікно не буде закрито, так що виконання будь-якого коду, що знаходить після нього, на час відкладається. (Це, однак, не означає, що в цей час не може виконуватися й ніякий інший код — наприклад, при наявності запущеного таймера оброблювач його подій однаково буде працювати). Найбільше часто застосовувана в коді схема виглядає так: відображення модального вікна, очікування його закриття й наступне виконання над його даними якої-небудь операції.

Метод Show() відображає немодальне вікно, що не блокує доступ користувача ні до яких інших вікон. Більше того, метод Show() здійснює повернення відразу ж після відображення вікна, так що наступні після нього в коді оператори виконуються негайно. Можна створювати й показувати відразу кілька немодальних вікон, і користувач може взаємодіяти з усіма ними одночасно. У випадку застосування немодальних вікон іноді потрібен код синхронізації, що гарантує відновлення інформації в другому вікні при внесенні якихось зміні в першому й тим самим виключати імовірність роботи користувача з недійсними даними.

Володіння вікнами

.NET дозволяє вікну «володіти» іншими вікнами. Вікна, що мають вікно-власника, зручно застосовувати для плаваючих вікон панелей інструментів і вікон команд. Одним із прикладів такого вікна є вікно Find and Replace (Знайти й замінити) в Microsoft Word. Коли вікно-власник згортається, вікно, яким воно володіє, теж автоматично згортається. Коли вікно, що має власника, перекриває вікно, яке ним володіє, воно завжди відображається зверху.

Для підтримки володіння вікна клас Window пропонує дві властивості: властивість Owner і властивість OwnedWindows. Властивість Owner являє собою посилання, що вказує на вікно, що володіє поточним вікном (якщо таке є), а властивість OwnedWindows — колекцію всіх вікон, якими володіє поточне вікно (знову ж, якщо такі є).

Непрямокутні вікна

Вікна незвичайної форми часто є товарним знаком ультрасучасних популярних застосунків багатьох редакторів фотографій, програм для створення кінофільмів і мп3-програвачів. У створенні базового застосунку нестандартної форми в WPF немає нічого складного. Однак створення привабливого вікна незвичайної форми вимагає чималих зусиль — і, нерідко, залучення талановитого дизайнера графіки для створення начерків і фонової графіки. Базова процедура для створення вікна нестандартної форми має на увазі виконання наступних кроків:

  1. Встановіть для властивості Window.AllowsTransparency значення true.
  2. Встановіть для властивості Window.WindowStyle значення None, щоб сховати не клієнтську область вікна (рядок заголовку). Якщо цього не зробити, при спробі показати вікно з'явиться помилка InvalidOperationException.
  3. Встановіть для тла (властивість Background) прозорий колір (колір Transparent, значення альфа-каналу якого дорівнює нулю). Або ж зробіть так, щоб для тла використовувалося зображення, що має прозорі області (з нульовим значенням альфа-канал).

Ці три кроки ефективно видаляють стандартний зовнішній вигляд вікна (який фахівці з WPF часто називають хромом (chrome) вікна). Для забезпечення ефекту вікна незвичайної форми далі необхідно надати якийсь непрозорий уміст, що має потрібну форму. Тут можливі перераховані нижче варіанти.

  • Надати фонову графіку, використовуючи файл такого формату, що підтримує прозорість. Наприклад, для тла можна використовувати файл PNG. Це простий прямолінійний підхід, і він дуже зручний, якщо доводиться працювати з дизайнерами, які не розбираються в XAML. Однак через те, що вікно буде візуалізуватися з більшою кількістю пікселів і більш високими системними параметрами DPI, фонова графіка може отримати перекручений вигляд. Це також може представляти проблему й у випадку дозволу користувачеві змінювати розміри вікна.
  • Використовувати доступні в WPF функції для малювання форми, щоб створити тло з векторним умістом. Такий підхід виключає втрату якості, якими б не були розміри вікна й DPI-параметри системи. Однак у цьому випадку напевно буде потрібно використовувати засіб проектування, що підтримує XAML.
  • Використовувати більше простий WPF-елемент, що має необхідну форму. Наприклад, вікно із чудовими округленими краями можна створити за допомогою елемента Border. Такий підхід дозволяє створювати вікна із сучасним зовнішнім виглядом у стилі Office без застосування яких-небудь дизайнерських навичок.

Взаємодія з WinForms

В ідеальному світі, як тільки розроблювач освоїв би нову технологію, подібну WPF, він міг би залишити колишню в минулому. Все повинне було б розроблятися тільки на основі нового, найбагатшого інструментарію, не потрібно було б турбуватися про успадкований код. Звичайно цей ідеальний світ не має нічого спільного зі світом реальним, у цьому є дві причини, які змушують розроблювачів WPF у певний момент взаємодіяти із платформою Windows Forms — щоб зберегти інвестиції в розробку існуючого коду й щоб компенсувати можливості, яких бракує в WPF.

Найпростіший спосіб інтеграції вмісту WPF і Windows Forms полягає в розташуванні їх в окремих вікнах. Таким чином, ваш застосунок буде складатися з добре інкапсульованих класів вікон, кожний з яких має справу тільки з однією технологією. Будь-які подробиці взаємодії обробляються в коді «клею» логіці, що створює й відображає вікна.

Додавання форм до застосунка WPF

Найпростіший підхід до змішування вікон і форм полягає в додаванні однієї або більше форм (з набору інструментів Windows Forms) до звичайного застосунку WPF. У Visual Studio це відбувається стандартно — клацання правою кнопкою миші на імені проекту в Solution Explorer і вибір у контекстному меню команду Add New Item. При першому додаванні форми Visual Studio додасть посилання на всі необхідні збірки Windows Forms, включаючи System.Windows.Forms.dll і System.Drawing.dll. Ви можете спроектувати форму в проекті WPF точно так само, як робите це в проекті Windows Forms. Коли ви відкриваєте форму, Visual Studio завантажує звичайний візуальний конструктор форм Windows Forms і наповнює панель інструментів елементами керування Windows Forms. Коли ви відкриваєте файл XAML для вікна WPF, то одержуєте середовище конструктора WPF.

Додавання вікон WPF у застосунок Windows Forms

Зворотний хід трохи складніше. Visual Studio не дозволяє прямо створити нове вікно WPF у застосунку Windows Forms, інакше кажучи, ви не побачите його як один з доступних шаблонів при клацанні правою кнопкою миші на імені проекту і виборі в контекстному меню команди Add New Item. Однак ви можете додати існуючі файли *.cs і *.xaml, які визначають вікно WPF, з іншого проекту. Щоб зробити це клацніть правою кнопкою миші на ім'я проекту в Solution Explorer, виберіть у контекстному меню команду Add Existing Item і знайдіть ці файли. Вам також знадобиться додати посилання на основні збірки WPF (PresentationCore.dll, PresentationFramework.dll і WindowsBase.dll).

Якщо додати вікно WPF до застосунка Windows Forms все буде правильно. Коли ви відкриєте його, то зможете використовувати дизайнер WPF для його модифікації. При побудові проекту буде скомпільований XAML і автоматично згенерований код буде об'єднаний з вашим класом коду, як ніби це було повноцінним WPF — застосунком. Створення проекту, що використовує форми й вікна, є нескладним. Однак є кілька додаткових нюансів, які варто враховувати, коли ви відображаєте ці форми й вікна під час виконання. Якщо вам потрібно показати вікно або форму модально (як це робиться з діалоговими вікнами), то така задача досить проста й не вимагає зміни коду. Але якщо ви хочете показувати вікна в немодальному режимі, то вам знадобиться розробити додатковий код, щоб забезпечити коректну підтримку клавіатури, що буде описано далі.

Відображення модальних вікон і форм

Відображення модальної форми в застосунку WPF не вимагає зусиль. Ви використовуєте в точності той же код. що й у проекті Windows Forms. Наприклад, якщо у вас є клас форми з іменем Form1, те для його модального відображення ви застосовуєте такий код:

Forml frm = new Form1();
if (frm.ShowDialog==System.Windows.Forms.DialogResult.OK)
{
  MessageBox.Show("Ви натиснули на OK у формі Windows Forms.");
}

Зауважте, що метод Form.ShowDialog() працює дещо інакше, ніж WPF-метод Window.ShowDialog (). У той час як Window.ShowDialog() повертає true, false або null, метод Form.ShowDialog() повертає значення з перерахування DialogResult. Зворотна задача — відображення вікна WPF з форми — настільки ж проста. Ви просто взаємодієте із загальнодоступним інтерфейсом вашого класу вікна, a WPF подбає про інше:

Windowl win = new Windowl();
if (win.ShowDialog()== true)
{
     MessageBox.Show("Ви натиснули на OK у вікні WPF.");
}

Відображення немодальних вікон і форм

Якщо ви хочете відображати вікна або форми в немодальному режимі, то тут все не так просто. Складність полягає в тому. що клавіатурне уведення приймається кореневим застосунком і повинен бути доставлене у відповідне вікно. Щоб це працювало між умістом WPF і Windows Forms, потрібен якийсь спосіб передачі цих повідомлень потрібному вікну або формі.

Якщо ви хочете показувати вікно WPF у немодальному режимі зсередини застосунка Windows Forms, то повинні для цього використовувати статичний метод ElementHost.EnableModelessKeyboardlnterop(). Вам також знадобиться посилання на збірку WmdowsFormsIntegration.dll, що визначає клас ElementHost у просторі імен System.Windows.Formslntegration.

Метод EnableModelessKeyboardlnterop() викликається після створення вікна, але перед його відображенням. Викликаючи цей метод, передайте йому посилання на нове вікно WPF, як показано нижче:

Window1 win = new Window1(); ElementHost.EnableModelessKeyboardInterop(win); win.Show();

При виклику EnableModelessKeyboardlnterop() об'єкт ElementHost додає фільтр повідомлень у застосунок Windows Forms. Цей фільтр повідомлень перехоплює клавіатурні повідомлення, коли активне ваше вікно WPF і пересилає їх йому. Без цього ваші елементи керування WPF просто не одержать клавіатурного уведення.

Якщо потрібно відобразити немодальний застосунок Windows Forms усередині застосунка WPF використовуйте аналогічний метод WindowsFormsHost.EnableWindowsFormsInterop().

Однак у цьому випадку не потрібно передавати посилання на форму, що ви хочете показати. Замість цього ви просто викликаєте цей метод один раз — перед відображенням будь-якої форми (гарним рішенням буде викликати його при запуску застосунка). Тепер ви можете просто показати форму в немодальному режимі, без усяких додаткових зусиль:

WindowsFormsHost.EnableWindowsFormsInterop(); Forml frm = new Forml(); frm.Show();

Без виклику EnableWindowsFormsInterop() ваша форма буде відображатися, але не розпізнає ніякого клавіатурного уведення. Наприклад, ви не зможете використовувати клавішу <Tab> для переходу від одного елемента керування до іншого.

Цей процес можна розширити на кілька рівнів. Наприклад, ви можете створити вікно WPF, що відображатиме форму (модально або не модально), а ця форма, у свою чергу, може показати вікно WPF. Хоча це не доведеться робити дуже часто, все-таки це кращий підхід, аніж підтримка взаємодії між різнорідними елементами. Така підтримка дозволяє інтегрувати різні типи вмісту в одному вікні, але не дозволяє вкладати на більш ніж один рівень углиб (наприклад, створити вікно WPF, що містить елемент керування Windows Forms, що, у свою чергу, містить у собі елемент керування WPF).

Компонування та елементи управління WPF

Поняття компонування в WPF

Вікно WPF може містити тільки один елемент. Щоб розмістити більше одного елемента й створити більше практичний користувальницький інтерфейс, вам потрібно помістити у вікно спеціальний елемент керування — контейнер і потім додавати елементи в цей контейнер.

В WPF компонування визначається використовуваним контейнером. Хоча є кілька контейнерів, серед яких можна вибирати будь-який «ідеальне» вікно WPF має піддаватись описаним нижче ключовим принципам.

  • Елементи (такі як елементи керування) не повинні мати явно встановлених розмірів. Замість цього вони ростуть, щоб заповнити їхній уміст. Наприклад, кнопка збільшується, коли ви додаєте в неї текст. Ви можете обмежити елементи керування прийнятними розмірами, установлюючи максимальне й мінімальне їхні значення.
  • Елементи не вказують свою позицію в екранних координатах. Замість цього вони впорядковуються своїм контейнером на основі розміру, порядку й (необов'язково) іншої інформації, специфічної для контейнера компонування. Якщо ви хочете додати пробіл між елементами, то використовуєте для цього властивість Margin.
  • Контейнери компонування «розділяють» доступний простір між своїми дочірніми елементами. Вони намагаються надати кожному елементу його найкращий розмір (на основі його вмісту), якщо дозволяє вільний простір. Вони можуть також виділяти додатковий простір дочірнім елементам.
  • Контейнери компонування можуть бути вкладеними. Типовий користувальницький інтерфейс починається з Grid — найрозвиненішого контейнера, і містить інші контейнери компонування, які організовують менші групи елементів, такі як текстові поля з мітками, елементи списку, піктограми в панелі інструментів і т. д.

Компонування WPF відбувається за дві стадії: стадія виміру й стадія розміщення. На стадії виміру контейнер виконує прохід у циклі дочірніми елементами й опитує їхні найкращі розміри. На стадії розміщення контейнер поміщає дочірні елементи у відповідні позиції.

Всі контейнери компонування WPF є панелями, які успадковані від абстрактного класу System.Windows.Controls.Panel Фактично, базовий клас Panel — це не що інше, як початкова точка для побудови інших більш спеціалізованих класів. WPF пропонує ряд похідних від Panel класів, які ви можете використовувати для організації компонування. Як і всі елементи керування WPF і більшість візуальних елементів, ці класи перебувають у просторі імен System.Windows.Controls.

Основні панелі компонування

  • StackPanel — Розміщає елементи в горизонтальний або вертикальний стек. Цей контейнер компонування звичайно використовується в невеликих секціях великого більше складного вікна.
  • WrapPanel — Розміщає елементи в серіях рядків з переносом. У горизонтальній орієнтації Wrap Panel розташовує елемент у рядку ліворуч праворуч, потім переходить до наступного рядка. У вертикальній орієнтації WrapPanel розташовує елемент зверху долілиць, використовуючи додаткові стовпчики для доповнення елементів, що залишилися.
  • DockPanel — Вирівнює елементи по краю контейнера.
  • Grid — Вишиковує елементи в рядки й стовпчики невидимої таблиці. Це один з найбільш гнучких і широко використовуваних контейнерів компонування.
  • UniformGrid — Розташовує елементи в невидиму таблицю, установлюючи однаковий розмір для всіх осередків. Цей контейнер компонування використовується нечасто.
  • Canvas — Дозволяє елементам позиціонуватися абсолютно — за фіксованими координатами. Цей контейнер компонування найбільше схожий на традиційний компоновщик Windows Forms, але не передбачає засобів прив'язки й стикування. У результаті це невідповідний вибір для вікон змінного розміру, який призводить до значного обсягу роботи.

Властивості компонування

  • HorizontalAlignment. Визначає позиціонування дочірнього елемента усередині контейнера компонування, коли доступно додатковий простір по горизонталі. Ви можете вибрати Center, Left, Right або Stretch.
  • VerticalAlignment. Визначає позиціонування дочірнього елемента усередині контейнера компонування, коли доступно додатковий простір по вертикалі. Ви можете вибрати Center, Top, Bottom або Stretch.
  • Margin. Додає небагато місця навколо елемента. Властивість Margin — це екземпляр структури System.Windows.Thickness з окремими компонентами для верхньої, нижньої, лівої й правої граней.
  • MinWidth і MinHeight. Установлює мінімальні розміри елемента. Якщо елемент занадто великий, щоб поміститися в його контейнер компонування, він буде усічений.
  • MaxWidth і MaxHeight. Установлює максимальні розміри елемента. Якщо контейнер має вільний простір, елемент не буде збільшений понад зазначені межі, навіть якщо властивості HorizontalAlignment і VerticalAlignment установлені в Stretch.
  • Width і Height. Явно встановлюють розміри елемента. Ця установка перевизначає значення Stretch для властивостей HorizontalAlignment і VerticalAlignment. Однак цей розмір не буде встановлений, якщо виходить за межі, задані в MinWidth, MinHeight, MaxWidth і MaxHeight.

Інші елементи управління

  • ScrollViewer. В WPF підтримку прокручування забезпечити нескладно, однак для цього буде потрібно спеціальний інгредієнт — елемент керування вмістом ScrollViewer. Щоб забезпечити підтримку прокручування, вам потрібно впакувати вміст, що ви хочете прокручивати, в ScrollViewer. Попри те що цей елемент керування може зберігати що завгодно, звичайно він використовується для впакування контейнера компонування.
  • GroupBox і Tabltem — елементи керування вмістом, що мають заголовки. У класу ContentControl є три спадкоємці: GroupBox. Tabltem і Expander. Елемент керування GroupBox є найпростішим з них. Він відображається у вигляді вікна з округленими кутами й заголовком.
  • Tabltem представляє сторінку в елементі керування TabControl. Клас Tabltem додає одна важлива властивість IsSelected, що показує, чи відображається в цей момент вкладка в елементі керування TabControl.

Найекзотичнішим елементом керування вмістом є Expander. Він упаковує область умісту, яку користувач може показувати або приховувати, клацаючи на невеликій кнопці зі стрілкою. Ця технологія використовується часто в оперативних довідкових системах, а також на Web-Сторінках, щоб вони могли включати більші обсяги вмісту, не перевантажуючи користувачів інформацією, яку їм не хочеться бачити.

  • Найпростішим елементом керування вмістом є Label — мітка. Як і будь-який інший елемент керування вмістом, вона приймає одиночну порцію вмісту, що ви хочете помістити усередині її. Відмітною рисою елемента Label є його підтримка мнемонічних команд — клавіш швидкого доступу, які передають фокус зв'язаному елементу керування.

WPF розпізнає три типи кнопок: Button, CheckBox і RadioButton. Всі ці кнопки являють собою елементи керування вмістом, що є спадкоємцями класу ButtonBase.

WPF включає три текстових елементи керування: TextBox, RichTextBox і PasswordBox. Елемент PasswordBox є прямим спадкоємцем класу Control. Елементи керування TextBox і RichTextBox є спадкоємцями класу TextBase.

WPF включає багато елементів керування, які можуть працювати з колекцією елементів, починаючи із простих елементів керування ListBox і ComboBox і закінчуючи більше спеціалізованими елементами керування, такими як ListView, TreeView і ToolBar. Всі ці елементи керування є нащадками класу ItemsControl (він, у свою чергу, є нащадком класу Control).

Елемент керування Slider є більше спеціалізованим елементом керування, що часом виявляється корисним. Наприклад, ним можна скористатися для завдання числових значень у тих ситуаціях, коли саме число не є особливо важливим. Наприклад, гучність у програвачі найкраще встановлювати, переміщаючи в різні сторони бігунок на лінійці прокручування. Загальна позиція бігунка показує відносну гучність (нормально, тихо, голосно), а лежаче в його основі число не представляє особливого змісту для користувача.

Елемент керування ProgressBar показує хід виконання тривалої задачі. На відміну від повзунка. ProgressBar не є інтерактивним елементом керування. Навпаки, за зміну значення властивості Value відповідає винятково ваш код. (правила WPF визначають, що ProgressBar не повинен бути елементом керування, оскільки він не реагує на дії миші або уведення на клавіатурі.)

Декоратори

Мова йде про декоратори, які звичайно служать для того, щоб графічно урізноманітити й прикрасити область навколо об'єкта. Всі декоратори є спадкоємцями класу System.WindowsControls.Decorator. Більшість декораторів призначена для використання разом з певними елементами керування. Клас Border дуже простий. Він приймає окрему порцію вкладеного вмісту (яким часто є панель компонування) і додає до нього тло або рамку. Viewbox — більш екзотичний декоратор. Принцип, закладений в основу Viewbox, зрозуміти нескладно. Будь-який уміст, що ви поміщаєте в декоратор Viewbox, масштабується таким чином, щоб він міг уміститися в цьому декораторі.

Користувацькі елементи

Користувальницький елемент- не найкращий вибір, коли ви хочете просто підправити зовнішній вигляд елемента, але цілком виправданий, коли потрібно істотно змінити його функціональність. Наприклад, є причини існування в WPF окремих класів TextBox і PasswordBox. тому що вони обробляють натискання клавіш по- різному, зберігаючи дані усередині себе різними способами, по-різному взаємодіючи з іншими компонентами, такими як буфер обміну й т.п. Аналогічно, якщо ви хочете спроектувати елемент керування, що має свій власний винятковий набір властивостей, методів і подій, то вам доведеться створювати його самостійно. FrameworkElement — найнижчий рівень, з яким ви звичайно будете мати справу при створенні користувальницького елемента. Звичайно такий підхід вибирається тільки тоді, коли потрібно намалювати його вміст «з нуля» за допомогою перевизначення OnRender() і використання System.Windows.Media.DrawingContext. Це схоже на підхід, де користувальницький інтерфейс конструювався на основі об'єктів Visual. Клас FrameworkElement надає лиш базовий набір властивостей і подій для елементів, які не призначені для взаємодії з користувачем.

Control Цей клас найчастіше служить початковою точкою при побудові елемента керування «з нуля». Це — базовий клас для всіх взаємодіючих з користувачем графічних елементів керування. Клас Control додає властивості для установки тла й переднього плану, а також шрифту й вирівнювання вмісту. Крім того, цей клас поміщає себе в послідовність табуляції (властивістю isTabStop) і одержує повідомлення про подвійне клацання (через події MouseDoubleCiick і Preview MouseDoubleClick). Але більше важливо те, що клас Control визначає властивість Template, що дозволяє заміняти його зовнішній вигляд з необмеженою гнучкістю. ContentControl. Це — базовий клас для елементів керування, які можуть відображатися як єдине ціле з довільним умістом. Уміст може бути елементом користувальницького об'єкта, застосовуваного в сполученні із шаблоном. Багато елементів керування впаковують специфічний, обмежений тип умісту. Оскільки ці елементи керування не підтримують всіх елементів, вони не повинні визначатися як елементи керування із умістом.

UserControl Це елемент керування із умістом, що може бути сконфігурованим із застосуванням поверхні часу проектування. Хоча такий користувальницький елемент керування не настільки відрізняється від звичайного елемента керування із умістом, звичайно він використовується тоді, коли ви хочете швидко повторно застосувати незмінний блок користувацього інтерфейсу в більш ніж одному вікні (замість створення дійсно окремого елемента керування, що може бути перенесений з одного застосунка в інший). ItemsControl, Selector — базовий клас для елементів керування, службовців оболонками для списків елементів, але не підтримуючий вибір позицій, у той час як Selector — більше спеціалізований базовий клас для елементів, що підтримують вибір. Ці класи нечасто застосовуються для створення користувальницьких елементів керування, оскільки засобу шаблонів даних ListBox, ListView і TreeView забезпечують достатню гнучкість.

Panel. Базовий клас для елементів керування, що володіє логікою компонування. Елемент із компонуванням може містити в собі безліч дочірніх елементів і розміщати їх відповідно до певної семантики компонування. Часто панелі включають прикріплені властивості, які можуть бути встановлені в дочірні елементи для того, щоб конфігурувати їхнє розташування. Decorator. Специфічний клас елемента керування Це базовий клас для елементів, що служать оболонками для інших елементів і забезпечують графічний ефект або певний засіб. Двома яскравими прикладами можуть служити Border, що малює лінію навколо елемента, і viewbox, що масштабує свій уміст динамічно з використанням трансформацій. Серед інших декорацій — клас Chrome, використовуваний для постачання знайомими рамками й тлом часто використовуваних елементів керування, таких як кнопка.

Поняття команд. WPF-події, що маршрутизуються

Загальні відомості про команди

У добре спроектованому застосунку Windows логіка застосунка перебуває не в оброблювачах подій, а закодована в методах, що більше мають високий рівень. Кожний із цих методів представляє одну «задачу» (task), розв'язувану застосунком.

Кожна задача може покладатися на додаткові бібліотеки (окремо скомпільовані компоненти, у яких інкапсулюється бізнес-логіка або доступ до бази даних). Приклад таких відносин показаний на ескізі. Найочевиднішим способом використання такого дизайну є додавання оброблювачів подій скрізь, де вони потрібні, і застосування кожного з них для виклику відповідного методу застосунка. Фактично, у такому разі код вікна перетворюється в полегшену комутаційну панель, що реагує на уведення й пересилає запити усередину застосунка. Хоча такий дизайн є цілком розумним, він не заощаджує ніяких зусиль. Багато задач застосунка можуть ініціюватися на різних маршрутах, через що все ж таки доводиться часто писати деклька оброблювачів подій, що викликають той самий метод застосунка. В цьому немає особливої проблеми (тому що код комутаційної панелі досить простий), але життя набагато ускладнюється, коли доводиться мати справу зі станом користувальницького інтерфейсу.

Зрозуміти, про що йде мова, допоможе простий приклад. Припустимо, що є програма, до складу якої входить метод з іменем PrintDocument(). Цей метод може ініціюватися чотирма способами: через головне меню (шляхом вибору в меню File (Файл) команди Print (Друк)), через контекстне меню (шляхом виконання клацання правою кнопкою миші в порожній області й вибору в контекстному меню, що з'явився, команди Print (Друк)), за допомогою клавіатурної комбінації (<Ctrl+P>) і за допомогою відповідної кнопки в панелі інструментів. У певних моментах життєвого циклу застосунку задача PrintDocument() повинна бути недоступної. Це має на увазі відключення відповідних команд у двох меню й кнопки в панелі інструментів таким чином, щоб на них не можна було виконувати клацання, а також ігнорування клавіатурної комбінації <Ctrl+P>. Написання коду, що робить це, (і додавання коду, що включає дані елементи керування пізніше) — дуже непростий підхід.

Навіть ще гірше те, що допущення в ньому помилки може призвести до того, що різні блоки коду стану будуть перекриватися неправильно, залишаючи елемент керування в активному стані навіть тоді, коли він не повинен бути доступний. Написання й налагодження подібного коду є одним з найменш приємних аспектів розробки Windows-застосунків. На диво багатьох досвідчених розроблювачів Windows-застосунків, у наборі інструментальних засобів Windows Forms не було ніяких функціональних можливостей, які могли б полегшувати виконання подібних операцій. Розроблювачі могли створювати необхідну їм інфраструктуру самостійно, але більшість із них воліло цього не робити.

На щастя, WPF заповнює цей пробіл, пропонуючи нову командну модель, що надає дві наступних важливих можливості:

  • делегування подій відповідними командам;
  • підтримка включеного стану елемента керування в синхронізованому виді за допомогою стану відповідної команди.

Модель команд WPF складається з великої кількості рухливих частин. Ключовими в ній є чотири такі компоненти:

  • Команди. Команда представляє задачу застосунка й стежить за тим. коли вона може бути виконана. Однак коду, який виконує задачу застосунка, команди насправді не містять.
  • Прив'язки команд. Кожна прив'язка (binding) має на увазі з'єднання команди з логікою, що має до неї відношення, застосунка, відповідальної за обслуговування певної області користувальницького інтерфейсу. Такий факторизований дизайн дуже важливий, тому що та сама команда може використовуватися в декількох місцях у застосунку й мати в кожному з них різне призначення. Для забезпечення подібного поводження саме й служать різні прив'язки однієї й тої ж команди.
  • Джерела команд. Джерело команди ініціює команду. Наприклад, і елемент керування Menultem, і елемент керування Button можуть служити джерелами команд. Клацання на них у такому випадку буде приводити до виконання прив'язаної команди.
  • Цільові об'єкти команд. Цільовий об'єкт команди — це елемент, для якого призначена дана команда, тобто елемент, на якому вона виконується. Наприклад, команда Paste може вставляти текст в елемент TextBox, а команда OpenFile — відображати документ в елементі DocumentViewer. Цільовий об'єкт може бути важливий, а може бути й неважливий, що залежить від природи команди.

Інтерфейс ICommand Серцем моделі команд WPF безумовно є інтерфейс System.Windows.Input.ICommand, що визначає спосіб, у відповідність із яким працюють команди. Цей інтерфейс включає два методи й подія:

public interface ICommand { void Execute(object parameter); bool CanExecute(object parameter); event EventHandler CanExecuteChanged; }

При створенні своїх власних команд реалізовувати інтерфейс ICommand прямо не обов'язково. Замість цього можна використовувати клас System.Windows.Input.RoutedCommand, що реалізує цей інтерфейс автоматично. Клас RoutedCommand є єдиним класом у WPF, що реалізує інтерфейс ICommand. Інакше кажучи, всі команди WPF являють собою екземпляри класу RoutedCommand (або похідного від нього класу). Одна із ключових концепцій, що лежать в основі моделі команд у WPF, полягає в тому, що клас RoutedCommand не містить ніякої логіки застосунка. Він просто представляє команду. Це означає, що один об'єкт RoutedCommand має ті ж можливості, що й інший. Якщо інтерфейс ICommand інкапсулює ідею команди — дії, що може ініціюватися й бути або не бути доступною, то клас RoutedCommand змінює команду так, щоб вона могла подібно бульбашці підніматися нагору ієрархією елементів WPF до відповідного оброблювача подій.

Більшість команд, з якими розробнику доведется мати справу, будуть не об'єктами RoutedCommand, а екземплярами класу RoutedUICommand, який успадковується від класу RoutedCommand. (Насправді всі заготовлені команди, які надає WPF. є саме об'єктами RoutedUICommand.)

Бібліотека команд

Розроблювачі WPF урахували той факт, що в кожному застосунку може використовуватися величезна кількість команд, і що багато команд можуть бути загальними для множини застосунків. Наприклад, у всіх застосунках, призначених для обробки документів, будуть присутні свої власні версії команд New (Створити), Open (Відкрити) і Save (Зберегти). Тому для зменшення обсягу зусиль, необхідних для створення таких команд, вони включили до складу WPF бібліотеку базових команд, у якій утримується понад 100 команд. Всі ці команди доступні через статичні властивості п'яти відповідних статичних класів.

  • ApplicationCommands. Цей клас надає загальні команди, включаючи команди, пов'язані з буфером обміну (такі як Копіювати, Вирізати і Вставити), і команди, що стосуються обробки документів (New (Створити), Open (Відкрити), Save (Зберегти), SaveAs (Зберегти як), Print (Друк) і т. д.).
  • NavigationCommands. Цей клас надає команди, використовувані для навігації, включаючи ті, що призначено для сторінкових застосунків (на зразок команди BrowseBack (Назад), BrowseForward (Уперед) і NextPage (Перехід)), і ті, що підходять для застосунків, призначених для роботи з документами (команди IncreaseZoom (Масштаб) і Refresh (Обновити)).
  • EditingCommands. Цей клас надає довгий перелік команд, призначених у більшій мірі для редагування документів, включаючи команди для переміщення (MoveToLineEnd (Перехід у кінець рядка), MoveLeftByWord (Перехід уліво на одне слово), MoveUpByPage (Перехід на одну сторінку нагору) і т. д.), виділення вмісту (SelectToLineEnd (Виділення до кінця рядка), SelectLeftByWord (Виділення слова ліворуч)) і зміни форматування (ToggleBold і ToggleUnderline (Виділення напівжирним і підкресленням)).
  • MediaCommands. Цей клас включає набір команд для роботи з мультимедія (серед яких команда Play (Відтворити). Pause (Пауза), NextTrack (Перехід до наступної композиції) і IncreaseVolume (Збільшення гучності)).

Відключення команд відбувається так:

CommandBinding binding = new CommandBinding (ApplicationCommands.Save); 
binding.Executed += SaveCommand_Executed; 
binding.CanExecute += SaveCommand CanExecute; 
this.CommandBindings.Add(binding);

У цьому оброблювачі подій потрібно просто перевірити значення змінної isDirty і встановити відповідне значення для властивості CanExecuteRoutedEventArg.CanExecute:

private void SaveCommand_CanExecute (object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = isDirty;
}

Якщо значенням властивості is Dirty виявляється false, команда відключається (робиться недоступною), а якщо true, то навпаки включається. (Без прапора CanExecute буде зберігатися саме останнє значення.)

Події, що маршрутизуються

Кожний розробник, що використовує .NET, знайоммий з поняттям події — це повідомлення, що посилається об'єктом (наприклад, елементом WPF), щоб повідомити код про те, що відбулося щось важливе. WPF поліпшує модель подій .NET завдяки новій концепції маршрутизації подій. Маршрутизація дозволяє події виникати в одному елементі, а генеруватися — в іншому. Наприклад, маршрутизація подій дозволяє клацанню, що почалося в кнопці панелі інструментів, генеруватися в панелі інструментів, а потім у вікні, що вміщає панель, і тільки потім оброблятися вашим кодом.

Маршрутизація подій пропонує більшу гнучкість для написання лаконічного коду, що зможе обробляти події в більше зручному для цього місці. Вона необхідна також для роботи з моделлю вмісту WPF. яка дозволяє створювати прості елементи (наприклад, кнопки) з десятків окремих інгредієнтів, кожний з яких має свій власний набір подій.

Ці події бувають трьох видів.

  • Прямі події (direct event) подібні до звичайних подій .NET. Вони виникають в одному елементі, і не передаються в інший. Наприклад, MouseEnter (виникає, коли покажчик миші наводиться на елемент) є простою подією.
  • Події, що піднімаються («бульбашкові» події, bubbling event), «мандрують» нагору ієрархією. Наприклад, MouseDown є подією, що піднімається. Вона виникає в елементі, на якому було зроблено клацання. Потім вона передається від цього елемента до батька, потім до батька цього батька, і так далі. Цей процес триває доти, поки WPF не досягне вершини дерева елементів.
  • Тунельні події (tunneling event) переміщаються вниз ієрархією. Вони надають можливість попередньо переглядати (і, можливо, зупиняти) подію до того, як воно дійде до відповідного елемента керування. Наприклад, PreviewKeyDown дозволяє перервати натискання клавіші, спочатку на рівні вікна, а потім у більш специфічних контейнерах, до тих пір, поки ви не дійдете до елемента, що мав фокус у момент натискання клавіші.

Попри те, що кожний елемент має найширший спектр подій, найважливіші події звичайно діляться на чотири описаних нижче категорії.

Події часу існування

Ці події виникають, коли елемент ініціялізується, завантажується або вивантажується.

  • Події миші. Ці події є результатом дій миші.
  • Події клавіатури. Ці події є результатом дій клавіатури (наприклад, натискання клавіші).
  • Події пера. Ці події є результатом використання пера (stylus).

Разом події миші, клавіатури й пера відомі як події введення.

Фокус

У світі Windows користувач може працювати одночасно з одним елементом керування. Елемент керування, що у цей момент часу одержує натискання клавіші користувачем, перебуває у фокусі. Іноді такий елемент вимальовується трохи інакше. Наприклад, кнопка WPF здобуває синій відтінок, щоб свідчить про те, що вона перебуває у фокусі. Щоб елемент керування міг одержувати фокус, його властивості Focusable потрібно привласнити значення true. За замовчуванням воно є таким для всіх елементів керування.

Досить цікаво те, що властивість Focusable визначається як частина класу UIElement, а це означає, що інші елементи, які не є елементами керування, теж можуть одержувати фокус. Звичайно в класах, які не визначають елементи керування, властивість Focusable за замовчуванням має значення false. Проте, ви можете привласнити йому true. Спробуйте зробити це на прикладі контейнера Stack Panel — коли він буде одержувати фокус, на краях панелі буде з'являтися пунктирна рамка.

Щоб перемістити фокус із одного елемента на іншій, користувач може клацнути кнопкою миші або скористатися клавішею <Tab> і клавішами керування курсором. У попередніх середовищах розробки програмісти додавали багато зусиль для того, щоб клавіша <Tab> передавала фокус «за законами логіки» (звичайно ліворуч праворуч, а потім долілиць вікна), і щоб при першому відображенні вікна фокус передавався необхідному елементу керування. В WPF така додаткова робота потрібно дуже рідко, оскільки WPF використовує ієрархічне компонування елементів для реалізації послідовності переходу за допомогою клавіші табуляції. Фактично, при натисканні клавіші <Tab>, здійснюється перехід до першого нащадка в поточному елементі або, якщо поточний елемент не має нащадка, до наступного нащадка, що перебуває на тому ж рівні.

Якщо ви хочете керувати послідовністю переходу за допомогою клавіші табуляції, ви можете задати властивість Tablndex кожного елемента керування, щоб визначити його місце в числовому порядку. Елемент керування, властивість Tablndex якого має значення 0, одержує фокус першим, потім фокус одержує елемент із більшим значенням цієї властивості (наприклад. 1.2. 3 і т. д.). Якщо кілька елементів мають однакові значення властивості TabIndex, WPF виконує автоматичну послідовність передачі фокуса, відповідно до якої фокус одержує найближчий елемент у послідовності.

Особливості прив'язки та взаємодії з даними

Прив'язка даних

Прив'язка даних- це традиція добування інформації з об'єкта й відображення її в користувальницькому інтерфейсі застосунка без написання коду, що виконує всю цю роботу. Часто «товсті» клієнти використовують двонаправлену прив'язку даних, що додає можливості — «заштовхування» інформації з користувальницького інтерфейсу назад в деякий об'єкт — знову ж таки, з мінімальним кодуванням, або взагалі без нього. Оскільки багато Windows-застосунків пов'язані з даними (і всі вони в певний час мають потребу у взаємодії з даними), прив'язка даних перебуває в центрі уваги такої технології користувальницьких інтерфейсів, як WPF. Розроблювачі, що прийшли до WPF з досвідом роботи в Windows Forms, знайдуть у прив'язці даних WPF багато схожого з тим, до чого вони звикли. Як і в Windows Forms, прив'язка даних WPF дозволяє створювати прив'язки, які витягають інформацію практично з будь-якої властивості будь-якого елемента. WPF також включає набір облікових елементів керування, які можуть обробляти цілі колекції інформації й дозволяють здійснювати навігацію по цій інформації. Однак відбулися й істотні зміни в способах прив'язки даних, що відбуваються «за лаштунками», з'явилася деяка вражаюча нова функціональність, а також можливості тонкого настроювання. Багато концепцій залишилися колишніми, але код змінився.

Найпростіший сценарій прив'язки даних складається в тому. що ваш вихідний об'єкт є елементом WPF, а вихідна властивість — властивістю залежностей. Це пояснюється тим, що властивості залежностей мають убудовану підтримку повідомлень про зміни. У результаті, коли ви міняєте значення властивості залежностей вихідного об'єкта, прив'язане властивість у цільовому об'єкті обновляється автоматично. Це саме те. що вам потрібно, і для цього не потрібно будувати ніякої додаткової інфраструктури. Щоб зрозуміти, як можна прив'язати один елемент до іншого, розглянемо приклад наступного вікна. Воно містить два елементи керування- Slider і TextBlock з єдиним рядком тексту. Якщо перетягнути бігунок вправо, розмір шрифту тексту негайно збільшиться. Якщо зрушити його вліво, розмір шрифту зменшиться. Ясно, що було б зовсім неважко запрограмувати таке поводження в коді. Для цього вам усього лише знадобилося б реагувати на подію ValueChanged і копіювати поточне значення бігунка в TextBlock. Однак прив'язка даних дозволяє зробити це ще простіше.

При використанні прив'язки даних вам не потрібно вносити ніяких змін у вихідний об'єкт (у цьому випадку- Slider). Просто сконфігуруйте його, щоб він приймав правильний діапазон значень, як це звичайно робиться:

<Slider Name="sliderFontSize" Margin="3" Minimum="l" Maximum="40" Value="10" TickFrequency="l" TickPlacement="TopLeft"> 
</Slider> 

Прив'язка задається в елементі TextBlock. Замість установки FontSize із застосуванням літерального значення ви використовуєте вираження прив'язки, як показано нижче:

<TextBlock Margin="10" Text="Simple Text" Name="lblSampleText" FontSize="{Binding ElementName=sliderFontSize, Path=Value}" > 
</TextBlock>

Вираження прив'язки даних використовують розширення розмітки XAML (звідси й фігурні дужки). Вираження починається зі слова Binding, оскільки ви створюєте екземпляр класу System.Windows.Data.Binding. Хоча ви можете сконфігурувати об'єкт Binding декількома способами, у даній ситуації потрібно встановити всього дві властивості: ElementName, що вказує елемент-джерело, і Path, що вказує, яка саме властивість прив'язується елемента-джерела.

Варто наголосить на тому, що WPF не збуджує винятків, щоб сповістити вас про проблеми прив'язки даних. Якщо ви специфікуєте неіснуючий елемент або властивість, то не одержите ніякої вказівки на таку помилку — замість цього дані просто не з'являться в цільовій властивості.

Створення прив'язки в коді

При побудові вікна звичайно найкраще повідомляти ваше вираження прив'язки в коді розмітки XAML, використовуючи розширення Binding. Однак прив'язку можна також створити й у програмному коді.

От як можна створити прив'язку TextBlock. показану в попередньому прикладі:

Binding binding = new Binding ПРО ; 
binding.Source = sliderFontSize; 
binding.Path = new PropertyPath(«Value»); 
binding.Mode = BindingMode.TwoWay; 
IblSampleText.SetBinding(TextBlock.TextProperty, binding);

Якщо ви хочете видалити прив'язку, щоб установлювати значення властивості звичайним способом, вам знадобиться допомога методів ClearBinding() і ClearAllBindings().

Напрямок прив'язки

Дотепер ви познайомилися з односпрямованою й двонаправленою прив'язками. Насправді WPF дозволяє при установці властивості Binding. Mode застосовувати одне з п'яти значень перерахування System.Windows.Data.BindingMode. Oneway. Цільова властивість оновлюється при зміні властивості-джерела. TwoWay. Цільова властивість обновляється при зміні властивості-джерела, і властивість-джерело обновляється при зміні цільової властивості.

OneTime. Цільова властивість установлюється споконвічно на основі значення властивості-джерела. Однак із цього моменту зміни ігноруються (якщо тільки прив'язка не встановлена на зовсім інший об'єкт). Звичайно ви використовуєте цей метод для скорочення накладних витрат, якщо знаєте, що вихідна властивість не буде змінюватися.

OneWaySource. Подібно OneWay, тільки навпаки. Вихідна властивість оновлюється при зміні цільової властивості (що може здатися трохи «задом наперед»), але цільова властивість ніколи не оновлюється. Default. Тип прив'язки залежить від цільової властивості. Воно буде або TwoWay (для встановлюваних користувачем властивостей, таких як TextBox.Text), або OneWay (для всіх інших). Всі прив'язки використовують цей підхід, якщо тільки ви не вкажете іншої.

Прив'язка користувальницьких об'єктів до бази даних

Коли розроблювачі чують термін прив'язка даних, то вони часто думають про один специфічний її застосунок — одержанні інформації з бази даних і відображенні на екрані з мінімальним обсягом коду або взагалі без нього. Як ви вже бачили, прив'язка даних в WPF — набагато ширше поняття. Навіть якщо ваш застосунок ніколи не вступає в контакт із базою даних, все-таки зберігається ймовірність застосування прив'язки даних для автоматизації взаємодії елементів між собою або трансляції об'єктної моделі в наочне подання. Однак ви повинні знати багато подробиць про прив'язку об'єктів, для чого ми розглянемо традиційний приклад, що запитує й оновлює таблицю бази даних. Перш ніж звернутися до цього, вам належить довідатися про спеціальний компонент доступу до даних і об'єкті даних, використовуваному в цьому прикладі.

При створенні класу доступу до даних ви повинні слідувати декільком базовим правилам, які описані нижче. • Відкривати й закривати з'єднання швидко. Відкривайте з'єднання з базою даних при кожному виклику методу й закривайте перед завершенням методу. Таким чином, з'єднання не залишаться відкритими з необережності. Один зі способів гарантувати закриття з'єднання в належний час — використовувати блок using. • Реалізувати обробку помилок. Використовуйте обробку помилок, щоб гарантувати закриття з'єднань навіть у випадку виникнення винятків. • Додержуватися практики дизайну, що не підтримує стан. Передавайте всю необхідну для методу інформацію в його параметрах і повертайте всі витягнуті дані через значення повернення. Це дозволить уникнути ускладнень у багатьох сценаріях (наприклад, якщо ви хочете створити багатопотоковий застосунок або розташувати ваш компонент бази даних на сервері). • Зберігати рядок підключення в одному місці. В ідеалі такими місцями повинен бути конфігураційний файл вашого застосунка.

Прив'язка до вираження LINQ

Однієї із причин переходу від .NET 3.0 до .NET 3.5 є підтримка мови інтегрованих запитів (Language Integrated Query — LINQ). Використовуючи підтримку, включену в .NET 3.5. ви можете використовувати однаково структуровані запити LINQ для добування даних з колекції, що перебуває в пам'яті, з файлу XML або з бази даних SQL Server. Як і інші мови запитів. LINQ дозволяє фільтрувати, сортувати й трансформувати дані.

Хоча LINQ виходить за рамки теми, що розглядається в даному розділі, ви можете багато чого довідатися про нього із простого приклада. Наприклад, припустимо, що у вас є колекція об'єктів Product з іменем products, і ви хочете створити другу колекцію, що містить тільки ті продукти, ціна яких перевищує $100. Використовуючи процедурний код. Ви можете написати щось таке:

// Одержати повний список продуктів. 
List<Product> products = App.StoreDB.GetProducts(); 
// Створити другу колекцію з потрібні продуктам;:. 
List<Product> matches = new List<Product> (); 
foreach (Product product in products) 
if (product.UnitCost >= 100) 
matches.Add(product); 

Використовуючи LINQ. ви можете написати наступне, набагато більше зручне вираження:

// Одержати повний список продуктів. 
List<Product> products = App.StoreDB.GetProducts();
// Створити другу колекцію з відповідними продуктами
IEnumerable<Product> matches =
from product in products 
where product.UnitCost >= 100 
select product; 

Цей приклад використовує API-Інтерфейс LINQ to Collections, тобто застосовує вираження LINQ для витягнення запиту даних, що знаходяться в пам'яті. Вираження LINQ використовують набір нових ключових слів мови, включаючи from, in і select. Ці ключові слова LINQ становлять невід'ємну частин мови С#.

Шаблони

Виявляється, що в кожного елемента керування є вбудований «рецепт», що визначає те, як він повинен візуалізуватись (у вигляді групи більше базових елементів).

Цей рецепт називається шаблоном елемента керування (control template) і визначається за допомогою блоку XAML-розмітки. Коли створюється шаблон елемента керування, новий шаблон заміняє існуючий повністю. Це забезпечує найвищий ступінь гнучкості, але при цьому також ще й трохи ускладнює життя. У більшості випадків перед створенням своєї власної адаптованої версії потрібно переглянути стандартний шаблон, що використовує даний елемент керування. У деяких випадках спеціальний шаблон елемента керування може повністю повторювати стандартний шаблон, за винятком якоїсь однієї незначної зміни.

У документації WPF код XAML стандартних шаблонів елементів керування не наведений. Однак всю необхідну інформацію можна одержати програмно. У цілому потрібно зробити наступне: витягти шаблон елемента керування з його властивості Template (яке є частиною класу Control) і потім серіялізувати його в XAML за допомогою класу XamlWriter.

Стилі

Стиль — це колекція значень властивостей, які можуть бути застосовані до елемента. Система стилів WPF грає ту ж роль, що й стандарт каскадних таблиць стилів (Cascading Style Sheet — CSS) грає в HTML-коді розмітки. Подібно до CSS, стилі WPF дозволяють визначати загальний набір характеристик форматування й застосовувати їх у всьому застосунку для забезпечення узгодженості. Як і CSS, стилі WPF можуть працювати автоматично, призначатися для елементів конкретного типу й каскадуватись через дерево елементів. Однак стилі WPF є могутнішими, тому що можуть установлювати будь-яку властивість залежностей.

Переваги стилів показані на наступному простому прикладі. Нехай треба стандартизувати використовуваний у вікні стиль. Найпростішим підходом буде встановити відповідальні за шрифт властивості утримуючий цей шрифт вікна. До числа таких властивостей, які, до речі, визначаються в класі Control, ставляться FontFamily. For.tSize. FontWeight (для шрифту з напівжирним накресленням). FoncStyle (для шрифту з курсивним накресленням) і Font St retch. Завдяки функції спадкування значень властивостей, при установці цих властивостей на рівні вікна всі елементи усередині вікна одержать однакові значення, якщо тільки вони явно не перевизначають їх.

Тепер давайте візьмемо іншу ситуацію. Уявіть, що потрібно заблокувати шрифт, використовуваний для якихось певних елементів у користувальницькому інтерфейсі. Якщо є можливість ізолювати ці елементи в спеціальний контейнер (наприклад, якщо вони все перебувають усередині одного елемента керування Grid або StackPanel). тоді в принципі можна скористатися тим же самим підходом і встановити відповідальні за шрифт властивості цього контейнера. Однак у реальному житті все зазвичай виявляється не так просто. Наприклад, може бути необхідно, щоб у всіх кнопок була своя гарнітура й розмір шрифту, що не залежить від параметрів шрифту, які використовуються в інших елементах. У такому випадку потрібний спосіб, що дозволяє визначити ці деталі в одному місці й повторно використовувати їх скрізь, де вони будуть необхідні.

Стилі пропонують ідеальне рішення. Можна визначити один єдиний стиль, що впаковує всі властивості, які потрібно встановити. Наприклад, можна надійти так, як показано нижче.

<Window.Resources> 
<Style x:Key="BigFontButtonStyle"> 
<Setter Property="Control.FontFamily" Value="Times New Roman" /> 
<Setter Property="Control.FontSize" Value="8" /> 
<Setter Property="Control.FontWeight" Value="Bold" /> 
</Style> 
</Window.Resources>

У цьому коді розмітки створюється один єдиний ресурс: об'єкт System.Windows. Style. У цьому об'єкті втримується колекція Setters із трьома об'єктами Setter, по одному для кожної властивості, що потрібно встановити. У кожному об'єкті Setter вказується ім'я властивості, на яке він впливає, і значення, що він повинен застосовувати до цієї властивості. Як і всі ресурси, об'єкт стилю має ключове ім'я, за яким його можна при необхідності витягати з колекції. У цьому разі це ключове ім'я виглядає як BigFontButtonStyle. (За загальноприйнятою згодою ключові імена стилів звичайно закінчуються словом «Style».) Система стилів додає безліч переваг. Вона не тільки дозволяє створювати групи параметрів, чітко зв'язаних між собою, але й також спрощує код розмітки, полегшуючи застосування цих параметрів. Найкраще те, що стиль можна застосовувати, не турбуючись про те, які властивості він установлює. У попередньому прикладі параметри шрифту були організовані в стиль за назвою BigFontButtonStyle. Якщо пізніше раптом знадобиться забезпечити кнопки з великим шрифтом областями Padding і Margin, у код можна буде також дуже легко додатково додати, відповідно, властивості Padding і Margin, після чого всі, що використовують даний стиль кнопки, автоматично одержать нові параметри стилю.

Колекція Setters є найважливішою властивістю класу Style. Але в принципі ключовими вважаються цілих п'ять властивостей, короткий опис яких наведено в табл.

Setters Колекція об'єктів Setter або EventSetter, які автоматично встановлюють значення властивостей і додають оброблювачі подій.

Triggers Колекція об'єктів, які успадковуються від TriggerBase і дозволяють автоматично змінювати параметри стилю. Параметри стилю можуть змінюватися, наприклад, або у випадку зміни значення якоїсь іншої властивості, або у випадку виникнення якої-небудь події.

Resources Колекція ресурсів, які повинні використовуватися зі стилями. Наприклад, може виникнути необхідність використовувати один єдиний об'єкт для установки більш ніж однієї властивості. У такому випадку більше ефективним варіантом буде створити об'єкт як ресурс і потім використовувати цей ресурс в об'єкті Setter (чим створювати його у вигляді частини кожного об'єкта Setter, використовуючи вкладені дескриптори). BasedOn Властивість, що дозволяє створювати більше спеціалізований стиль, що успадковує (і, при бажанні, що перевизначає) параметри іншого стилю.

TargetType Властивість, що ідентифікує тип елемента, на який діє даний стиль. Ця властивість дозволяє створювати як об'єкти Setter, що впливають тільки на певні елементи, так і об'єкти Setter, що автоматично вступають у силу для елементів конкретного типу.

Власне кажучи, застосовувати стилі й ресурси разом зовсім необов'язково. Наприклад, стиль певної кнопки можна визначити, і просто прямо заповнивши її колекцію Style, як показано нижче:

<Button Padding=5 Margin=5"> 
<Button.Style> 
<Style> 
<Setter Property="Control.FontFamily" Value="Times New Roman" /> 
<Setter Property="Control.FontSize" Value=8" /> 
<Setter Property="Control.FontWeight" Value="Bold" /> 
</Style> 
</Button.Style> 
<Button.Content>A Customized Button</Button.Content> 
</Button>

Автоматичне застосування стилів за типом

Поки що було показано, як можна створювати іменовані стилі й посилатися на них у коді розмітки. Однак існує ще один підхід. Можна робити так, щоб до елементів певного типу стиль застосовувався автоматично. Робиться це доволі просто. Усе, що потрібно- це встановити властивість TargetType так, щоб воно вказувало на потрібний тип (як описувалося раніше), і взагалі не використовувати ключове ім'я. Тоді WPF установлює ключове ім'я неявно за допомогою розширення розмітки типу, як показано нижче: х:Кеу="{х:Турі Button}"

Далі стиль автоматично застосовується до будь-яких кнопок, що зустрічаються нижче в дереві елементів. Наприклад, якщо визначити стиль подібним чином у вікні, він буде застосовуватися до кожної кнопки в цьому вікні (за умови відсутності далі в коді ще одного стилю, що заміняє його).

Хоча автоматично застосовувані стилі й зручні, вони можуть ускладнювати дизайн. Нижче перераховано кілька можливих причин.

  • У складному вікні з великою кількістю стилів і рівнів стилів стає важко відслідковувати те, чи встановлюється дана властивість за допомогою спадкування значень властивостей або за допомогою стилю (і якщо воно встановлюється за допомогою стилю, то за допомогою якого саме). У результаті при необхідності змінити навіть яку-небудь просту деталь може знадобитися перегляд коду розмітки всього вікна.
  • Форматування у вікні часто спочатку є більше загальним, а потім поступово ускладнюється. У разі застосування до вікна автоматичних стилів де-небудь на ранньому етапі, швидше за все, далі ці стилі буде потрібно перевизначати в багатьох місцях за допомогою явних стилів. А це значно ускладнює весь дизайн. Набагато простіше буде створити іменовані стилі для кожної комбінації необхідних характеристик форматування й застосовувати їх за іменем.
  • У разі створення автоматичного стилю, наприклад, для елемента TextBox, обов'язково прийдеться вносити зміни в інші елементи керування, які використовують цей елемент TextBlock (наприклад, у керований шаблоном елемент керування ListBox).

Щоб уникнути таких проблем, найкраще застосовувати автоматичні стилі розважливо. Наприклад, може виявитися найкраще використовувати автоматичний стиль не для всього вікна, а тільки для додання одноманітного заповнення кнопкам або для керування параметрами полів елементів TextBox у конкретному контейнері.

Локалізація

Локалізація WPF-застосунків концептуально відрізняється від підходу до багатомовності інтерфейсів у WinForms. Тут одиницею локалізації є XAML-файл (з технічної точки зору — скомпільований BAML-pecypc. який вбудовується в застосунок). При бажанні підтримувати три різні мови, необхідно буде включити три BAML-pecypca. WPF буде вибирати з них відповідний на підставі поточних настроювань культури на тому комп'ютері, на якому виконується застосунок. (З технічної точки зору WPF буде використовувати для прийняття рішення значення властивості CurrentUICulture з потоку користувацького інтерфейсу).

Звичайно, у такому процесі не було б особливого змісту, якби потрібно було створювати (і розгортати) універсальне складання з усіма локалізованими ресурсами.

Це було б не краще, ніж створювати окремі версії застосунка для кожної мови, оскільки застосунок доводилося би повністю компонувати заново при кожній необхідності додати підтримку для якоїсь нової культури (або необхідності настроїти текст в одному із уже існуючих ресурсів). На щастя, в. NET ця проблема вирішується за допомогою підлеглих збірок (satellite assemblies), які працюють із застосунком, але зберігаються в окремих каталогах. При створенні локалізованого WPF-застосунка кожний локалізований BAML-pecypc міститься в окремій підлеглій збірці. Для того щоб застосунок міг використовувати цю збірку, її розміщують в підпапці під основною папкою застосунка, наприклад, з іменем fr-FR, призначеної для французької мови (Франція). Після цього застосунок може зв'язуватися із цією підлеглою збіркою автоматично шляхом використання технології зондування (probing), що є частиною .NET Framework, починаючи з версії 1.0.

Найбільші труднощі в локалізації застосунка полягають у робочому процесі — іншими словами в тому, як витягти XAML-файли із проекту, зробити їх локалізованими, скомпілювати в підлеглі складання й потім повернути назад в застосунок. Створення локалізованих користувальницьких інтерфейсів

Перш ніж починати що-небудь перекладати, спочатку потрібно розібратися з тим, як застосунок буде реагувати на зміну вмісту. Наприклад, якщо подвоїти довжину всього тексту в користувальницькому інтерфейсі, то як зміниться загальне компонування вікна? У випадку якщо було розроблене дійсно гнучке компонування, ніяких проблем виникнути не повинне. Інтерфейс повинен бути здатний підбудуватися відповідно до динамічного вмісту. Нижче описані деякі рекомендації, що гарантують коректне відображення локалізованих елементів.

  • Не застосовувати жорстко закодовані значення ширини або висоти.
  • Установлювати для властивості Window.SizeToContent значення Width, Height або WidthAndHeight, так щоб розмір вікна міг збільшуватися в міру необхідності. (Знов-таки, це є обов'язковим не завжди; все залежить від структури вікна, але в деяких випадках буває дуже навіть корисним.)
  • Використовувати для впакування великих обсягів тексту елемент ScrollViewer.

Підготовка застосунка для локалізації

Наступний крок — включити підтримку локалізації для проекту. Для цього буде потрібно внести тільки одну зміну, а саме — додати у файл .csproj проекту де-небудь у першому розділі <PropertyGroup> наступний елемент:

<UICulture> uk-UK</UICulture>

Це вкаже компіляторові, що мовою (культурою) за замовчуванням для застосунка повинен бути, наприклад, українська (при необхідності можна вибрати й іншу мову). Після внесення цього коректування процес компонування зміниться. При наступній компіляції застосунка буде створений підкаталог з іменем uk-UK. Усередині цієї папки буде перебувати підлегла збірка з таким же іменем, як і в застосунка, і розширенням .resources.dll (наприклад. LocalizableApplication.resources.dll).

У цьому складанні будуть утримуватися всі скомпільовані BAML-ресурси для застосунка, які раніше зберігалися в основному складанні застосунка.

Тепер при запуску даного застосунка загальномовне виконуюче середовище (CLR) буде автоматично шукати підлеглі складання у відповідному каталозі на підставі регіональних параметрів комп'ютера, і завантажувати відповідний локалізований ресурс. Наприклад, при запуску застосунка на комп'ютері з культурою fr-FR, середовище CLR буде відшукувати підкаталог fr-FR і використовувати підлеглі складання, які виявить там. Це означає, щоб додати в локалізований застосунок підтримку для додаткових культур, буде потрібно просто додати відповідні додаткові підкаталоги й підлеглі збірки, не турбуючись про вихідний виконуваний файл.

Коли середовище CLR починає зондувати підкаталоги на предмет наявності підлеглої збірки, воно слідує кільком простим правилам черговості.

  1. Спочатку воно перевіряє найспецифічніший із всіх доступних каталогів. Це означає, що воно шукає підлегле складання, призначене і для поточної мови, і регіону (на зразок fr-FR).
  2. Якщо йому не вдається відшукати такий каталог, воно починає шукати підлеглу збірку, призначену для поточної мови (такого як fr).
  3. Якщо йому не вдається знайти і такий каталог, тоді воно генерує виняток IOException.

Цей список є трохи спрощеним. Тим, хто вирішить застосовувати глобальний кеш збірок (Global Assembly Cache — GAC) для спільного використання якихось компонентів у всьому комп'ютері, варто знати, що .NET фактично перевіряє GAC на початку кроків 1 і 2. Інакше кажучи, на першому кроці CLR-середовище перевіряє, чи немає потрібної версії складання у кеші GAC, і якщо є, використовує його. Те ж саме відбувається й на кроці 2.

Процес перекладу

Коли вся необхідна для локалізації інфраструктури готова, залишається тільки створити відповідні підлеглі складання з альтернативними версіями вікон (у формі BAML) і розмістити їх у відповідних каталогах.

  1. Ви позначаєте в застосунку елементи, які потрібно локалізувати. При бажанні ви можете також додати додаткові коментарі для того, щоб допомогти перекладачеві.
  2. Далі ви витягуєте локалізовані деталі у файл .cvs (текстовий файл із роздільниками комами) і відправляєте його перекладачеві.
  3. Після одержання перекладеної версії цього файлу ви знову запускаєте утиліту locbaml, щоб згенерувати необхідне підлегле складання.

Перше, що потрібно зробити — додати в усі елементи, які потрібно локалізувати, спеціальний атрибут Uid. Наприклад:

<Button x:Uid="Button1" Margin="10" Padding=3">A button</Button>

Атрибут Uid відіграє роль подібну тієї. що виконує атрибут Name — у цьому випадку він унікальним чином ідентифікує кнопку в контексті одного XAML-документу. Це дозволяє вказати локалізований текст тільки для цієї кнопки. Однак існують кілька причин того, чому в WPF використовується атрибут Uid, а не значення Name, одна з яких полягає в тому, що елементові керування ім'я може не призначатися, а встановлюватися відповідно до різних домовленостей і застосовуватися в коді. Насправді властивість Name є таким фрагментом інформації, що локалізується.

Хоча й необов'язково, але атрибут Uid найкраще додати до кожного елемента в кожному вікні застосунка, який локалізується. Це може створити масу додаткових зусиль, однак існує утиліта msbuild.ехе, що вміє робити це автоматично. Потрібно просто використовувати її в такий спосіб:

msbuild /t:updateuid LocalizableApplication.csproj

Тут передбачається, що атрибути Uid потрібно додати в застосунок за назвою LocalizableApplication. Якщо потрібно перевірити, чи у всіх елементів є атрибути Uid (і чи не був який-небудь із них випадково продубльованим), можна використати утиліту msbuild.exe у такий спосіб:

msbuild /t:checkuid LocalizableApplication.csproj

У разі генерації атрибутів Uid за допомогою msbuild, вони встановлюються відповідно до імені елемента керування, до якого ставляться.

Для отримання вмісту всіх елементів служить утиліта командного рядка locbaml. У цей час locbaml не поставляється як скомпільований засіб. Замість цього її вихідний код доступний у вигляді зразка (у довідці Visual Studio, у присвяченому locbaml розділі) і повинен компілюватися вручну.

Використовуючи locbaml, варто обов'язково перебувати саме в тій папці, у якій утримується скомпільоване складання (наприклад, це може бути папка LocalizableApplication\bin\Debug). Щоб витягти список деталей для локалізації, потрібно вказати locbaml на підлеглу збірку й задати параметр /parse, як показано нижче: locbaml /parse en-US\LocalizableApplication.resources.dll

Утиліта locbaml виконує в підлеглі збірці пошук всіх наявних у ній скомпільованих BAML-ресурсів і генерує файл .cvs з деталями. У даному прикладі цей файл .csv одержить ім'я LocalizationApplication.resources.csv.

Кожний рядок у отриманому файлі представляє одну властивість для локалізації, що застосовувалося для того або іншого елемента в документі XAML, і складається із семи перерахованих нижче значень.

  • Ім'я BAML-pecypca (наприклад. LocalizableApplication.g.en-US.resources: windowl.baml).
  • Uid елемента й ім'я властивості, що підлягає локалізації. Наприклад: StackPanel_l: System.Windows.FrameworkElement.Margin.
  • Категорія локалізації. Це значення береться з перерахування LocalizationCategory, яке допомагає ідентифікувати тип умісту, що представляється даною властивістю, (довгий текст, заголовок, шрифт, напис на кнопці, що спливає, підказка й т.д.).
  • Значення, що показує, чи є дана властивість прочитною (тобто представленим у вигляді тексту в користувальницькому інтерфейсі). Всі прочитні значення повинні локалізуватися завжди, у той час як нечитні значення можуть вимагати, а можуть і не вимагати локалізації.
  • Значення, що показує, чи можна значення даної властивості змінювати перекладачеві. Завжди виглядає як true, якщо тільки його спеціально не змінити на протилежне (false).
  • Додаткові коментарі, які були надані для перекладача. Якщо коментарі не надавалися, це значення буде порожнім.
  • Значення властивості. Це саме та деталь, що і повинна локалізуватися.

Тепер ви має нагоду відкрити згенерований файл у текстовому редакторі й замінити (перекласти) необхідний текстовий контент у відповідну мову, зберігши переклад у новий файл.

Тепер можна переходити до створення підлеглих збірок для інших культур. Знов-таки, для виконання цієї задачі потрібна утиліта locbaml, але цього разу її варто використовувати з параметром /generate.

Не забувайте про те, що підлегле складання буде містити альтернативну копію кожного цілого вікна у вигляді вкладеного BAML-pecypca. Для того щоб створити такі ресурси, утиліта locbaml повинна переглянути вихідну підлеглу збірку, замінити всі нові значення з переведеного файлу .cvs і потім згенерувати нову підлеглу збірку. А це означає, що ви повинні вказати утиліті locbaml на вихідну підлеглу збірку й (за допомогою параметра /trans:) на перекладений список значень. Ви також повинні повідомити її, яку культуру представляє це складання (за допомогою параметра /cul:). Майте на увазі, що культури визначаються за допомогою двоскладних ідентифікаторів, які перераховані в описі класу System.Globalization.Culturelnfo.

Нижче показаний приклад, що ілюструє все це:

locbaml /generate en-US\LocalizableApplication.resources.dll /trans:LocalizableApplication.resources.French.csv /cul: fr-FR /out: fr-FR 

Ця команда робить описані нижче речі.

  • Використовує вихідне підлегле складання en-US\LocalizedApplication. resources.dll.
  • Використовує перекладений . cvs-файл French.cvs.
  • Використовує культуру «французька мова (Франція)».
  • Розташовує результат у підпапку fr-FR (яка повинна вже існувати). Хоча може здатися, що це повинне виконуватися неявним чином на підставі використовуваної культури, однак, дану деталь все-таки варто надавати обов'язково.

При виконанні цієї команди утиліта locbaml створить нову версію складання LocalizableApplication.resources.dll з перекладеними значеннями й помістить її в підпапку застосунку з іменем f r-FR.

Тепер, у випадку запуску даного застосунка на комп'ютері, де для параметра культури встановлена французька мова (Франція), автоматично з'явиться альтернативна версія вікна. Змінити культуру можна в доступному через панель керування розділі Regional and Language Options (Мова й регіональні стандарти) або. за допомогою коду, що змінює параметр культури тільки поточного потоку. Наприклад,

Thread.CurrentThread.CurrentUICulture = new CultureInfo(«fr-FR»)

Трансформація

Величезний обсяг завдань, пов'язаних з малюванням, може бути спрощений завдяки використанню трансформації — об'єкта, що змінює спосіб малювання фігури або елемента за допомогою схованого зрушення використовуваної їм координатної системи. B WPF трансформації представлені класами, успадкованими від абстрактного класу System.Windows.Media.Transform.

Мають місце такі трансформації:

  • TranslaceTransform — зміщає вашу координатну систему на певну х, у величину. Ця трансформація зручна, якщо ви хочете намалювати ту ж фігуру в різних місцях;
  • RotateTransform — повертає вашу координатну систему на Angle градусів, намальовані фігури повертаються навколо заданої точки Center. Саме параметр кута анімується від 0 до 360 градусів у даному прикладі;
  • ScaleTransform — масштабує координатну систему в більшу або меншу сторону. Можна застосовувати різний ступінь масштабування за вимірами X і Y, таким чином розтягуючи або стискаючи фігуру;
  • SkewTransform — деформує координатну систему, нахиляючи її на певне число градусів. Наприклад, якщо намалювати квадрат, то після трансформації він стане паралелограмом;
  • MatrixTransform — модифікує координатну систему, використовуючи матричне множення на призначену матрицю. Це найскладніша трансформація — вона вимагає певної математичної підготовки.
  • TransformGroup — комбінує кілька трансформацій таким чином, — що вони можуть застосовуватися одночасно. Порядок застосування трансформацій важливий — він впливає на кінцевий результат. Наприклад, обертання фігури (за допомогою RotateTransform) з наступним переміщенням її (за допомогою TransiateTransform) перемістить фігуру в інше місце, чим якщо ви спочатку перемістите її, а потім повернете.

Анімація

Анімація — центральна частина моделі WPF. Це значить, що вам не потрібно використовувати таймери й код обробки подій, щоб привести її в дію. Замість цього ви створюєте її декларативно, не написавши жодного рядка коду С#. Також анімація зовсім непомітно сама інтегрується у звичайні вікна WPF і на сторінки. Наприклад, якщо ви анімуєте кнопку таким чином, що вона дрейфує вікном, проте, вона при цьому продовжує поводитися як кнопка. Її можна стилізувати, вона приймає фокус і на ній можна клацнути, запустивши звичайний код оброблювача події.

Уявіть приклад украй простої анімації, що розширює кнопку. Коли ви клацаєте на кнопці. WPF плавно розсовує обидві сторони кнопки, поки вона не заповнить вікно.

Щоб створити такий ефект, ви використовуєте анімацію. що модифікує властивість Width кнопки. Нижче наведений код. що створює й запускає цю анімацію при клацанні на кнопці.

DoubleAnimation widthAnimation = new DoubleAnimation() ; 
widthAnimation.From = 160;
widthAnimation.To = this.Width — 30;
widthAniamtion.Duration = TimeSpan.FromSeconds(5);
cmdGrow.BeginAnimation(Button.WidthProperty, widthAnimation);

Є три деталі, які становлять необхідний мінімум для опису анімації, що використовує лінійну інтерполяцію: початкове значення (From), кінцеве значення (To) і час, за яке анімація повинна виконатись (Duration). У даному прикладі кінцеве значення засноване на поточній ширині утримуючу кнопку вікна.

Бітові ефекти

Бітові ефекти WPF — готові візуальні ефекти, які можна застосувати до будь-якого елемента. Ціль ефектів — надати простий декларативний спосіб удосконалення зовнішнього вигляду тексту, зображень, кнопок тощо. Замість написання власного коду малювання ви використовуєте один із класів-спадкоємців BitmapEffect (із простору імен System.Windows.Media.Effects), щоб одержати миттєвий ефект типу розмивання, відблисків і тіней. Мають місце такі ефекти:

  • BlurBitmapEffect — розмиває вміст елемента;
  • BevelBitmapEffect — додає опуклу рамку навколо вмісту. Цей ефект реалізовано стосовно кнопок «Гаразд» та «Відміна»;
  • EmbossBitmapEffect — створює ефект «тиснення», виділяючи границі й лінії, як якби вони були вирізані або видавлені;
  • OuterGlowBitmapEffect — додає кольорове сяйво навколо вашого вмісту. Якщо якийсь елемент управління є активним, то можна звернути увагу на цей ефект;
  • DropShadowBitmapEffect — додає прямокутну тінь, що, за вашим елементом.
  • BitmapEffectGroup Застосовує комбінацію бітових ефектів. Порядок вказівки ефектів має значення, оскільки кожний застосовується поверх існуючих. BitmapEf fectGroup використовується рідко, оскільки множить накладні витрати. Інакше кажучи, кнопка із чотирма ефектами вимагає вчетверо більше обробки, чим кнопка з одним ефектом.

Переваги та особливості тривимірної графіки

Підтримка тривимірної графіки — одна з перлин платформи WPF. Попередні високорівневі набори інструментів розробки, такі як Windows Forms, взагалі обходилися без підтримки 3-D, залишаючи її фанатам Direct. Фактично найбільше вражає в тривимірних засобах WPF простота роботи з ними. Хоча можна створити складний код. що генерує і модифікує тривимірні сітки із залученням серйозного математичного апарату, але з тим же успіхом можна просто експортувати тривимірні моделі з інструменту проектування й маніпулювати ними за допомогою простих трансформацій. Ключові засоби забезпечуються високорівневими класами, що не вимагають глибоких знань.

Попри те, що модель тривимірної графіки WPF одразу ясна й погоджена, створення багатих тривимірних інтерфейсів залишається досить складною задачею. Для того щоб вручну закодувати тривимірну анімацію (або навіть просто зрозуміти покладені в її основу концепції), вам знадобиться щось більше, ніж трохи математики. А моделювання чого-небудь, крім найтривіальніших тривимірних сцен на основі вручну написаного коду XAML — це величезна робота, набагато більше складна, ніж двовимірний еквівалент ручного створення векторного зображення XAML. Із цієї причини, швидше за все, вам варто звернутися до інструментів від незалежних розроблювачів для створення тривимірних об'єктів, експортувати їх в XAML. а потім додавати до своїх застосунків.

Тривимірна графіка в WPF містить у собі такі інгредієнти:

  • вікно перегляду (viewport), що містить ваш тривимірний вміст:
  • тривимірний об'єкт:
  • джерело світла, що висвітлює частину вашої тривимірної сцени:
  • камера, що представляє точку, з якої ви спостерігаєте тривимірну сцену.

Звичайно, найскладніші тривимірні сцени будуть містити множину об'єктів і можуть включати множину джерел світла. (Можна також створити тривимірний об'єкт, що не вимагає джерела світла, якщо він сам є таким.) Однак перераховані інгредієнти являють собою гарний початко.

Вікно перегляду може містити в собі будь-який тривимірний об'єкт, успадкований від класу Visual3D (із простору імен System.Windows.Media.Media3D. у якому перебуває переважна більшість тривимірних класів).

Щоб побудувати тривимірний об'єкт, треба почати з побудови геометрії. для цієї мети існує тільки один клас — MeshGeometry3D. Не дивно, що об'єкт MeshGeometry3D представляє сітку (mesh). Якщо раніше ви мали справу із тривимірним малюванням (або читали що-небудь про технології, покладених в основу сучасних відеокарт), то вже повинні знати, що комп'ютери воліють будувати тривимірні об'єкти із трикутників. Це пояснюється тим. що трикутники — це найпростіший, найдокладніший спосіб опису поверхні.

Трикутники прості, тому що кожний з них визначається всього трьома точками (вершинами в кутах). Дуги й криві поверхні виразно більше складні. Трикутники докладні, тому що всі інші фігури із прямими гранями (квадрати, прямокутники й більше складні багатокутники) можуть бути розбиті на колекції трикутників. Добре це або погано, але сучасне апаратне забезпечення графіки і її програмування будується на основі цієї базової абстракції.

Визначивши потрібну властивість MeshGeometry3D, потрібно помістити його в оболонку GeometryModel3D. Клас GeometryModel3D має тільки три властивості: Geometry, Material і BackMaterial. Властивість Geometry приймає MeshGeometry3D, що визначає фігуру вашого тривимірного об'єкта. На додаток можна застосовувати властивості Material і BackMaterial для визначення поверхонь, з яких складається ваша фігура. Поверхня важлива через дві причини. По-перше, вона визначає колір об'єкта (хоча можна використовувати й складніші кисті, що малюють текстури замість суцільного кольору). По-друге, вона визначає те, як матеріал реагує на світло. WPF включає чотири класи матеріалів, кожний з яких успадковується від абстрактного класу Material із простору імен System.Windows.Media.Media3D. Цими матеріалами є DiffuseMaterial (створює пласку матову поверхню, що розподіляє світло рівномірно у всіх напрямках), SpecularMaterial (створює блискучу безбарвну поверхню (типу металу або скла). Відбиває світло в протилежному напрямку подібно дзеркалу.), EmissiveMaterial (створює розпечену поверхню, що сама випромінює світло, хоча це світло не відбивають інші об'єкти сцени), MaterialGroup (дозволяє комбінувати більше одного матеріалу. Матеріали, що комбінуються накладаються один на одного в тім порядку, у якому додавалися до MaterialGroup).

Щоб створити реалістично пофарбовані тривимірні об'єкти, WPF використовує модель висвітлення. Основна ідея в тому, що ви додаєте один (або трохи) джерел світла до тривимірної сцени. Характер висвітлення ваших об'єктів залежить від обраного типу джерела світла, його положення, напрямку й інтенсивності.

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

  • Світлові ефекти обчислюються для об'єктів індивідуально. Світло, відбите від одного об'єкта, не відбивається в іншому об'єкті. Аналогічно, об'єкт не відкидає тіні на інший об'єкт, незалежно від його місця розташування.
  • Освітленість обчислюється для вершин кожного трикутника, а потім інтерполюється по його поверхні. (Іншими словами, WPF визначає інтенсивність світла в кожному куті, а потім згладжує його для заповнення всього трикутника.) У результаті такого дизайну об'єкти, що складаються з відносно невеликого числа трикутників, можуть бути освітлені неправильно. Щоб забезпечити краще висвітлення, вам належить ділити ваші фігури на сотні або навіть тисячу трикутників.

Класи джерел світла

  • DirectionalLight Заповнює сцену паралельними променями світла, що йдуть у зазначеному вами напрямку.
  • AmbientLight Наповнює сцену розсіяним світлом.
  • PointLight Світло поширюється в усі сторони із точкового джерела.
  • SpotLight Світло поширюється з однієї крапки по конусі.

Перш ніж тривимірна сцена буде відображена, вам потрібно розташувати камеру в коректній позиції і орієнтувати неї в правильному напрямку. Це робиться установкою у властивість Viewport3D.Camera об'єкта Camera. Вибір правильної камери — відносно проста задача, але розміщення й конфігурування її- трохи складніше. Перше, що потрібно, це специфікувати — крапку в тривимірному просторі, де буде позиціонована камера, установивши значення її властивості Position. Другий крок — установка тривимірного вектора у властивості LookDirection, що вказує орієнтацію камери. У типовій тривимірній сцені камера розміщається ледве далі одного з кутів сцени через властивість Position, а потім нахиляється для одержання необхідного виду за допомогою властивості LookDirection.

Звичайно, це лише першочерговий огляд тривимірних можливостей. Найпростіші приклади 3D-ефектів, а тим паче огляд вісіх концептуальних можливостей без перебільшення виходить за рамки представленого матеріалу.

Аспекти версії WPF 4.0 у MicroSoft Visual Studio 2010

12 квітня у світ має вийти MS Visual Studio 2010 — ще новіший, досконаліший та функціональніший інструмент для розробки windows-застосунків, в тому числі і за допомогою платформи WPF. У версії WPF4 міститимуться наступні можливості.

WPF4 додає різноманітні елементи керування, щоб створювати бізнес-застосунка ще швидше й простіше. Новий набір елементів керування включає такі необхідні елементи керування, як DataGrid, DatePicker і Calendar.

Windows 7 і елемент керування Office Ribbon

Він підтримує всі стандартні можливості, з якими знайомий кінцевий користувач: закладки й групи, динамічна зміна розмірів, панель швидкого доступу, меню застосунка, закладки, що залежать від контексту, гарячі клавіші й багато чого іншого.

Кешовані застосунки

Величезний виграш у продуктивності доступний з новою можливістю WPF4 — Cached Composition, що дозволяє застосункам кешувати довільний вміст, включаючи «живі» і повністю інтерактивні елементи керування, векторну геометрію, растрові зображення, що зберігаються у відеопам'яті й т.п. Помістивши один раз елементи в кеш, вони можуть бути довільно перетворені, анімовані, керовані, до них можна застосувати Effects, без необхідності перемальовувати закешовані елементи. Все це приводить до збереження CPU і GPU від перемальовування вмісту й дозволяє GPU перемальовувати прямо з кешу. Кеш розуміє «брудні» області, тому курсор, що блимає, у закешованому текстовому блоці, наприклад, повинен буде перемалювати тільки курсор між фреймами. З'явилася навіть нова кисть, що використовує «розумний» кеш, фактично це VisualBrush з поліпшеною продуктивністю.

Функції для полегшення анімації

Дискретна, лінійна й сплайнова анімація вже підтримується попередніми версіями WPF. WPF4 представляє новий концепт «Easing Functions», які дозволяють розробникам створювати різну анімацію. Наприклад, пружинисті рухи. Функції спрощення (Easing Functions) визначають спосіб анімації від початку й до кінця. Убудовані функції спрощення надають спектр режимів в анімації: кругової, експонентної, еластичної й пружинистої. Функції спрощення спроектовані легкорозширюваними, дозволяючи розробникам створювати власні. Завдяки даному нововведенню дизайнери можуть без зусиль створювати плавну й органічну анімацію. Новий стек для промальовування тексту WPF був повністю заміненим, що призвело до істотних удосконалень у чіткості промальовування тексту, здатності до зміни настроювань і підтримки міжнародних мов. Цей новий текстовий стек підтримує оптимізований символьний шар, щоб показувати текст із порівнянною різкістю тексту в Win32/GDI:

Так само новий стек підтримує явно обрані режими промальовування тексту: aliased, grayscale, ClearType.

Новий текстовий стек дозволяє оптимізувати текстову прив'язку для анімації й статики. До того ж, новий текстовий стек підтримує шрифти з убудованими картами зображень, що дозволяє багатьом східно-азіатським шрифтам промальовуватись із чіткістю, до якої звикли користувачі Win32.

Власні словники

WPF містить у собі перевірку орфографії, що до 4-ї версії використовувала словники, надавані ОС. Це було великою проблемою для застосунків, які використовують специфічну термінологію, дані застосунки набридали постійними повідомленнями про неправильне написання. WPF4 представляє API, що дозволяє застосункам додавати слова в словники, використовувані WPF для перевірки орфографії. http://blogs.msdn.com/text/archive/2009/10/02/custom-dictionaries.aspx Windows 7.

Підтримка мультитач

З поданням мультитач уведення й підтримки маніпулюванням обробки, WPF4 надає відмінний спосіб додати пікантності у ваш клієнтський застосунок в Windows 7. З'явилися нові події маніпуляції й інерції: мультитач події Manipulation, Inertia (Pan, Zoom, Rotate) над UIElement; прості мультитач події (Up, Move, Down) над UIElement, UIElement3D і ContentElement; захоплення декількох елементів керування; підтримка мультитач в ScrollViewer; розширюваність сенсорних пристроїв; сумісність із Surface SDK у майбутньому.

Інтеграція c Windows 7 інтерфейсом. WPF 4 надає нового й ключового функціоналу в Windows 7 для WPF розроблювачів. Нова панель задач захаращена й може передавати більше інформації. Aero-піктограми підтримують користувальницькі команди. Jump List забезпечує доступ до контекстуального запуску задач і файлів для застосунку. WPF 4 інтегрується з функціональністю Windows 7 Jump List, включаючи задачі, елементи, список недавно відкритих і часто використовуваних елементів, власні категорії. Інтеграція з панеллю задач Windows 7, включаючи, індикатор прогресу, іконки накладення, кнопки з мініатюрами з підтримкою команд, текст опису для мініатюр у менеджері вікон.

В Windows 7 панель задач була повністю перероблена для зменшення захаращення й поліпшення допомоги користувачеві у виконанні задач у кілька кліків. WPF4 надає інтеграцію з панеллю задач Windows 7 в XAML, дозволяючи застосункам виводити корисну інформацію користувачеві через іконки в панелі задач, використовуючи іконки накладення, індикатор прогресу, панель іконок, текст опису іконок і мініатюр вікон у панелі задач. Так само з'явився новий клас TaskbarItemInfo, що розширений, як властивість залежностей. Він містить у собі всі нові особливості панелі задач в Windows7.

Накладення іконки

Накладення іконки (Icon Overlays) дозволяють застосунку виводити певні повідомлення й стан користувачеві через власну кнопку в панелі задач, відображаючи невелике накладення, що з'являється в нижньому правому куті кнопки.

Індикатор прогресу. Кнопка в панелі завдань може бути використана для відображення простої інформації про прогрес, не змушувати користувача зайвий раз відкривати вікно для перегляду стану. Індикатор прогресу може бути використаний для відстеження процесу копіювання файлів, завантаження, інсталяції, запису даних на диск або іншої операції, що займає певний відрізок часу.

Новий рушій XAML/BAML-парсера. В WPF 4 була замінена реалізація XamlReader.Load(), BAML-завантаження, функціональність Control & DataTemplate новим рушієм, побудованим на основі нового System.Xaml.dll. Microsoft виправила багато помилок і додала багато нових функціональних поліпшень. Користувачі XamlReader.Load() можуть скористатися декількома новими особливостями мови в XAML2009, такими як підтримка генерик-типів. MarkupExtensions і TypeConverters можуть уже зараз одержувати доступ до багатьох сервісів через створення об'єктного графа, дозволяючи виконувати більше сценаріїв, як доступ до Root-об'єкта. Буде ще простіше створити інструменти для аналізу й маніпулювання XAML з новим низькорівневим API, наданим у System.Xaml.dll.

Див. також

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.