Как создать wpf приложение в vs 2019
Как вам уже известно, XAML и WPF — это две разные, хотя и взаимодополняющие технологии. В результате вполне возможно создать приложение WPF, в котором не используется XAML.
В общем случае существуют три разных стиля кодирования, которые могут применяться при создании приложения WPF:
Только код
Это традиционный подход, используемый в Visual Studio для приложений Windows Forms. Пользовательский интерфейс в нем генерируется операторами кода.
Код и не компилированная разметка (XAML)
Это специализированный подход, который имеет смысл в определенных сценариях, когда нужны исключительно динамичные пользовательские интерфейсы. При этом часть пользовательского интерфейса загружается из файла XAML во время выполнения с помощью класса XamlReader из пространства имен System.Windows.Markup.
Код и компилированная разметка (BAML)
Это предпочтительный подход для WPF, поддерживаемый в Visual Studio. Для каждого окна создается шаблон XAML, и этот код XAML компилируется в BAML, после чего встраивается в конечную сборку. Во время выполнения скомпилированный BAML извлекается и используется для регенерации пользовательского интерфейса.
Разработка на основе только кода — наименее распространенный (но полностью поддерживаемый) путь написания приложений WPF без применения какого-либо XAML-кода. Очевидным недостатком разработки на основе только кода является то, что этот вариант потенциально чрезвычайно утомителен. Элементы управления WPF не включают параметризованных конструкторов, поэтому даже добавление простой кнопки в окно требует нескольких строк кода. Одним потенциальным преимуществом разработки на основе только кода является ограниченное пространство для настройки.
Например, можно сгенерировать форму, заполненную элементами управления вводом, на основе информации из записи базы данных, или же можно на основе какого-то условия принять решение, добавлять или подставлять элементы управления в зависимости от текущего пользователя. Все, что для этого потребуется — это логика проверки условия и ветвление. И напротив, когда используемые документы XAML встраиваются в сборку как фиксированные, неизменные ресурсы.
Несмотря на то что вы вряд ли будете создавать WPF-приложения на основе только кода, вы, скорее всего, воспользуетесь этим подходом для создания элемента управления WPF в некоторой точке, когда нужна адаптируемая порция пользовательского интерфейса. Ниже представлен пример:
Концептуально класс Class1 в этом примере сильно напоминает форму из традиционного приложения Windows Forms. Он наследуется от базового класса Window и добавляет приватную переменную-член для каждого элемента управления. Для ясности этот класс выполняет свою работу по инициализации в выделенном методе InitializeComponent().
Предисловие
Следующий контент переведен из официальных документов Microsoft
Изучив это руководство, вы познакомитесь со многими инструментами, диалоговыми окнами и конструкторами, доступными при разработке приложений с использованием Visual Studio. Вы создадите приложение «Hello, World», спроектируете пользовательский интерфейс, добавите код и отладите ошибки. За это время вы научитесь использовать интегрированную среду разработки (IDE).
Предварительное условие
Настроить IDE
При запуске Visual Studio сначала открывается окно «Пуск». Выберите «Продолжить, но без кода», чтобы открыть среду разработки. Вы увидите окна инструментов, меню и панели инструментов, а также пространство главного окна. Окно «Инструменты» пристыковано к левой и правой сторонам окна «Приложение», с полем поиска, строкой меню и стандартной панелью инструментов вверху. При загрузке решения или проекта редактор и дизайнер отображаются в центре окна приложения. При разработке приложений большая часть времени будет проводиться в этой центральной области.
Создать проект
При создании приложения в Visual Studio вы должны сначала создать проект и решение. В этом примере будет создан проект Windows Presentation Foundation (WPF).
- Откройте Visual Studio 2019.
- В окне «Пуск» выберите «Создать новый проект».
4. На следующем экране дайте проекту имя «HelloWPFApp» и выберите «Создать».
Visual Studio создаст проект и решение HelloWPFApp, а «Обозреватель решений» отобразит различные файлы. «Конструктор WPF» отображает представление конструктора и представление XAML файла MainWindow.xaml в разделенном представлении. Вы можете сдвинуть разделитель, чтобы показать больше или меньше частей любого вида. Вы можете выбрать просмотр только визуального представления или представления XAML.
Примечания:Чтобы узнать больше о XAML (расширяемом языке разметки приложений), обратитесь кОбзор XAML для WPFстраница.
Вы можете настроить его после создания проекта. Чтобы выполнить эту операцию, выберите «Окно свойств» в меню «Вид» или нажмите F4. Затем вы можете отображать и изменять параметры элементов, элементов управления и других элементов в приложении.
Измените имя MainWindow.xaml
Дайте MainWindow более конкретное имя. В «Обозревателе решений» щелкните правой кнопкой мыши «MainWindow.xaml» и выберите «Переименовать». Переименуйте файл в «Greetings.xaml».
Дизайн пользовательского интерфейса (UI)
Если дизайнер не открыт, выберите «Greetings.xaml», а затем нажмите «Shift + F7», чтобы открыть дизайнер.
Мы добавим в это приложение три типа элементов управления: элемент управления TextBlock, два элемента управления RadioButton и элемент управления Button.
Добавить элемент управления TextBlock
Нажмите «Ctrl + Q», чтобы активировать окно поиска, а затем введите «панель инструментов». В списке результатов выберите «Вид»> «Панель инструментов».
На панели инструментов разверните узел «Общедоступные элементы управления WPF», чтобы просмотреть элемент управления TextBlock.
Добавьте элемент управления TextBlock в область конструктора, выбрав элемент «TextBlock» и перетащив его в окно в области конструктора. Отцентрируйте элемент управления в верхней части окна. В Visual Studio 2019 и более поздних версиях вы можете использовать красную справочную линию для центрирования элемента управления.
Ваше окно должно выглядеть как на изображении ниже:
Разметка XAML должна выглядеть, как в следующем примере:
Настроить текст в текстовом блоке
В представлении XAML найдите разметку TextBlock и измените свойство Text с TextBox изменить на Select a message option and then choose the Display button.
Разметка XAML должна выглядеть, как в следующем примере:
Если хотите, снова отцентрируйте TextBlock и сохраните изменения, нажав Ctrl + S или используя пункт меню «Файл».
Затем добавьте в форму два элемента управления RadioButton.
Добавить переключатель
- В «Панели инструментов» найдите элемент управления «RadioButton». .
2. Добавьте два элемента управления RadioButton в область конструктора, выбрав элемент «RadioButton» и перетащив его в окно на поверхности конструктора. Переместите кнопки (выбирая их и используя клавиши со стрелками), чтобы кнопки отображались рядом под элементом управления TextBlock. Используйте красную контрольную линию для выравнивания элементов управления.
Ваше окно должно выглядеть так:
3. Слева от элемента управления RadioButton.«Атрибуты»В окне будет"название"Атрибут (находится в«Атрибуты»В верхней части окна) изменится на HelloButton 。
4. В окне «Свойства» элемента управления RadioButton справа измените свойство «Имя» на GoodbyeButton , Затем сохраните изменения.
Затем вы добавите отображаемый текст для каждого элемента управления RadioButton. Следующая программа обновит элемент управления RadioButton"содержание"Атрибуты.
Добавьте отображаемый текст для каждого переключателя
- В XAML HelloButton с участием GoodbyeButton Атрибут "content" обновлен до "Hello" с участием "Goodbye" . Разметка XAML теперь должна напоминать следующий пример:
Установите переключатель в положение по умолчанию
На этом шаге HelloButton будет выбран по умолчанию, поэтому всегда будет выбран один из двух переключателей.
- В представлении XAML найдите разметку для HelloButton.
- Добавьте свойство IsChecked и установите для него значение «True». В частности, добавьте IsChecked="True" 。
Разметка XAML теперь должна напоминать следующий пример:
Последний добавленный элемент пользовательского интерфейса - это элемент управления Button.
Добавить кнопку управления
- В «Панели инструментов» найдите элемент управления «Кнопка», а затем добавьте его в интерфейс дизайна под элементом управления RadioButton, перетащив элемент управления на форму в представлении конструктора. Если вы используете Visual Studio 2019 или более поздней версии, появится красная линия, которая поможет вам центрировать элемент управления.
- В представлении XAML измените значение «содержимого» элемента управления Button с Content="Button" изменить на Content="Display" , Затем сохраните изменения.
Ваше окно должно выглядеть примерно так, как показано на рисунке ниже.
Разметка XAML теперь должна напоминать следующий пример:
Добавить код на кнопку дисплея
- В области конструктора дважды щелкните"дисплей"Кнопка.
В это время открывается «Greetings.xaml.cs», и курсор находится на Button_Click Об инциденте.
2. Введите следующий код:
3. Сохраните заявку.
Отладить и протестировать приложение
Найдите и исправьте ошибки
На этом этапе вы столкнетесь с предыдущей ошибкой, вызванной изменением имени файла MainWindow.xaml.
Начать отладку и найти ошибки
- Запустите отладчик, нажав F5 или выбрав «Отладка», а затем «Начать отладку».
Появится окно «Режим прерывания», а в окне «Вывод» будет указано, что возникла исключительная ситуация IOException: «Не удалось найти ресурс'mainwindow.xaml '».
2. Выберите «Отладка»> «Остановить отладку», чтобы остановить программу отладки.
Когда мы начали это руководство, мы переименовали MainWindow.xaml в Greetings.xaml, но код по-прежнему ссылается на MainWindow.xaml в качестве стартового URI приложения, поэтому проект нельзя запустить.
Укажите Greetings.xaml в качестве URI запуска
- В «Обозревателе решений» откройте файл «App.xaml».
- Будет StartupUri="MainWindow.xaml" изменить на StartupUri="Greetings.xaml" , Затем сохраните изменения.
Снова запустите отладчик (нажмите «F5»). Вы должны увидеть окно приветствия приложения.
Теперь закройте окно приложения и прекратите отладку.
Используйте точки останова для отладки
Вы можете протестировать код во время отладки, добавив несколько точек останова. Вы можете добавить точку останова, выбрав «Отладка»> «Переключить точку останова», щелкнув левое поле рядом со строкой кода, где вы хотите добавить точку останова в редакторе, или нажав F9.
Добавить точку останова
- Откройте Greetings.xaml.cs и выберите следующую строку: MessageBox.Show("Hello.")
- По выбору"отладка"-> «Переключить точку останова»Чтобы добавить точку останова из меню. Красный кружок появляется рядом со строкой кода в крайнем левом поле окна редактора.
- Выберите следующую строку: MessageBox.Show("Goodbye.") 。
- Нажмите клавишу «F9», чтобы добавить точку останова, а затем нажмите «F5», чтобы начать отладку.
- В“Greetings”В окне выберите“Hello”Радиокнопка, а затем выберите"дисплей"Кнопка. Чтобы MessageBox.Show("Hello.") Строка будет выделена желтым цветом. Внизу IDE окна «авто», «локальное» и «мониторинг» состыкованы вместе слева, а окна «стек вызовов», «точка останова», «настройки исключений», «команда», «немедленный» и «вывод». Состыкованы справа.
6. В строке меню выберите «Отладка»> «Выход».
8. В“Greetings”В окне выберите“Goodbye”Радиокнопка, а затем выберите"дисплей"Кнопка. Строка MessageBox.Show("Goodbye.") Будет выделен желтым цветом.
10. Закройте окно приложения и прекратите отладку.
11. В строке меню выберите «Отладка»> «Отключить все точки останова».
Просмотр представления элементов пользовательского интерфейса
В запущенном приложении вы увидите небольшой компонент, отображаемый в верхней части окна. Это помощник во время выполнения, с помощью которого вы можете быстро получить доступ к некоторым полезным функциям отладки. Нажмите первую кнопку «Перейти к дереву визуализации в реальном времени». Сразу вы увидите окно, содержащее дерево, которое содержит все визуальные элементы страницы. Разверните узел и найдите добавленную кнопку.
Сгенерировать релизную версию приложения
Убедившись, что все готово, вы можете подготовить релизную версию приложения.
- В главном меню выберите «Создать»> «Чистое решение» по очереди, чтобы удалить промежуточные файлы и файлы вывода, созданные в процессе предыдущего создания. Это не обязательно, но это очистит выходные данные отладочной сборки.
- Используйте раскрывающийся список на панели инструментов (в настоящее время отображается «Отладка»), чтобы изменить конфигурацию сборки HelloWPFApp с «Отладка» на «Выпуск».
- Выберите «Создать»> «Создать решение», чтобы сгенерировать решение.
- Созданный файл .exe можно найти в каталоге решения и проекта (. \ HelloWPFApp \ HelloWPFApp \ bin \ Release).
Интеллектуальная рекомендация
Gensim Skip-Gram модель для Word2Vec
Вступление Генизм - это библиотека Python с открытым исходным кодом, которая используется для легко эффективно извлечь семантические темы в документах. Он используется для обработки оригинального, нес.
Встраиваем VSCode в OpenCV IDE (C ++, window10 1803)
Каталог статей вступление окружение шаг 1. Конфигурация Visual Studio Code 2. Конфигурация OpenCV 3. Конфигурация MinGw 4. Конфигурация cmake 5. Конфигурация проекта 6. Ссылка на ссылку В конце концов.
Интеграция и инструменты fastDFS + spring + maven
После завершения установки его нужно интегрировать в проект. 1. Поместите файл в папку config. 1.1 Содержание файла tracker_server = 192.168.1.202: 22122 - адрес сервера отслеживания, номер порта по у.
Основы Linux
Пользователи Linux делятся на два типа: Пользователь суперадминистратора: root, хранится в каталоге / root Обычные пользователи: хранятся в каталоге / home Каталог Linux /: [*] Корневой каталог. Как п.
По разным причинам большинство из нас использует десктопные приложения, как минимум, браузер :) А у некоторых из нас возникает необходимость в написании своих. В этой статье я хочу пробежаться по процессу разработки несложного десктопного приложения с использованием технологии Windows Presentation Foundation (WPF) и применением паттерна MVVM. Желающих продолжить чтение прошу под кат.
В чём особенность WPF?
Два основных отличия WPF от других средств построения десктопных приложений:
- Язык разметки XAML, предназначенный для разметки самого интерфейса окна.
- Рендеринг посредством DirectX, аппаратное ускорение графики.
Я не буду углубляться в подробности, т.к. это не совсем тема статьи. Если интересно, то гуглить XAML, WPF rendering, milcore.dll и DirectX :)
О чём эта статья?
Эта статья содержит пример приложения, построенного на технологии WPF:
Я постараюсь ориентировать материал статьи в практическую сторону в стиле "повторяй за мной" с пояснениями.
Что нам понадобится для повторения статьи?
Так же в этом разделе я опишу создание проекта.
После создания нового проекта откроется окно редактора интерфейса, у меня оно выглядит так
Внизу находится редактор разметки, вверху — предпросмотр интерфейса окна, но можно поменять относительное расположение редактора кода и предпросмотра интерфейса так, что они будут располагаться в горизонтальном порядке, с помощью вот этих кнопок (справа на границе двух областей):
Перед тем, как начать
Элементы окна (их ещё называют контрОлами от слова Control) должны размещаться внутри контейнера или внутри другого элемента типа ContentControl. Контейнер — это специальный контрол, позволяющий разместить внутри себя несколько дочерних контролов и организовать их взаимное расположение. Примеры контейнеров:
- Grid — позволяет организовать элементы по столбцам и строкам, ширина каждого столбца или строки настраивается индивидуально.
- StackPanel — позволяет расположить дочерние элементы в одну строку или столбец.
Есть и другие контейнеры. Поскольку контейнер тоже является контролом, то внутри контейнера могут быть вложенные контейнеры, содержащие вложенные контейнеры и так далее. Это позволяет гибко располагать контролы относительно друг друга. Так же с помощью контейнеров мы можем не менее гибко управлять поведением вложенных контролов при изменении размеров окна.
MVVM и интерфейс INotifyPropertyChanged. Копия текста.
Итогом этого примера станет приложение с двумя контролами, в одном из которых можно редактировать текст, а в другом только просматривать. Изменения из одного в другой будут переходить синхронно без явного копирования текста с помощью привязки (binding).
Итак, у нас есть свежесозданный проект (я назвал его Ex1), перейдём в редактор разметки и первым делом заменим контейнер, указанный по умолчанию (<Grid></Grid>) на <StackPanel></StackPanel>. Этого контейнера будет достаточно, т.к. нам понадобится расположить всего лишь два контрола один над другим. Укажем явно, каким образом будут располагаться компоненты, добавив свойство Orientation="Vertical". Добавим внутрь стек панели парочку элементов: поле для ввода текста и поле для отображения текста. Поскольку эти контролы не будут содержать вложенного кода, можно описать их самозакрывающимся тегом (см. код ниже). После всех вышеописанных процедур код описания контейнера и вложенных контролов должен принять такой вид:
Мы сделали привязку, но пока непонятно к чему :) Нам нужен объект какого-то класса и какое-то свойство в этом объекте, к которому будет выполнена привязка (как ещё говорят, на которое нужно забиндиться).
Так что это за класс? Этот класс называется вьюмоделью (view model) и служит как раз связующим звеном между view (интерфейсом или его частями) и model (моделью, т.е. теми частями кода, которые отвечают за логику приложения. Это позволяет отделить (в какой-то степени) логику приложения от интерфейса (представления, view) и называется паттерном Model-View-ViewModel (MVVM). В рамках WPF этот класс также называется DataContext.
Однако, просто написать класс вьюмодели недостаточно. Нужно ещё как-то оповещать механизм привязки о том, что свойство вьюмодели или свойство вью изменилось. Для этого существует специальный интерфейс INotifyPropertyChanged, который содержит событие PropertyChanged. Реализуем этот интерфейс в рамках базового класса BaseViewModel. В дальнейшем все наши вьюмодели мы будем наследовать от этого базового класса, чтобы не дублировать реализацию интерфейса. Итак, добавим в проект каталог ViewModels, а в этот каталог добавим файл BaseViewModel.cs. Получим такую структуру проекта:
Код реализации базовой вьюмодели:
Создадим для нашего класса MainWindow свою вьюмодель, унаследовавшись от базовой. Для этого в том же каталоге ViewModels создадим файл MainWindowViewModel.cs, внутри которого будет такой код:
Шикарно! Теперь нужно добавить в эту вьюмодель свойство, на которое будем биндить текст наших контролов. Поскольку это текст, тип этого свойства должен быть string:
В итоге получим такой код
Так, кажется, справились. Осталось забиндиться на это свойство из вьюхи и готово. Давайте сделаем это прямо сейчас:
Ништяк, запускаем проект, набираем текст в текстбокс иииии… ничего не происходит))) Ну, ничего страшного, на самом деле мы идём правильной дорогой, просто пока ещё не дошли до нужной точки.
Предлагаю на минутку остановиться и подумать, чего же нам не хватает. Вьюха у нас есть. Вьюмодель тоже. Свойства вроде забиндили. Нужный интерфейс реализовали. Проделали кучу работы ради копирования жалкой строчки текста, за что нам это. 111
Ладно, шутки в сторону. Мы забыли создать объект вьюмодели и кое-что ещё (об этом позже). Сам класс мы описали, но это ничего не значит, ведь у нас нет объектов этого класса. Ок, где нужно хранить ссылку на этот объект? Ближе к началу примера я упомянул некий DataContext, используемый в WPF. Так вот, у любой вью есть свойство DataContext, которому мы можем присвоить ссылку на нашу вьюмодель. Сделаем это. Для этого откроем файл MainWindow.xaml и нажмём F7, чтобы открыть код этой вьюхи. Он практически пустой, в нём есть только конструктор класса окна. Добавим в него создание нашей вьюмодели и поместим её в DataContext окна (не забываем добавить using с нужным неймспейсом):
Это было просто, но этого всё равно не хватает. По-прежнему при запуске приложения никакой синхронизации текста не происходит. Что ещё нужно сделать?
Нужно вызвать событие PropertyChanged при изменении свойства SynchronizedText и сообщить вьюхе о том, что она должна следить за этим событием. Итак, чтобы вызвать событие, модифицируем код вьюмодели:
Что мы тут сделали? Добавили скрытое поле для хранения текста, обернули его в уже существующее свойство, а при изменении этого свойства не только меняем скрытое поле, но и вызываем метод OnPropertyChanged, определённый в базовой вьюмодели и вызывающий событие PropertyChanged, объявленное в интерфейсе INotifyPropertyChanged, так же реализованное в базовой вьюмодели. Получается, что при каждом изменении текста возникает событие PropertyChanged, которому передаётся имя свойства вьюмодели, которое было изменено.
Ну, почти всё, финишная прямая! Осталось указать вьюхе, что оно должно слушать событие PropertyChanged:
Помимо того, что мы указали, по какому триггеру должно происходить обновление, мы так же указали, в какую сторону это обновление отслеживается: от вью к вьюмодели или наоборот. Поскольку в текстбоксе мы вводим текст, то нам интересны изменения только во вью, поэтому выбираем режим OneWayToSource. В случае с текстблоком всё ровно наоборот: нам интересны изменения во вьюмодели, чтобы отобразить их во вью, поэтому выбираем режим OneWay. Если бы нам нужно было, чтобы изменения отслеживались в обе стороны, можно было не указывать Mode вообще, либо указать TwoWay явно.
Итак, запускаем программу, набираем текст и voi-la! Текст синхронно меняется, и мы нигде ничего не копировали!
Спасибо за внимание, продолжение следует. Будем разбираться с DataTemplate и паттерном Command.
В этой статье показан пример создания WPF-приложения на основе MVVM паттерна проектирования. За основу используется PRISM 6, как MVVM-фреймворк и Autofac, как DI-контейнер.
Создание проекта в Visual Studio
Постараюсь очень кратко изложить основную мысль, поэтому сразу к делу. Для проекта выбираем WPF Application:
Название для проекта вы можете выбрать на своё усмотрение, а я всегда придерживаюсь принципов описанных в предыдущей статье. После того, как Visual Studio создат проект, я удалю файл MainWindow.xaml, для чистоты эксперимента.
Установка Nuget-пакетов
Для построения приложения по принципам MVVM мне потребуется фреймворк, который реализует этот принцип. Причем сам фреймворк может быть написан собственноручно или создан сторонними разработчиками. Я возьму PRISM фреймворк, который был разработан Microsoft, но в дальнейшем был отдан в OpenSource другим разработчикам, которые, надо сказать, честно, продолжают наращивать функционал.
Следующий на очереди DI-контейнер. В моем случае, это Autofac. Я сразу установлю Prism.Autofac, который установит как зависимость сам Autofac:
Главное окно и его ViewModel
Теперь пришло время создать новое главное окно программы. Так повелось, что для WPF-приложения я обычно использую название для главного окна Shell. Но перед этим создаем папки Views и ViewModels. Теперь создаем Shell.xaml и ShellViewModel.cs в соответствующих папках.
Bootstrapper на Autofac
Создаем Bootstrapper, который наследуем от AutofacBootstrapper:
Не трудно заметить, что весь код состоит из переопределения перегрузов методов, где подставлются нужные нам зависимости.
App.xaml
Так как теперь для старта нашего приложения будет использоваться Bootstrapper, необходимо удалить "точку входа" из файла App.xaml.
Удалите выделенный на картинке атрибут.
ShellViewModel.cs
Чтобы наш ShellViewModel превратился в настоящий ViewModel для нашего представления, надо унаследовать класс от BindableBase, который являет собой реализацию интерфейса INotifyPropertyChanged. А еще добавил свойство DisplayName, которое буду использовать как заголовок для моего приложения.
Shell.xaml
Пришло время заняться представлением Shell. Для того, что наш ShellViewModel стал объектом DataContext для нашего Shell можно использовать несколько операций. Но для начала откройте разметку Shell.xaml и свяжите свойство Title со свойством DisplayName (из ShellViewModel).
Теперь можно как вариант в Code-Behind фале App.xaml.cs прописать следующий код
Можно запустить приложение и мы увидим заголовок главного окна программ будет "WPF PRISM (MVVM) + DI (Autofac)". Но перед тем как запустить надо инициализировать наш Bootstrapper в App.xaml.cs.
Вот теперь можно запускать
Не очень информативное представление но факт остается в следующем: MVVM - работает! Однако, надо сказать, что PRISM предоставляет более эстетичный вариант привязки View и ViewModel. Удалите из файла Shell.xaml.cs строку
Более того, я бы вам рекомендовал его больше никогда не открывать при использовании MVVM подхода.
Давайте применим другой подход. Для этого содержимое файла Shell.xaml в соответствии с приведенным листингом.
Можно запустить приложение. Результат будет тем же что и до этого, но теперь в Code-Behind файле чистота и порядок!
Заключение
Вряд ли созданный проект можно считать основой для конкретного приложения. Это просто начало начал. Для того чтобы проект стал проектом надо проделать еще кучу дополнительных телодвижений для подготовки инфраструктуры решения (solution). А вот количество телодвижений и их суть, уже зависит от того, какой тип приложения вы хотите построить: Модульное приложение, MDI-приложение, SDI-приложение и так далее. Одно могу сказать, что PRISM предоставляет все эти возможности.
Читайте также: