Windows presentation foundation в net

Windows Presentation Foundation (WPF)

.NET Foundation
Build Status
MIT License

Windows Presentation Foundation (WPF) is a UI framework for building Windows desktop applications.

WPF supports a broad set of application development features, including an application model, resources, controls, graphics, layout, data binding and documents. WPF uses the Extensible Application Markup Language (XAML) to provide a declarative model for application programming.

WPF’s rendering is vector-based, which enables applications to look great on high DPI monitors, as they can be infinitely scaled. WPF also includes a flexible hosting model, which makes it straightforward to host a video in a button, for example.

Visual Studio’s designer, as well as Visual Studio Blend, make it easy to build WPF applications, with drag-and-drop and/or direct editing of XAML markup.

As of .NET 6.0, WPF supports ARM64.

See the WPF Roadmap to learn about project priorities, status and ship dates.

WinForms is another UI framework for building Windows desktop applications that is supported on .NET (7.0.x/6.0.x). WPF and WinForms applications only run on Windows. They are part of the Microsoft.NET.Sdk.WindowsDesktop SDK. You are recommended to use the most recent version of Visual Studio to develop WPF and WinForms applications for .NET.

To build the WPF repo and contribute features and fixes for .NET 8.0, Visual Studio 2022 Preview is required.

Getting started

  • .NET 6.0 SDK, .NET 7.0 SDK
  • .NET Preview SDKs (8.0 daily, 7.0 servicing)
  • Getting started instructions
  • Contributing guide
  • Migrating .NET Framework WPF Apps to .NET Core

Status

  • We are currently developing WPF for .NET 8.

See the WPF roadmap to learn about the schedule for specific WPF components.

Test published at separate repo Tests and have limited coverage at this time. We will add more tests, however, it will be a progressive process.

The Visual Studio WPF designer is now available as part of Visual Studio 2019.

How to Engage, Contribute and Provide Feedback

Some of the best ways to contribute are to try things out, file bugs, join in design conversations, and fix issues.

  • This repo defines contributing guidelines and also follows the more general .NET Core contributing guide.
  • If you have a question or have found a bug, file an issue.
  • Use daily builds if you want to contribute and stay up to date with the team.

.NET Framework issues

Issues with .NET Framework, including WPF, should be filed on VS developer community,
or Product Support.
They should not be filed on this repo.

Relationship to .NET Framework

This code base is a fork of the WPF code in the .NET Framework. .NET Core 3.0 was released with a goal of WPF having parity with the .NET Framework version. Over time, the two implementations may diverge.

The Update on .NET Core 3.0 and .NET Framework 4.8 provides a good description of the forward-looking differences between .NET Core and .NET Framework.

This update states how going forward .NET Core is the future of .NET. and .NET Framework 4.8 will be the last major version of .NET Framework.

Code of Conduct

This project uses the .NET Foundation Code of Conduct to define expected conduct in our community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at conduct@dotnetfoundation.org.

Reporting security issues and security bugs

Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) secure@microsoft.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the Security TechCenter.

Also see info about related Microsoft .NET Core and ASP.NET Core Bug Bounty Program.

License

.NET Core (including the WPF repo) is licensed under the MIT license.

.NET Foundation

.NET Core WPF is a .NET Foundation project.

See the .NET home repo to find other .NET-related projects.

Особенности платформы WPF

Последнее обновление: 13.11.2022

Технология WPF (Windows Presentation Foundation) является часть экосистемы платформы .NET и представляет собой подсистему для построения графических интерфейсов.

Если при создании традиционных приложений на основе WinForms за отрисовку элементов управления и графики отвечали такие части ОС Windows, как User32 и GDI+,
то приложения WPF основаны на DirectX. В этом состоит ключевая особенность рендеринга графики в WPF: используя WPF,
значительная часть работы по отрисовке графики, как простейших кнопочек, так и сложных 3D-моделей, ложиться на графический процессор на видеокарте,
что также позволяет воспользоваться аппаратным ускорением графики.

Одной из важных особенностей является использование языка декларативной разметки интерфейса XAML, основанного на XML: вы можете создавать насыщенный
графический интерфейс, используя или декларативное объявление интерфейса, или код на управляемых языках C#, VB.NET и F#, либо совмещать и то, и другое.

Первая версия — WPF 3.0 вышла вместе с .NET Framework 3.0 и операционной системой
Windows Vista в 2006 году. И с тех пор платформа WPF является частью экосистемы .NET и развивается вместе с фреймворком .NET. Например, на сегодняшний день последней версией фреймворка
.NET является .NET 7, и WPF полностью поддерживается этой версией фреймворка.

Преимущества WPF

Что вам, как разработчику, предлагает WPF?

  • Использование традиционных языков .NET-платформы — C#, F# и VB.NET для создания логики приложения

  • Возможность декларативного определения графического интерфейса с помощью специального языка разметки XAML, основанном на xml и представляющем
    альтернативу программному созданию графики и элементов управления, а также возможность комбинировать XAML и C#/VB.NET

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

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

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

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

  • Аппаратное ускорение графики — вне зависимости от того, работаете ли вы с 2D или 3D, графикой или текстом, все компоненты приложения
    транслируются в объекты, понятные Direct3D, и затем визуализируются с помощью процессора на видеокарте, что повышает производительность, делает графику более плавной.

  • Создание приложений под множество ОС семейства Windows

В тоже время WPF имеет определенные ограничения. Несмотря на поддержку трехмерной визуализации, для создания приложений с большим количеством трехмерных изображений, прежде всего игр, лучше использовать другие средства — DirectX
или специальные фреймворки, такие как Monogame или Unity.

Также стоит учитывать, что по сравнению с приложениями на Windows Forms объем программ на WPF и потребление ими памяти в процессе работы в среднем несколько выше. Но это с лихвой компенсируется
более широкими графическими возможностями и повышенной производительностью при отрисовке графики.

Кроме того, несмотря на то, что WPF работает поверх кроссплатформенной среды .NET 5/6/7, но в силу природы WPF и зависимости от компонентов Windows, на данный момент
создавать приложения на WPF можно только под ОС Windows.

Архитектура WPF

Схематически архитектуру WPF можно представить следующим образом:

Архитектура WPF

Как видно на схеме, WPF разбивается на два уровня: managed API и unmanaged API (уровень интеграции с DirectX).
Managed API (управляемый API-интерфейс) содержит код, исполняемый под управлением общеязыковой среды выполнения .NET — Common Language Runtime.
Этот API описывает основной функционал платформы WPF и состоит из следующих компонентов:

  • PresentationFramework.dll: содержит все основные реализации компонентов и элементов управления, которые можно использовать при
    построении графического интерфейса

  • PresentationCore.dll: содержит все базовые типы для большинства классов из PresentationFramework.dll

  • WindowsBase.dll: содержит ряд вспомогательных классов, которые применяются в WPF, но могут также использоваться и вне данной платформы

Unmanaged API используется для интеграции вышележащего уровня с DirectX:

  • milcore.dll: собственно обеспечивает интеграцию компонентов WPF с DirectX. Данный компонент написан на неуправляемом коде
    (С/С++) для взаимодействия с DirectX.

  • WindowsCodecs.dll: библиотека, которая предоставляет низкоуровневую поддержку для изображений в WPF

Еще ниже собственно находятся компоненты операционной системы и DirectX, которые произвоят визуализацию компонентов приложения, либо выполняют прочую низкоуровневую обработку.
В частности, с помощью низкоуровневого интерфейса Direct3D, который входит в состав DirectX, происходит трансляция

Здесь также на одном уровне находится библиотека user32.dll. И хотя выше говорилось, что WPF не использует эту библиотеку
для рендеринга и визуализации, однако для ряда вычислительных задач (не включающих визуализацию) данная библиотека продолжает использоваться.

Файл:Dotnet 3.5.png

WPF в составе .NET Framework

Windows Presentation Foundation (WPF[1]) — система для построения клиентских приложений Windows с визуально привлекательными возможностями взаимодействия с пользователем, графическая (презентационная) подсистема в составе .NET Framework (начиная с версии 3.0), использующая язык XAML[2].

WPF предустановлена в Windows Vista (.NET Framework 3.0), Windows 7 (.NET Framework 3.5 SP1), Windows 8 (.NET Framework 4.0 и 4.5), Windows 8.1 (.NET Framework 4.5.1). С помощью WPF можно создавать широкий спектр как автономных, так и запускаемых в браузере приложений[3].

Особенности технологии[]

В основе WPF лежит векторная система визуализации, не зависящая от разрешения устройства вывода и созданная с учётом возможностей современного графического оборудования. WPF предоставляет средства для создания визуального интерфейса, включая язык XAML (Extensible Application Markup Language), элементы управления, привязку данных, макеты, двухмерную и трёхмерную графику, анимацию, стили, шаблоны, документы, текст, мультимедиа и оформление[3].

Графической технологией, лежащей в основе WPF, является DirectX, в отличие от Windows Forms, где используется GDI/GDI+[4]. Производительность WPF выше, чем у GDI+ за счёт использования аппаратного ускорения графики через DirectX.

Также существует урезанная версия CLR, называющаяся WPF/E, она же известна как Silverlight.

Использование разметки XAML[]

XAML представляет собой язык декларативного описания интерфейса, основанный на XML. Также реализована модель разделения кода и дизайна, позволяющая кооперироваться программисту и дизайнеру. Кроме того, есть встроенная поддержка стилей элементов, а сами элементы легко разделить на элементы управления второго уровня, которые, в свою очередь, разделяются до уровня векторных фигур и свойств/действий. Это позволяет легко задать стиль для любого элемента, например, Button (кнопка).

Средства разработки[]

Для работы с WPF требуется любой .NET-совместимый язык. В этот список входит множество языков: C#, F#, VB.NET, C++, Ruby, Python, Delphi (Prism), Lua и многие другие.
Для полноценной работы может быть использована как Visual Studio, так и Expression Blend. Первая ориентирована на программирование, а вторая — на дизайн и позволяет делать многие вещи, не прибегая к ручному редактированию XAML. Примеры этому — анимация, стилизация, состояния, создание элементов управления и так далее.

Возможности[]

WPF предоставляет широкий спектр возможностей по созданию интерактивных настольных приложений:

Привязка данных[]

Это гибкий механизм, который позволяет через расширения разметки XAML связывать различные данные (от значений свойств элементов управления до общедоступных свойств, реализующих поля базы данных через Entity Framework). Привязка данных представлена классом Binding, который в свою очередь унаследован от MarkupExtension, что позволяет использовать привязки не только в коде, но и в разметке:

<StackPanel Orientation="Horizontal">
   <Slider x:Name="slider" Width="200" Minimum="1" Maximum="100" Value="60"/>        
   <TextBox Text="{Binding ElementName=slider, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>

Помимо основного класса Binding в WPF реализовано еще несколько механизмов привязок:

  • MultiBinding — позволяет создавать множественные привязки, указывая несколько элементов
  • TemplateBinding — используется в шаблонах для связывания свойства элемента внутри шаблона со свойством элемента, к которому применен шаблон
  • PriorityBinding — ранжирует список привязок и выбирает из них свойство (согласно приоритету) к которому будет применена привязка. Если привязка, имеющая наивысший приоритет успешно возвращает значение, то нет необходимости обрабатывать другие привязки в списке.

Стили[]

Позволяют создавать стилевое оформление элементов и, как правило, используются только в разметке:

<Button>       
   <Button.Style>           
      <Style TargetType="Button">
          <Setter Property="FontSize" Value="20"/>                
          <Setter Property="Foreground" Value="LimeGreen"/>           
      </Style>       
   </Button.Style>    
</Button>

Если стиль задается в ресурсах (например в словаре ресурсов), то можно использовать атрибут x:Key для указания уникального ключа. Затем в элементе управления, к которому необходимо применить стиль, нужно использовать расширение разметки StaticResource для связывания с этим стилем. Если использовать этот прием, то стили не будут нагромождать разметку.

Шаблоны элементов управления[]

Позволяют менять графическое оформление элементов и представлены классом ControlTemplate. В отличие от стилей, можно менять не только графическое представление элемента, но и его структуру. При этом шаблон элемента управления задается через свойство Template.

Простой пример круглой кнопки:

   <Button Content="Hey!" Background="LimeGreen" Foreground="White">        
      <Button.Template>            
         <ControlTemplate TargetType="Button">                
            <Grid>                    
               <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" Stretch="Fill"/>                   
               <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>                
            </Grid>            
         </ControlTemplate>        
      </Button.Template>   
   </Button>

Шаблоны данных[]

В отличие от шаблонов элементов управления, задаются для определенного контекста данных (который в блочных элементах управления задается через свойство DataContext, а в списковых через ItemsSource). Сам шаблон данных представлен классом DataTemplate. Для обозначения типа данных, к которому необходимо применить шаблон, используется свойство DataType.

Ресурсы[]

Система ресурсов позволяет объединять шаблоны, стили, кисти, анимацию и многие другие интерактивные элементы, что существенно упрощает работу с ними. Ресурсы задаются в свойстве Resources класса FrameworkElement, от которого унаследованы все элементы управления, панели компоновки и даже класс Application. Это позволяет создавать многоуровневую систему ресурсов:

  • ресурсы внутри объекта — действительны только для этого объекта
  • ресурсы внутри панели компоновки (например Grid) — позволяет задать границу контекста ресурсов на уровне этой панели
  • ресурсы внутри окна Window — если в приложении используется несколько окон, то ресурсы одного окна не будут доступны ресурсам другого окна
   <Window.Resources>
        <SolidColorBrush x:Key="SampleBrush" Color="LimeGreen"/>
    </Window.Resources>

    ...

    <Button Content="Hey!" Background="{StaticResource SampleBrush}" />
  • ресурсы приложения — доступны повсеместно (как правило задаются в отдельном словаре ресурсов)

Графика[]

WPF представляет обширный, масштабируемый и гибкий набор графических возможностей:

  • Графика, не зависящая от разрешения и устройства. Основной единицей измерения в графической системе WPF является аппаратно-независимый пиксель, который составляет 1/96 часть дюйма независимо от фактического разрешения экрана.
  • Дополнительная поддержка графики и анимации. WPF упрощает программирование графики за счет автоматического управления анимацией. Разработчик не должен заниматься обработкой сцен анимации, циклами отрисовки и билинейной интерполяцией
  • Аппаратное ускорение. Графическая система WPF использует преимущества графического оборудования, чтобы уменьшить использование ЦП.
Двухмерная графика[]

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

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

Трехмерная графика[]

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

Версии[]

  • WPF 3.0 (Ноябрь 2006)
  • WPF 3.5 (Ноябрь 2007)
  • WPF 3.5sp1 (Август 2008)
  • WPF 4 (Апрель 2010)
  • WPF 4.5 (Август 2012)

См. также[]

  • Silverlight
  • Windows Forms
  • QML

Литература[]

  • Шаблон:±. WPF: Windows Presentation Foundation в .NET 4.5 с примерами на C# 5.0 для профессионалов, 4-е издание = Pro WPF 4.5 in C# 2012: Windows Presentation Foundation in .NET 4.5, 4th edition. — Шаблон:Указание места в библиоссылке: «Вильямс», 2013. — 1024 с. — ISBN 978-5-8459-1854-3.
  • Шаблон:±. WPF: Windows Presentation Foundation в .NET 4.0 с примерами на C# 2010 для профессионалов = Pro WPF in C# 2010: Windows Presentation Foundation with .NET 4.0. — Шаблон:Указание места в библиоссылке: «Вильямс», 2011. — С. 1024. — ISBN 978-5-8459-1657-0.
  • Шаблон:±. Основы Windows Presentation Foundation. — Шаблон:Указание места в библиоссылке: БХВ-Петербург, 2008. — 432 с. — ISBN 978-5-9775-0265-8.
  • Шаблон:±. Illustrated WPF. — Шаблон:Указание места в библиоссылке: Apress, 2009. — 508 с. — ISBN 978-1-4302-1910-1.

Ссылки[]

  • MSDN Library — Windows Presentation Foundation
  • Общие сведения о графике, анимации и мультимедиа WPF

Примечания[]

  1. Обычно произносится как «даб-пи-эф»
  2. Произносится как «замл»
  3. 3,0 3,1
    Ошибка скрипта: Модуля «String» не существует. Введение в WPF (ru). Microsoft. Проверено 15 ноября 2010. Архивировано из первоисточника 14 февраля 2012.
  4. Шаблон:±. WPF: Windows Presentation Foundation в .NET 3.5 с примерами на C# 2008 для профессионалов = Pro WPF in C# 2008: Windows Presentation Foundation with .NET 3.5. — 2-ое. — Шаблон:Указание места в библиоссылке: «Вильямс», 2008. — С. 25. — 928 с. — ISBN 978-5-8459-1429-3.

Шаблон:DotNET
Шаблон:Windows API
Шаблон:Инструментарии виджетов
Шаблон:Microsoft APIs

Время на прочтение
7 мин

Количество просмотров 66K

Всем привет!

По разным причинам большинство из нас использует десктопные приложения, как минимум, браузер :) А у некоторых из нас возникает необходимость в написании своих. В этой статье я хочу пробежаться по процессу разработки несложного десктопного приложения с использованием технологии Windows Presentation Foundation (WPF) и применением паттерна MVVM. Желающих продолжить чтение прошу под кат.

Думаю, необязательно говорить, что WPF — это разработка Microsoft :) Технология эта предназначена для разработки десктопных приложений под Windows, начиная с Windows XP. Почему именно так? Это связано с тем, что WPF работает поверх платформы .NET, минимальные требования которой — Windows XP и новее. К сожалению, WPF не работает на других платформах, хотя есть шансы, что в ближайшее время это изменится: в стадии разработки находится WPF-based фреймворк Avalonia.

В чём особенность WPF?

Два основных отличия WPF от других средств построения десктопных приложений:

  • Язык разметки XAML, предназначенный для разметки самого интерфейса окна.
  • Рендеринг посредством DirectX, аппаратное ускорение графики.

Я не буду углубляться в подробности, т.к. это не совсем тема статьи. Если интересно, то гуглить XAML, WPF rendering, milcore.dll и DirectX :)

О чём эта статья?

Эта статья содержит пример приложения, построенного на технологии WPF:

  • MVVM и интерфейс INotifyPropertyChanged. Копия текста.

Я постараюсь ориентировать материал статьи в практическую сторону в стиле «повторяй за мной» с пояснениями.

Что нам понадобится для повторения статьи?

Небольшой опыт разработки на C# :) Как минимум, нужно хорошо понимать синтаксис языка. Также понадобится Windows-машина (в примерах будет Win 10) с установленной на ней Visual Studio (в примерах будет 2017, есть бесплатная Community версия). При установке VS необходимо будет включить поддержку десктопной разработки под платформу .NET

image

Так же в этом разделе я опишу создание проекта.

Запускаем VS, создаём новый проект, тип приложения выбираем WPF App (.NET Framework) (можно ввести в строке поиска справа вверху), называем как угодно.

image

После создания нового проекта откроется окно редактора интерфейса, у меня оно выглядит так

image

Внизу находится редактор разметки, вверху — предпросмотр интерфейса окна, но можно поменять относительное расположение редактора кода и предпросмотра интерфейса так, что они будут располагаться в горизонтальном порядке, с помощью вот этих кнопок (справа на границе двух областей):

image

Перед тем, как начать

Элементы окна (их ещё называют контрОлами от слова Control) должны размещаться внутри контейнера или внутри другого элемента типа ContentControl. Контейнер — это специальный контрол, позволяющий разместить внутри себя несколько дочерних контролов и организовать их взаимное расположение. Примеры контейнеров:

  • Grid — позволяет организовать элементы по столбцам и строкам, ширина каждого столбца или строки настраивается индивидуально.
  • StackPanel — позволяет расположить дочерние элементы в одну строку или столбец.

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

MVVM и интерфейс INotifyPropertyChanged. Копия текста.

Итогом этого примера станет приложение с двумя контролами, в одном из которых можно редактировать текст, а в другом только просматривать. Изменения из одного в другой будут переходить синхронно без явного копирования текста с помощью привязки (binding).

Итак, у нас есть свежесозданный проект (я назвал его Ex1), перейдём в редактор разметки и первым делом заменим контейнер, указанный по умолчанию (<Grid></Grid>) на <StackPanel></StackPanel>. Этого контейнера будет достаточно, т.к. нам понадобится расположить всего лишь два контрола один над другим. Укажем явно, каким образом будут располагаться компоненты, добавив свойство Orientation=»Vertical». Добавим внутрь стек панели парочку элементов: поле для ввода текста и поле для отображения текста. Поскольку эти контролы не будут содержать вложенного кода, можно описать их самозакрывающимся тегом (см. код ниже). После всех вышеописанных процедур код описания контейнера и вложенных контролов должен принять такой вид:

<StackPanel Orientation="Vertical">
    <TextBox />
    <TextBlock />
</StackPanel>

Теперь сосредоточимся на цели этого примера. Мы хотим, чтобы при наборе текста в текстбоксе этот же текст синхронно отображался в текстблоке, избежав при этом явной операции копирования текста. Нам понадобится некая связующая сущность, и вот тут-то мы и подошли к такой штуке, как привязка (binding), о которой было сказано выше. Привязка в терминологии WPF — это механизм, позволяющий связывать некоторые свойства контролов с некоторыми свойствами объекта C#-класса и выполнять взаимное обновление этих свойств при изменении одной из частей связки (это может работать в одну, в другую или в обе стороны сразу). Для тех, кто знаком с Qt, можно провести аналогию слотов и сигналов. Чтобы не растягивать время, перейдём к коду.

Итак, для организации привязки нужны свойства контролов и некое свойство некоего C#-класса. Для начала разберёмся с XAML-кодом. Текст обоих контролов хранится в свойстве Text, поэтому добавим привязку для этих свойств. Делается это так:

<TextBox Text="{Binding}"/>
<TextBlock Text="{Binding}"/>

Мы сделали привязку, но пока непонятно к чему :) Нам нужен объект какого-то класса и какое-то свойство в этом объекте, к которому будет выполнена привязка (как ещё говорят, на которое нужно забиндиться).

Так что это за класс? Этот класс называется вьюмоделью (view model) и служит как раз связующим звеном между view (интерфейсом или его частями) и model (моделью, т.е. теми частями кода, которые отвечают за логику приложения. Это позволяет отделить (в какой-то степени) логику приложения от интерфейса (представления, view) и называется паттерном Model-View-ViewModel (MVVM). В рамках WPF этот класс также называется DataContext.

Однако, просто написать класс вьюмодели недостаточно. Нужно ещё как-то оповещать механизм привязки о том, что свойство вьюмодели или свойство вью изменилось. Для этого существует специальный интерфейс INotifyPropertyChanged, который содержит событие PropertyChanged. Реализуем этот интерфейс в рамках базового класса BaseViewModel. В дальнейшем все наши вьюмодели мы будем наследовать от этого базового класса, чтобы не дублировать реализацию интерфейса. Итак, добавим в проект каталог ViewModels, а в этот каталог добавим файл BaseViewModel.cs. Получим такую структуру проекта:

image

Код реализации базовой вьюмодели:

using System.ComponentModel;

namespace Ex1.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Создадим для нашего класса MainWindow свою вьюмодель, унаследовавшись от базовой. Для этого в том же каталоге ViewModels создадим файл MainWindowViewModel.cs, внутри которого будет такой код:

namespace Ex1.ViewModels
{
    public class MainWindowViewModel : BaseViewModel
    {

    }
}

Шикарно! Теперь нужно добавить в эту вьюмодель свойство, на которое будем биндить текст наших контролов. Поскольку это текст, тип этого свойства должен быть string:

public string SynchronizedText { get; set; }

В итоге получим такой код

namespace Ex1.ViewModels
{
    public class MainWindowViewModel : BaseViewModel
    {
        public string SynchronizedText { get; set; }
    }
}

Так, кажется, справились. Осталось забиндиться на это свойство из вьюхи и готово. Давайте сделаем это прямо сейчас:

<TextBox Text="{Binding Path=SynchronizedText}"/>
<TextBlock Text="{Binding Path=SynchronizedText}"/>

Ништяк, запускаем проект, набираем текст в текстбокс иииии… ничего не происходит))) Ну, ничего страшного, на самом деле мы идём правильной дорогой, просто пока ещё не дошли до нужной точки.

Предлагаю на минутку остановиться и подумать, чего же нам не хватает. Вьюха у нас есть. Вьюмодель тоже. Свойства вроде забиндили. Нужный интерфейс реализовали. Проделали кучу работы ради копирования жалкой строчки текста, за что нам это???!?!111

Ладно, шутки в сторону. Мы забыли создать объект вьюмодели и кое-что ещё (об этом позже). Сам класс мы описали, но это ничего не значит, ведь у нас нет объектов этого класса. Ок, где нужно хранить ссылку на этот объект? Ближе к началу примера я упомянул некий DataContext, используемый в WPF. Так вот, у любой вью есть свойство DataContext, которому мы можем присвоить ссылку на нашу вьюмодель. Сделаем это. Для этого откроем файл MainWindow.xaml и нажмём F7, чтобы открыть код этой вьюхи. Он практически пустой, в нём есть только конструктор класса окна. Добавим в него создание нашей вьюмодели и поместим её в DataContext окна (не забываем добавить using с нужным неймспейсом):

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new MainWindowViewModel();
}

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

Нужно вызвать событие PropertyChanged при изменении свойства SynchronizedText и сообщить вьюхе о том, что она должна следить за этим событием. Итак, чтобы вызвать событие, модифицируем код вьюмодели:

public class MainWindowViewModel : BaseViewModel
{
    private string _synchronizedText;
    public string SynchronizedText
    {
        get => _synchronizedText;
        set
        {
            _synchronizedText = value;
            OnPropertyChanged(nameof(SynchronizedText));
        }
    }
}

Что мы тут сделали? Добавили скрытое поле для хранения текста, обернули его в уже существующее свойство, а при изменении этого свойства не только меняем скрытое поле, но и вызываем метод OnPropertyChanged, определённый в базовой вьюмодели и вызывающий событие PropertyChanged, объявленное в интерфейсе INotifyPropertyChanged, так же реализованное в базовой вьюмодели. Получается, что при каждом изменении текста возникает событие PropertyChanged, которому передаётся имя свойства вьюмодели, которое было изменено.

Ну, почти всё, финишная прямая! Осталось указать вьюхе, что оно должно слушать событие PropertyChanged:

<TextBox Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"/>
<TextBlock Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>

Помимо того, что мы указали, по какому триггеру должно происходить обновление, мы так же указали, в какую сторону это обновление отслеживается: от вью к вьюмодели или наоборот. Поскольку в текстбоксе мы вводим текст, то нам интересны изменения только во вью, поэтому выбираем режим OneWayToSource. В случае с текстблоком всё ровно наоборот: нам интересны изменения во вьюмодели, чтобы отобразить их во вью, поэтому выбираем режим OneWay. Если бы нам нужно было, чтобы изменения отслеживались в обе стороны, можно было не указывать Mode вообще, либо указать TwoWay явно.

Итак, запускаем программу, набираем текст и voi-la! Текст синхронно меняется, и мы нигде ничего не копировали!

image

Спасибо за внимание, продолжение следует. Будем разбираться с DataTemplate и паттерном Command.

    Windows Presentation Foundation (WPF) is a user interface framework for building desktop applications. Implementing Extensible Application Markup Language (XAML) to define and script views, it has become the standard for Windows application user interfaces. Introduced in 2006, Microsoft has reenergized its commitment to WPF with its inclusion into .NET Core.

    The tutorial in this post will guide you through creating a project that will mimic many of the basic functions of a word processor. You will be surprised at how easy it is to implement many features with standard tool elements. You will be introduced to some of the many constructs of WPF and create a foundation for experimenting further.

    Prerequisites

    You’ll need the following tools and resources to build and run this project:

    Windows 10 – It puts the Windows in Windows Presentation Foundation.

    .NET Core SDK 3.1 – The SDK includes the APIs, runtime, and CLI.

    Visual Studio 2019 with the following workloads and individual components:

    • .NET desktop development workload (Includes C#)
    • GitHub Extension for Visual Studio (If you want to clone the companion repository.)

    You should have a general knowledge of Visual Studio and the C# language syntax. You will be adding and editing files, and debugging code.

    There is a companion repository for this post available on GitHub. It contains the complete source code for the tutorial project.

    Creating the project

    Begin this tutorial by creating a WPF App (.NET Core) project for C# named WPFTwilioExample. You can put the solution and project folders wherever it’s most convenient for you.

    While you could easily use this code in a .NET Framework project, this example uses .NET Core, which is closer to the forthcoming .NET 5 release scheduled for November 2020.

    After the tooling finishes creating the project, note the presence of the App.xaml file and its associated App.xaml.cs code-behind file. Also examine the MainWindow.xaml and associated code-behind file, MainWindow.xaml.cs.

    MainWindow will be the main window for this application and where you will add UI elements. The App class is the entry point to the application and where the MainWindow.xaml is specified as the startup window. You can see this App.xaml file with the line:

    StartupUri="MainWindow.xaml"
    

    If you ever wanted your application to start with another window, this is where you would specify that. For now, leave it as it is.

    In the MainWindow.xaml file, note how the main element is a Grid. The Grid is one of several very powerful container components in WPF. Others include StackPanel, Canvas, WrapPanel, UniformGrid and DockPanel. Each has its advantages and situations that it fits best. In this example you will be using the DockPanel.

    Replace the existing contents of the MainWindow.xaml file with the following XAML markup:

    <Window x:Class="WPFTwilioExample.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WPFTwilioExample"
            mc:Ignorable="d"
            Title="Mini WordProcessor" Height="450" Width="800">
        <DockPanel LastChildFill="True">
    
        </DockPanel>
    </Window>
    

    The changed value for the Title attribute will be displayed in the window’s title bar when the application runs. Also, note that the DockPanel element has an attribute of LastChildFill="True". While this is the default value, it is done for clarity.

    The DockPanel is a container that allows you to dock child controls to the top, bottom, left or right. By default, the last control, if not given a specific dock position, will fill the remaining space. Use the DockPanel whenever you need to dock one or several controls to one of the sides, like for dividing up the window into specific areas. In this example, you will be adding a menu, toolbar, and status elements.

    With the window ready you will add components to it which will add capability. The first component you will add is applicable to the window as a whole and will be placed outside of the DockPanel. This will be the CommandBindings section where CommandBinding elements will be defined.

    Insert the following XAML markup in the MainWindow.xaml file above the DockPanel element:

        <Window.CommandBindings>
    <CommandBinding Command="New" Executed="New_Executed" CanExecute="CommonCommandBinding_CanExecute" />
      <CommandBinding Command="Open" Executed="Open_Executed" CanExecute="CommonCommandBinding_CanExecute" />
            <CommandBinding Command="Save" Executed="Save_Executed" CanExecute="CommonCommandBinding_CanExecute" />
        </Window.CommandBindings>
    

    Open the MainWindow.xaml.cs code-behind file and insert the following C# code after the MainWindow() constructor:

    private void CommonCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
            e.CanExecute = true;
    }
    private void New_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    }
    
    private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    }
    
    private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    }
    

    The code block above adds a group of Commands. Commands are constructs that associate a name with an action. They lend to a more loose coupling between UI elements and the code. In this case, you are adding the New, Open, and Save commands. The Executed attributes in the <CommandBinding> elements in the MainWindow.xaml file direct the Command to execute the listed method while the CanExecute attribute points to the CommonCommandBinding_CanExecute method. The CanExecute associated method in the code-behind file is run when the app is idle to indicate whether the command can be run or it is disabled. In this case it always returns true. This is useful in situations where a command might be disabled unless certain conditions are met.

    The _Execute handler methods for the New, Open and Save commands in the code behind are provided as stubs for now and will be fleshed out shortly.

    You will now be adding the first UI element to the window. Just inside the DockPanel container add a menu with File and Edit top level items by inserting the following XAML markup inside the <DockPanel> element:

    <Menu DockPanel.Dock="Top">
        <MenuItem Header="_File">
            <MenuItem Command="New" Header="_New" />
            <MenuItem Command="Open" Header="_Open" />
            <MenuItem Command="Save" Header="_Save" />
            <Separator />
            <MenuItem Header="_Exit" />
        </MenuItem>
        <MenuItem Header="_Edit">
            <MenuItem Command="Cut"   Header="Cut    (Ctrl+X)" />
            <MenuItem Command="Copy"  Header="Copy   (Ctrl+C)" />
            <MenuItem Command="Paste" Header="Paste  (Ctrl+V)" />
        </MenuItem>
    </Menu>
    

    Note how the _File MenuItem elements reference the Commands that were added earlier.

    The _Edit menu items reference Cut, Copy and Paste, but those CommandBindings were not created. The commands reference the ApplicationCommands.Cut, ApplicationCommands.Copy and ApplicationCommands.Paste commands. These are RoutedCommands and will be processed by the TextBox that you will add shortly. Rather than implement these commands in the code, they’ll use the default behavior of the TextBox element. The application will simply route the commands until an element that can handle them processes them. WPF event routing is a very powerful capability. Routed Events tunnel down the element tree and bubble up the element tree.

    But while the menu is now visible, the application still does nothing.

    Before going on, look at the design panel and notice how the menu bar that was just added with the <Menu> element is positioned at the top of the DockPanel. That results from the DocPanel.Doc="Top" attribute for the Menu element.

    Continuing with the UI elements, add the following code to the MainWindow.xaml file inside the DockPanel element and after the menu elements you previously inserted:

    <ToolBarTray DockPanel.Dock="Top" Background="LightGray">
        <ToolBar>
            <Button Command="Cut" Content="Cut" />
            <Button Command="Copy" Content="Copy" />
            <Button Command="Paste" Content="Paste" />
            <ToggleButton Command="EditingCommands.ToggleBold" Name="btnBold" Content="Bold"/>
            <ToggleButton Command="EditingCommands.ToggleItalic" Name="btnItalic" Content="Italic"/>
            <ComboBox Name="cmbFontFamily" Width="150" SelectionChanged="cmbFontFamily_SelectionChanged" />
            <ComboBox Name="cmbFontSize" Width="50" IsEditable="True" TextBoxBase.TextChanged="cmbFontSize_TextChanged" />
        
      </ToolBar>
    </ToolBarTray>
    <StatusBar Name="statusbar" DockPanel.Dock="Bottom">
        <StackPanel Orientation="Horizontal">
            <TextBlock Name="StatusBlock"></TextBlock>
        </StackPanel>
    </StatusBar>
    <RichTextBox  AcceptsReturn="True" Name="MyTextBox"
        SelectionChanged ="MyTextBox_SelectionChanged">
    </RichTextBox>
    

    Here you added a ToolBarTray with several buttons docked to the top, a StatusBar with a TextBlock docked to the bottom, and a RichTextBox as the last element in the DockPanel. Since the RichTextBox is the last element in the DockPanel, it will fill the center of the window.

    That takes care of the code in the .xaml file.

    You will next add the functionality in the code-behind file MainWindow.xaml.cs which will make the application come alive.

    File system dialog boxes use resources from the Microsoft.Win32 and System.IO namespaces, so you will need to add the following using directives to the others in MainWindow.xaml.cs:

    using System.IO;
    using Microsoft.Win32;
    

    Earlier, you added placeholder stub methods for the New, Open and Save commands. Replace the stub methods with the following code:

    private void New_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    MyTextBox.Document.Blocks.Clear();
    }
    
    private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    OpenFileDialog dlg = new OpenFileDialog();
           dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*";
           if (dlg.ShowDialog() == true)
           {
                   FileStream fileStream = new FileStream(dlg.FileName, FileMode.Open);
                 TextRange range = new TextRange(MyTextBox.Document.ContentStart, MyTextBox.Document.ContentEnd);
                 range.Load(fileStream, DataFormats.Rtf);
            }
     }
    
     private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
     {
           SaveFileDialog dlg = new SaveFileDialog();
           dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*";
           if (dlg.ShowDialog() == true)
           {
               FileStream fileStream = new FileStream(dlg.FileName, FileMode.Create);
               TextRange range = new TextRange(MyTextBox.Document.ContentStart, MyTextBox.Document.ContentEnd);
               range.Save(fileStream, DataFormats.Rtf);
           }
    }
    

    New_Executed() is the handler method for the New command. When a new file is to be created the currently displayed text area will be cleared.

    The Open_Executed() and Save_Executed() methods are a bit more involved but similar to each other. A dialog box is opened to specify a file to load or save. The file extension applied to the files will be “.rtf” for Rich Text Format.

    There are three more things that need to be added to wrap up the functionality:

    1. Populating the combo boxes
    2. Taking action when the combo boxes selection changes
    3. Providing visual feedback for selected text

    The combo boxes for the font and the font size in the toolbar need to be populated. That will be done in the MainWindow class constructor with the following code.

    public MainWindow()
    {
    InitializeComponent();
          cmbFontFamily.ItemsSource = Fonts.SystemFontFamilies.OrderBy(f => f.Source);
          cmbFontSize.ItemsSource = new List<double>() { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 };
    
    }
    

    Although the combo boxes have data to display, they still do not do anything. Add the following code after the MainWindow constructor:

    private void cmbFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    if (cmbFontFamily.SelectedItem != null)
    MyTextBox.Selection.ApplyPropertyValue(Inline.FontFamilyProperty,   cmbFontFamily.SelectedItem);
    }
    
    private void cmbFontSize_TextChanged(object sender, TextChangedEventArgs e)
    {
    MyTextBox.Selection.ApplyPropertyValue(Inline.FontSizeProperty, cmbFontSize.Text);
    
    }
    

    These methods will be invoked when the combo box selections change. They’ll apply the font type and size styling to text that’s selected.

    With that completed there is one block of code left to add, the TextBox.SelectionChanged event handler. This method gets called any time there is a change to the selection in the textbox. It is here that all the elements you added (combo boxes, toolbar buttons, statusbar, and the textbox) are synchronized.

    Add the following code to the MainWindow.xaml.cs file at the bottom of the MainWindow class:

    private void MyTextBox_SelectionChanged(object sender, RoutedEventArgs e)
    {
    object temp = MyTextBox.Selection.GetPropertyValue(Inline.FontFamilyProperty);
           cmbFontFamily.SelectedItem = temp;
    
           temp = MyTextBox.Selection.GetPropertyValue(Inline.FontSizeProperty);
           if(temp is Double)
                cmbFontSize.Text = temp.ToString();
    
           temp = MyTextBox.Selection.GetPropertyValue(Inline.FontWeightProperty);
           btnBold.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontWeights.Bold));
                
           temp = MyTextBox.Selection.GetPropertyValue(Inline.FontStyleProperty);
           btnItalic.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontStyles.Italic));
                
           string info = string.Format($"{MyTextBox.Selection.Text}");
           StatusBlock.Text = info;
     }
    

    This is an interesting block of code. The first thing done is to get the FontFamilyProperty value from the selected code in the textbox. That value is used to select the related item in the cmbFontFamily combo box. In this way, the font of the selected code will be selected in the combo box.

    The next several lines do the same thing for the font size, font weight, and bold and italic buttons.

    Lastly, the status bar is given something to display and shows the selected text.

    Note the type checking done in the if statement prior to setting the cmbFontSize text. If multiple font sizes are detected in the selected text, the value of temp will be a data type that’s incompatible with the data type of cmbFontSize.Text. Checking for the correct data type prevents an error when assigning the property value.

    Testing the completed application

    Build and run the application. Enter some text in the textbox that fills the center of the app. Select a portion and make it bold or italic. Change the font size and style. When ready, save the file and give it a name. Creating a new file will clear the textbox for something new. Load the file you just saved and you will be able to continue working on it. Note how the selected text attributes are reflected in the toolbar elements. Observe the status bar when text is selected. You should see the selected text.

    Potential enhancements

    The RichTextBox control has a lot of very powerful editing capabilities. Exposing those capabilities through commands and a handful of UI elements creates a simple, yet powerful text editor.

    The layout created here is a standard style window with menu, toolbar, and status bar and is a good foundation for building additional functionality. Consider adding alignment tools to align text to the left or right or centered. Perhaps you wish to add color to your text in terms of a foreground or background color. The status bar could also be expanded to show more pertinent information, such as the number of words in the text box, or even a clock.

    Summary

    Windows Presentation Foundation is a very powerful tool. It supports layered text, animations, a powerful event and command routing system, and even includes support for elaborate graphics and styling. Microsoft has committed to the future of WPF with its inclusion in .NET Core and .NET 5 due for release later this year. Give it a try. You may be pleasantly surprised.

    Additional resources

    The following reference resources provide in-depth information on the topics discussed in this post:

    Differences in WPF — Differences between Windows Presentation Foundation (WPF) on .NET Core and .NET Framework. WPF for .NET Core.

    RichTextBox Overview — Differences between Textbox and RichTextBox

    If you’d like to learn how to build a WPF application to verify phone numbers and obtain information about callers and their phones, check out this post:

    Using Twilio Lookup in .NET Core WPF Applications

    TwilioQuest – If you’d like to learn more about programming C# and other languages, try this action-adventure game inspired by the 16-bit golden era of computer gaming.

    Jeffrey Rosenthal is a C/C++/C# developer and enjoys the architectural aspects of coding and software development. Jeff is a MCSD and has operated his own company, The Coding Pit since 2008. When not coding, Jeff enjoys his home projects, rescuing dogs, and flying his drone. Jeff is available for consulting on various technologies and can be reached via email, Twitter, or LinkedIn.

  • Windows presentation foundation font cache
  • Windows pro или windows pro торрент
  • Windows preferred path systemdrive programdata to store service data is not set
  • Windows pptp client windows 10
  • Windows powershell windows server 2008r2