|
При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
События и команды в 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;
}

