Опубликован: 05.08.2010 | Доступ: свободный | Студентов: 2008 / 47 | Оценка: 4.50 / 4.40 | Длительность: 60:26:00
Самостоятельная работа 12:

События и команды в WPF

Добавление иконок

Теперь добавим к пунктам меню иконки, которые можно взять в архиве VS2008ImageLibrary.zip, устанавливаемом на компьютер вместе с VS-2008 и находящимся в каталоге Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033. Мы будем использовать иконки из папки архива VS2008ImageLibrary\Actions\24bitcolor bitmaps. Для удобства они скопированы в каталог Source данной работы.

  • В панели Solution Explorer вызовите контекстное меню для корня проекта Notepad1 и добавьте командой Add/New Folder новую папку с именем Images
  • Вызовите для папки Images контекстное меню и командой Add/Existing Item скопируйте в нее иконки с расширением .bmp из папки Source:


  • Подключите иконку NewDocument.bmp к пункту меню File/New, модифицировав разметку для этого элемента так
Было      <MenuItem Header="_New" InputGestureText="Ctrl+N">
               </MenuItem>
          
     Стало     <MenuItem Header="_New" InputGestureText="Ctrl+N">
                   <MenuItem.Icon>
                       <Image Source="Images/NewDocument.bmp" Width="16" Height="16" />
                   </MenuItem.Icon>
               </MenuItem>
  • Запустите приложение и убедитесь, что иконка появилась слева от названия пункта меню New

Здесь есть один недостаток, который бросается в глаза - фон иконки имеет цвет Magenta. В Windows Forms фон устраняется установкой параметра ImageTransparentColor в значение цвета, который система будет считать прозрачным. В WPF для нейтрализации фона можно применить другой механизм, задействующий параметр OpacityMask, но пока мы оставим все как есть.

Создание логических ресурсов

Если в каждый элемент MenuItem мы будем встраивать дескриптор <MenuItem.Icon> с элементом Image, то разметка сильно разбухнет и станет нечитабельным. Чтобы сделать код более элегантным, воспользуемся новым механизмом WPF - логическими ресурсами. Ресурсы хороши тем, что их можно использовать многократно в различных местах приложения. В нашем приложении одни и те же иконки понадобятся в меню, панели инструментов и контекстном меню.

Каждый элемент, производный от класса FrameworkElement, включая окно Window, наследует от FrameworkElement коллекцию Resources типа словаря ресурсов System.Windows. ResourceDictionary. В эту коллекцию можно помещать описания пар ключ-объект, ссылки на которые потом можно подключать к элементам управления для встраивания объектов. Такой прием называется расширением разметки интерфейсных элементов.

Обязательным элементом при объявлении и использовании ресурса является ключ x:Key="идентификатор_ключа", которому можно присваивать произвольное имя. В пределах одной коллекции ключи должны быть уникальными. Использование ссылки на ресурс для свойства элемента выполняется по синтаксису

Свойство="{StaticResource идентификатор_ключа}" 
    или 
    Свойство="{DynamicResource идентификатор_ключа}"

Синтаксический анализатор разметки XAML присвоит свойству объявленный в ресурсе объект. Если применяется ссылка на статический ресурс, то объявление ресурса должно предшествовать его использованию. Встретив ссылку на ресурс анализатор начинает поиск объекта с заданным ключом вначале в коллекции Resources того элемента, где расположена ссылка. Не найдя нужную пару анализатор поднимается на уровень выше и просматривает коллекцию родительского элемента и так далее, пока не будет найдена соответствующая ссылке пара. Таким образом, ресурсы можно объявлять в любом элементе, но лучше сосредоточить их в коллекции <Window.Resources> окна или <Application.Resources> приложения.

  • Добавьте после открывающего дескриптора окна объявление логического ресурса, а в самозакрывающемся дескрипторе элемена меню - его использование по ключу
<Window x:Class="Notepad1.Window1"
    ...............................................
        >
    <Window.Resources>
        <!-- File -->
        <Image x:Key="iconNew" Source="Images/NewDocument.bmp" Width="16" Height="16" />
    </Window.Resources>
    
    <DockPanel LastChildFill="True">
        <!-- Меню -->
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <!-- Сокращенные варианты подключения иконок с использованием статических ресурсов -->
                <MenuItem Header="_New" InputGestureText="Ctrl+N" Icon="{StaticResource iconNew}" />
                ...............................................
            </MenuItem>
            ...............................................
        </Menu>
    </DockPanel>
</Window>
  • Запустите приложение и убедитесь, что иконка присутствует слева от названия пункта меню New, но разметка элемента меню стала значительно короче
  • Расширьте объявление ресурсов на все иконки
<Window.Resources>
        <!-- File -->
        <Image x:Key="iconNew"  Source="Images/NewDocument.bmp" Width="16" Height="16" />
        <Image x:Key="iconOpen" Source="Images/Open.bmp" Width="16" Height="16" />
        <Image x:Key="iconSave" Source="Images/Save.bmp" Width="16" Height="16" />
        <Image x:Key="iconPageSetup" Source="Images/PrintSetup.bmp" Width="16" Height="16" />
        <Image x:Key="iconPrintPreview" Source="Images/PrintPreview.bmp" Width="16" Height="16" />
        <Image x:Key="iconPrint" Source="Images/Print.bmp" Width="16" Height="16" />
        <!-- Edit -->
        <Image x:Key="iconUndo" Source="Images/Edit_Undo.bmp" Width="16" Height="16" />
        <Image x:Key="iconRedo" Source="Images/Edit_Redo.bmp" Width="16" Height="16" />
        <Image x:Key="iconCut" Source="Images/Cut.bmp" Width="16" Height="16" />
        <Image x:Key="iconCopy" Source="Images/Copy.bmp" Width="16" Height="16" />
        <Image x:Key="iconPaste" Source="Images/Paste.bmp" Width="16" Height="16" />
        <Image x:Key="iconDelete" Source="Images/Delete.bmp" Width="16" Height="16" />
        <Image x:Key="iconFind" Source="Images/Find.bmp" Width="16" Height="16" />
        <Image x:Key="iconFont" Source="Images/Font.bmp" Width="16" Height="16" />
    </Window.Resources>
  • Дополните разметку создания главного меню ссылками на ресурс объекта рисунка для свойства Icon
<!-- Меню -->
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <!-- Сокращенные варианты подключения иконок с использованием статических ресурсов -->
                <MenuItem Header="_New" InputGestureText="Ctrl+N" Icon="{StaticResource iconNew}" />
                <MenuItem Header="_Open..." InputGestureText="Ctrl+O" Icon="{StaticResource iconOpen}" />
                <MenuItem Header="_Save" InputGestureText="Ctrl+S" Icon="{StaticResource iconSave}" /> 
                <MenuItem Header="Save _As..." />
                <Separator />
                <MenuItem Header="Page Set_up..." Icon="{StaticResource iconPageSetup}" />
                <MenuItem Header="P_rint Preview" InputGestureText="Ctrl+F2" Icon="{StaticResource iconPrintPreview}" />
                <MenuItem Header="_Print..." InputGestureText="Ctrl+P" Icon="{StaticResource iconPrint}" />
                <Separator />
                <MenuItem Header="E_xit" />
            </MenuItem>
            <MenuItem Header="_Edit">
                <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" Icon="{StaticResource iconUndo}" />
                <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" Icon="{StaticResource iconRedo}" />
                <Separator></Separator>
                <MenuItem Header="Cu_t" InputGestureText="Ctrl+X" Icon="{StaticResource iconCut}" />
                <MenuItem Header="_Copy" InputGestureText="Ctrl+C" Icon="{StaticResource iconCopy}" />
                <MenuItem Header="_Paste" InputGestureText="Ctrl+V" Icon="{StaticResource iconPaste}" />
                <MenuItem Header="De_lete" InputGestureText="Del" Icon="{StaticResource iconDelete}" />
                <Separator></Separator>
                <MenuItem Header="_Find..." InputGestureText="Ctrl+F" Icon="{StaticResource iconFind}" />
                <MenuItem Header="Find _Next" InputGestureText="F3" />
                <MenuItem Header="_Replace..." InputGestureText="Ctrl+H" />
                <MenuItem Header="_Go To..." InputGestureText="Ctrl+G" />
                <Separator></Separator>
                <MenuItem Header="Select _All" InputGestureText="Ctrl+A" />
            </MenuItem>
            <MenuItem Header="F_ormat">
                <MenuItem Header="_Font..." Icon="{StaticResource iconFont}" />
                <Separator />
                <MenuItem Header="_Word Wrap" IsCheckable="True" IsChecked="True" InputGestureText="Ctrl+W" />
            </MenuItem>
            <MenuItem Header="_Help">
                <MenuItem Header="_About" />
            </MenuItem>
        </Menu>
  • Запустите приложение и проверьте наличие иконок в элементах главного меню

Создание панели инструментов, строки состояния и рабочей области

Панель инструментов содержит кнопки быстрого доступа к командам меню, наиболее часто употребляемые пользователем. Обычно она размещается в верхней части рабочей области окна после меню. Панель инструментов представляет собой как минимум один контейнер для элементов управления, чаще всего кнопок, определенный классом ToolBar. Но она может состоять и из нескольких групп инструментов, где каждая группа пакуется в отдельный контейнер ToolBar. Для управления совместным стандартным поведением нескольких панелей инструментов все они упаковываются в контейнер ToolBarTray.

  • Добавьте в контейнер размещения DockPanel после разметки меню разметку создания панели инструментов
<!-- Панель инструментов -->
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <Button Width="23" Content="{StaticResource iconNew}" />
                <Button Width="23" Content="{StaticResource iconOpen}" />
                <Button Width="23" Content="{StaticResource iconSave}" />
            </ToolBar>
            <ToolBar>
                <Button Width="23" Content="{StaticResource iconUndo}" />
                <Button Width="23" Content="{StaticResource iconRedo}" />
                <Separator />
                <Button Width="23" Content="{StaticResource iconCut}" />
                <Button Width="23" Content="{StaticResource iconCopy}" />
                <Button Width="23" Content="{StaticResource iconPaste}" />
                <Button Width="23" Content="{StaticResource iconDelete}" />
            </ToolBar>
            <ToolBar Header="Find:">
                <TextBox Width="100" />
                <Button Width="23" Content="{StaticResource iconFind}" />
            </ToolBar>
        </ToolBarTray>

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

  • Добавьте после разметки панели инструментов разметку создания строки состояния
<!-- Строка состояния -->
        <StatusBar DockPanel.Dock="Bottom" Height="32" Name="statusBar">
            <Label>Simulator Application is Loading</Label>
            <Separator />
            <ProgressBar Height="20" Width="100" IsIndeterminate="True" />
        </StatusBar>

Контейнер StatusBar строки состояния мы привязали к нижней части окна и наполнили тремя элементами, последний из которых будет имитировать ход процесса загрузки приложения за счет свойства IsIndeterminate="True".

  • Добавьте после разметки строки состояния многострочное текстовое поле редактирования TextBox, который будет представлять рабочую область окна приложения и занимать все свободное пространство, поскольку в открывающем дескрипторе панели размещения мы указали для последнего элемента соответствующий параметр <DockPanel LastChildFill="True">
<!-- Многострочное текстовое поле редактирования -->
        <TextBox TextWrapping="Wrap"
                 AcceptsReturn="True" 
                 AcceptsTab="True"
                 VerticalScrollBarVisibility="Auto"
                 >
        </TextBox>

Параметр TextWrapping="Wrap" переносит строку, если она не помещается по ширине в текстовое поле. AcceptsReturn="True" включает действие клавиши для переноса строк (accept - принимать, признавать). AcceptsTab="True" включает действие клавиши табуляции в текстовом поле. VerticalScrollBarVisibility="Auto" включает автоматическое появление линейки скролирования, когда текст выходит за пределы области редактирования по высоте.

Замена встроенного контекстного меню

  • Запустите приложение и вызовите контекстное меню для элемента редактирования щелчком правой кнопкой мыши или клавиатурным жестом Shift+F10

Мы видим, что элемент редактирования имеет по умолчанию контекстное меню, которое называется встроенным. Некоторые элементы управления имеют встроенное контекстное меню с готовой поддержкой команд.

  • Проверьте, что команды меню и клавиатурные комбинации выполняют операции вырезания и копирования в буфер обмена, а также вставку из буфера. Работает также и встроенная логика отключения источников команд, когда операция невозможна, например, буфер обмена пуст и нечего вставлять

Для тренировки, в дальнейшем мы переопределим встроенное в TextBox контекстное меню и встроенные команды, а заодно и отключим клавиатурные комбинации, инициирующие встроенные команды.

  • Добавьте внутрь дескриптора TextBox разметку контекстного меню с подключенными пиктограммами для элементов
<TextBox.ContextMenu>
                <ContextMenu Width="100">
                    <MenuItem Header="Cu_t" Icon="{StaticResource iconCut}" />
                    <MenuItem Header="_Copy" Icon="{StaticResource iconCopy}" />
                    <MenuItem Header="_Paste" Icon="{StaticResource iconPaste}" />
                    <MenuItem Header="De_lete" Icon="{StaticResource iconDelete}" />
                </ContextMenu>
            </TextBox.ContextMenu>
  • Запустите проект

Обратите внимание, что применение акселератора Shift+F10 всегда вызывает наше контекстное меню в центре элемента редактирования.

Назначение ресурсов неразделяемыми

Отметьте странное поведение иконок: при открытии разделов меню соответствующая группа иконок исчезает из других элементов управления. Это происходит потому, что по умолчанию логический ресурс считается разделяемым. А это значит, что объект, объявленный в ресурсе, при многократном использовании может появляться только в одном месте. Чтобы это устранить, нужно объявить ресурс неразделяемым с помощью атрибута x:Shared="False" (share - разделять, делиться; shared - общий).

  • Добавьте в секцию объявления ресурсов атрибут x:Shared="False"
<Window.Resources>
        <!-- File -->
        <Image x:Shared="False" x:Key="iconNew"  Source="Images/NewDocument.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconOpen" Source="Images/Open.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconSave" Source="Images/Save.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPageSetup" Source="Images/PrintSetup.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPrintPreview" Source="Images/PrintPreview.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPrint" Source="Images/Print.bmp" Width="16" Height="16" />
        <!-- Edit -->
        <Image x:Shared="False" x:Key="iconUndo" Source="Images/Edit_Undo.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconRedo" Source="Images/Edit_Redo.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconCut" Source="Images/Cut.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconCopy" Source="Images/Copy.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconPaste" Source="Images/Paste.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconDelete" Source="Images/Delete.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconFind" Source="Images/Find.bmp" Width="16" Height="16" />
        <Image x:Shared="False" x:Key="iconFont" Source="Images/Font.bmp" Width="16" Height="16" />
    </Window.Resources>
  • Запустите проект - убедитесь, что сам интерфейс функционирует нормально

Замена иконок на прозрачные

И наконец, прежде чем приступить к кодированию логики, все-таки изменим фон иконок, просто заменив их на рисунки с прозрачным фоном. Такой набор иконок можно найти все в том же поставляемом вместе с Visual Studio 2008 архиве VS2008ImageLibrary.zip в папке VS2008ImageLibrary\Actions\pngformat. Для удобства этот набор скопирован в прилагаемую к работе папку Source. Это файлы типа png и их имена имеют приставку HS (Hide or Show), например, вместо Cut.bmp имя нового файла - CutHS.png.

  • Добавьте в папку Images проекта из Source 14 рисунков png командой контекстного меню Add/Existing Item
  • Командой Find and Replace (жест Ctrl+H) выполните замену в файле Window1.xaml всех вхождений .bmp на HS.png
  • Запустите проект и убедитесь, что фон иконок во всех элементах стал прозрачным

Отключение встроенных команд

Прежде, чем приступить к реализации функциональности с помощью подключения команд, потренируемся на отключении встроенных команд элемента TextBox. Обратите внимание, что хоть мы и заменили встроенное меню TextBox на свое и соответствующие опции в этом контекстном меню перестали работать, все-таки продолжают функционировать встроенные жесты, типичные для текстового поля редактирования: Ctrl+Home, Ctrl+End, Ctrl+X, Ctrl+C, Ctrl+V, Ctrl+Z, Ctrl+Y и т.д. Попробуем отключить встроенные жесты на примере команд Cut (Ctrl+X), Copy (Ctrl+C) и Paste (Ctrl+V).

  • Добавьте в открывающий дескриптор окна подписку на событие Loaded, чтобы создать обработчик для размещения процедурного кода, который будет выполнен сразу после инициализации элементов окна, определенных в разметке
<Window x:Class="Notepad1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        
    Title="Window1: Управление состоянием источников команд"
    Width="500" Height="375"
    MinWidth="500" MinHeight="375"
    WindowStartupLocation="CenterScreen"
    ResizeMode="CanResizeWithGrip"
    Loaded="Window_Loaded"
        >
    ......................................................
</Window>
  • Присвойте в разметке имя элементу TextBox для возможности его кодирования в файле Window1.xaml.cs
<TextBox TextWrapping="Wrap"
                 AcceptsReturn="True" 
                 AcceptsTab="True"
                 VerticalScrollBarVisibility="Auto"
                 Name="txtBox1"
                 >
            .................................................
        </TextBox>
  • Добавьте в обработчик события Loaded следующий код отключения встроенных жестов вырезания, копирования и вставки в элементе текстового поля
private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Отключаем в TextBox встроенный жест Ctrl+X для команды Cut
            KeyBinding keyBinding = new KeyBinding(
                ApplicationCommands.NotACommand, Key.X, ModifierKeys.Control);
            txtBox1.InputBindings.Add(keyBinding);
            // Отключаем в TextBox встроенный жест Ctrl+C для команды Copy
            keyBinding = new KeyBinding(
                ApplicationCommands.NotACommand, Key.C, ModifierKeys.Control);
            txtBox1.InputBindings.Add(keyBinding);
            // Отключаем в TextBox встроенный жест Ctrl+V для команды Paste
            keyBinding = new KeyBinding(
                ApplicationCommands.NotACommand, Key.V, ModifierKeys.Control);
            txtBox1.InputBindings.Add(keyBinding);
        }

Ключевым элементом в данном способе отключения жестов является команда NotACommand, которая означает пустую команду. Мы отключили только источники возбуждения встроенных команд для конкретного элемента TextBox, но сами встроенные команды продолжают существовать с теми же самыми жестами.

Подключение иконки приложения

  • Скопируйте из папки Source в корень проекта Notepad1 командой контекстного меню Add/Existing Item иконку Notepad.ico и подключите ее к окну Window1
<Window x:Class="Notepad1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        
    Title="Window1: Управление состоянием источников команд"
    Width="500" Height="375"
    MinWidth="500" MinHeight="375"
    WindowStartupLocation="CenterScreen"
    ResizeMode="CanResizeWithGrip"
    Loaded="Window_Loaded"
    Icon="Notepad.ico"
       >
    ......................................................
</Window>

Распределение класса по нескольким файлам и создание вспомогательных функций

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

  • В панели Solution Explorer выделите узел проекта Notepad1 и командой Project/Add Class добавьте три файла с именами File.cs, Edit.cs и Other.cs
  • Скопируйте из файла Window1.xaml.cs в каждый из этих новых файлов код подключения пространств имен и отредактируйте заготовки частей класса Window1, которые пока будут одинаковые, так
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
    
namespace Notepad1
{
    partial class Window1
    {
    }
}
  • Откройте файл Window1.xaml.cs и дополните класс Window1 вспомогательным кодом, после чего он должен стать таким
using System;
using System.Windows;
using System.Windows.Input;
    
using Microsoft.Win32;  // Для стандартных диалогов Win32
using System.IO;        // Работа с файлами и каталогами
   
namespace Notepad1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Отключаем в TextBox встроенный жест Ctrl+X для команды Cut
            KeyBinding keyBinding = new KeyBinding(
                ApplicationCommands.NotACommand, Key.X, ModifierKeys.Control);
            txtBox1.InputBindings.Add(keyBinding);
            // Отключаем в TextBox встроенный жест Ctrl+C для команды Copy
            keyBinding = new KeyBinding(
                ApplicationCommands.NotACommand, Key.C, ModifierKeys.Control);
            txtBox1.InputBindings.Add(keyBinding);
            // Отключаем в TextBox встроенный жест Ctrl+V для команды Paste
            keyBinding = new KeyBinding(
                ApplicationCommands.NotACommand, Key.V, ModifierKeys.Control);
            txtBox1.InputBindings.Add(keyBinding);
        }
    
        #region private Fields - локальные поля
    
        //------------------------------------------------------
        //
        //  private Fields - локальные поля
        //
        //------------------------------------------------------
    
        bool IsModified = false;// Флаг изменений содержимого
        string strLoadedFile;   // Полное имя загруженного документа
    
        #endregion private Fields
    
        #region Auxiliary Methods - вспомогательные методы
    
        //------------------------------------------------------
        //
        //  Auxiliary Methods - вспомогательные методы
        //
        //------------------------------------------------------
    
        // Метод возвращает true, если содержимое 
        // TextBox не требует сохранения 
        bool flag;
        bool CheckModifiedAndSaveIt()
        {
            if (!IsModified)
                return true;
    
            MessageBoxResult result =
                MessageBox.Show(
                "Сохранить изменения?", "",     // Контекст и заголовок
                MessageBoxButton.YesNoCancel,   // Кнопки диалога
                MessageBoxImage.Question,       // Иконка вопроса
                MessageBoxResult.Yes            // Кнопка с фокусом
                );
            
            switch (result)
            {
                case MessageBoxResult.Yes:
                    if (String.IsNullOrEmpty(strLoadedFile))
                        flag = DisplaySaveDialog("");   // Запись с диалогом 
                    else
                        flag = SaveFile(strLoadedFile); // Просто запись
                    break;
                case MessageBoxResult.No:
                    flag = true;
                    break;
                case MessageBoxResult.Cancel:
                    flag = false;
                    break;
            }
            return flag;
        }
    
        // Вызывает диалоговое окно записи файла
        // и возвращает true, если файл был сохранен
        bool DisplaySaveDialog(string strFileName)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "Text Documents(*.txt)|*.txt|All Files(*.*)|*.*";
            dlg.FileName = strFileName;
            bool result = (bool)dlg.ShowDialog(this);   // Желание пользователя
            if (result)
                result = SaveFile(dlg.FileName);        // Возможность компьютера
            return result;
        }
    
        // Сохраняет документ и возвращает true при успехе 
        // Аргумент - полное имя файла 
        bool SaveFile(string strFileName)
        {
            try
            {
                File.WriteAllText(strFileName, txtBox1.Text, 
                    System.Text.Encoding.GetEncoding(1251));
            }
            catch (Exception e) 
            {
                // Ловим все исключения и выводим диалог
                MessageBox.Show(
                    "Ошибка записи файла:\n" + e.Message, "",
                    MessageBoxButton.OK,
                    MessageBoxImage.Asterisk
                    );
                return false;
            }
    
            strLoadedFile = strFileName;
            UpdateTitle();      // Меняем заголовок окна
            IsModified = false; // Нет изменений текста
            return true;
        }
    
        // Диалог открытия файла возвращает true при успехе
        bool DisplayOpenDialog()
        {
            flag = CheckModifiedAndSaveIt(); // Проверяем и сохраняем изменения
            if(!flag)
                return flag;
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Text Documents(*.txt)|*.txt|All Files(*.*)|*.*";
            bool result = (bool)dlg.ShowDialog(this);   // Желание пользователя
            if (result)
                result = OpenFile(dlg.FileName);        // Возможность компьютера
            return result;
        }
    
        // Открывает файл и при успехе возвращает true
        bool OpenFile(string strFileName)
        {
            try
            {
                txtBox1.Text = File.ReadAllText(strFileName, 
                    System.Text.Encoding.GetEncoding(1251));
            }
            catch (Exception e)
            {
                // Ловим все исключения и выводим диалог
                MessageBox.Show(
                    "Ошибка чтения файла:\n" + e.Message, "",
                    MessageBoxButton.OK,
                    MessageBoxImage.Asterisk
                    );
                return false;
            }
    
            strLoadedFile = strFileName;
            UpdateTitle();      // Меняем заголовок окна
            IsModified = false; // Нет изменений текста
            // Сбрасываем границы выделенного текста поля редактирования
            txtBox1.SelectionStart = 0;
            txtBox1.SelectionLength = 0;
            return true;
        }
    
        // Коррекция заголовка окна
        void UpdateTitle()
        {
            // Извлекаем заголовок окна из словаря ресурсов
            String title = Application.Current.
                    Resources["ApplicationTitle1"].ToString();
    
            //if (strLoadedFile == null || strLoadedFile.Trim() == String.Empty)
            if (String.IsNullOrEmpty(strLoadedFile))    // Проще!
            {
                this.Title = "Untitled - " + title;
                return;
            }
    
            // Извлекаем имя файла из полного пути
            int startIndex = strLoadedFile.LastIndexOf('\\') + 1;
            int endIndex;
            // Проверяем в системном реестре настройки системы по скрытию расширения файлов
            using (RegistryKey filekey = Registry.CurrentUser.CreateSubKey(
                @"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"))
            {
                if ((filekey != null) && (filekey.GetValue("HideFileExt", 0).ToString() == "0"))
                {
                    endIndex = strLoadedFile.Length;            // Нет расширения
                }
                else
                {
                    endIndex = strLoadedFile.LastIndexOf('.');  // Отсекаем расширение
                }
            }
    
            if (endIndex > startIndex)
            {
                this.Title = strLoadedFile.Substring(startIndex) + 
                    " - " + title;
            }
            else 
            {
                this.Title = strLoadedFile.Substring(startIndex, endIndex - startIndex) +
                    " - " + title;
            }
        }
    
        #endregion  Auxiliary Methods
    }
}

Смысл добавленного кода подробно помечен коментариями и следует его внимательно изучить. Ключевым полем работы кода является флаг IsModified, сигнализирующий об изменении содержимого элемента TextBox. Поднятие флага выполним в обработчике события TextChanged этого элемента.

  • В разметку TextBox добавьте регистрацию обработчика события TextChanged и создайте сам обработчик
<!-- Многострочное текстовое поле редактирования -->
        <TextBox TextWrapping="Wrap"
                 AcceptsReturn="True" 
                 AcceptsTab="True"
                 VerticalScrollBarVisibility="Auto"
                 Name="txtBox1"
                 TextChanged="txtBox1_TextChanged"
                 >
            ....................................................
        </TextBox>
  • Обработчик txtBox1_TextChanged() заполните так
private void txtBox1_TextChanged(object sender,
            System.Windows.Controls.TextChangedEventArgs e)
        {
            if (IsModified)
                return;
            else
                IsModified = true;
        }
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?