Опубликован: 05.08.2010 | Уровень: специалист | Доступ: платный
Лекция 5:

Работа с файлами и каталогами

Сохранение состояния (параметров) приложения

В завершение, на примере последнего упражнения, добавим средства сохранения состояния, чтобы при новом запуске прежние настройки текстовых полей и текущий каталог остались. Существует несколько (рассмотрим три) стандартных способов решения этой задачи и все они основаны на использовании абстрактного класса ApplicationSettingsBase:

  1. Программный - вручную создать класс, производный от ApplicationSettingsBase, и привязать в коде нужный параметр к сохраняемому свойству формы или элемента управления
  2. Воспользоваться страницей Settings конструктора проектов (вызывается командой Project/App7 Properties ). Страница автоматически генерируется оболочкой при создании заготовки проекта и базируется на файле Properties\Settings.Designer.cs.
  3. Воспользоваться панелью Properties для формы или элемента управления и привязать нужный параметр непосредственно к его свойству.

Два последних способа относятся к режиму разработки приложения (не к режиму выполнения). Их подробное описание можно найти в документации по ссылке: http://msdn.microsoft.com/ru-ru/library/a65txexh(VS.90).aspx

Способ 1.

Вначале рассмотрим программный способ (мы же программисты!).

  • Командой Project/Add Class добавьте новый файл с именем MySettings.cs, который заполните так
using System;
using System.Configuration;
    
namespace App7
{
    class MySettings : ApplicationSettingsBase
    {
        // Параметр типа 'Приложение' менять нельзя во время работы приложения
        // Параметр типа 'Пользователь' можно менять во время работы приложения
        //[ApplicationScopedSetting()]// Область действия - 'Приложение'
        [UserScopedSettingAttribute()]       // Область действия - 'Пользователь' 
        [DefaultSettingValueAttribute("0")]  // Значение параметра по умолчанию
        public String Min
        {
            get
            {
                return ((String)this["Min"]);
            }
            set
            {
                this["Min"] = (String)value;
            }
        }
    
        [UserScopedSettingAttribute()]
        [DefaultSettingValueAttribute("250")]
        public String Step
        {
            get
            {
                return ((String)this["Step"]);
            }
            set
            {
                this["Step"] = (String)value;
            }
        }
    
        [UserScopedSetting()]
        [DefaultSettingValue("2000")]
        public String Count
        {
            get
            {
                return ((String)this["Count"]);
            }
            set
            {
                this["Count"] = (String)value;
            }
        }
    
        [UserScopedSetting()]
        [DefaultSettingValue("ResultNumber.txt")]
        public String FileName
        {
            get
            {
                return ((String)this["FileName"]);
            }
            set
            {
                this["FileName"] = (String)value;
            }
        }
    
        [UserScopedSetting()]
        [DefaultSettingValue("")]
        public String DirTarget
        {
            get
            {
                return ((String)this["DirTarget"]);
            }
            set
            {
                this["DirTarget"] = (String)value;
            }
        }
    
        [UserScopedSetting()]
        [DefaultSettingValue("")]
        public String Series
        {
            get
            {
                return ((String)this["Series"]);
            }
            set
            {
                this["Series"] = (String)value;
            }
        }
    }
}

Здесь мы определили общедоступные свойства с ассоциативными именами (чтобы лучше различать) и прикрепили к каждому из них аттрибуты, определяющие область видимости (действия) параметра и значение по умолчанию. Параметр типа UserScopedSetting (или UserScopedSettingAttribute - полное имя) пользователь может менять во время работы приложения, а типа ApplicationScopedSetting - нет (например, строки подключения).

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

  • Модифицируйте в файле Form1.cs конструктор класса формы приложения следующим образом
// Объявили и инициализировали поле для объекта параметров
        MySettings settings = new MySettings(); 
        public Form1()
        {
            InitializeComponent();
    
            // Привязка именованных параметров к 
            // сохраняемым свойствам элементов управления
            txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
            txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
            txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
            txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
            txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
            txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
        }
  • Запустите/завершите приложение - пока значения текстовых полей не сохраняются

Причина здесь в том, что привязанные параметры нужно явно записывать при завершении приложения. Обычно это делается в обработчике события FormClosing формы.

  • Выделите форму в режиме Design, перейдите в панель Properies/Events и двойным щелчком на поле события FormClosing создайте для него обработчик, который заполните так
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            settings.Save();
        }
  • Запустите/завершите приложение - значения текстовых полей сохраняются, кроме поля txtDirTarget

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

  • Дополните обработчик кнопки btnDirTarget строкой передачи фокуса текстовому полю txtDirTarget так
private void btnDirTarget_Click(object sender, EventArgs e)
        {
            dialogFolder.ShowDialog();
            txtDirTarget.Text = dialogFolder.SelectedPath;
            txtDirTarget.Focus();// Без этого не сохраняет параметр в settings
        }

Последний оператор устанавливает на текстовое поле txtDirTarget фокус ввода, без чего значение Text не сохраняется в установках приложения при его закрытии (если только явно не щелкнуть по нему курсором).

  • Запустите/завершите приложение - теперь состояние приложения сохраняется полностью

Код, помещенный нами ранее в начало функции Form1_Load(), выполняет проверку на случай первого запуска приложения или когда между запусками сохраненное значение каталога изменилось. Чтобы испытать его работу, можно добавить такой обработчик двойного щелчка формы

private void Form1_DoubleClick(object sender, EventArgs e)
        {
            settings.DirTarget = "";
            txtDirTarget.Text = "";
            dialogFolder.SelectedPath = "";
            dialogFolder.RootFolder =
                System.Environment.SpecialFolder.MyComputer;
        }

Способ 2.

При создании нового проекта мастер оболочки попутно создает заготовки файлов Settings.settings и Settings.Designer.cs, в которые на этапе проектирования можно записать для компилятора исходные данные параметров, подлежащих сохранению. Эти файлы находятся в служебной папке Properties каталога проекта. Для удобства выполнения такой работы оболочка имеет вкладку (страницу) Settings панели конструктора проектов.

Чтобы продолжить рассмотрение Способа 2, необходимо вначале нейтрализовать весь код Способа 1, поскольку сохранять мы будем те же самые свойства текстовых полей.

  • В панели Solution Explorer выделите файл MySettings.cs и командой Exclude From Project контекстного меню (либо в разделе Project меню оболочки) исключите его из проекта
  • Закомментируйте (не удаляйте!) в конструкторе класса формы код привязки сохраняемых параметров к свойствам элементов
// Объявили и инициализировали поле для объекта параметров
        //MySettings settings = new MySettings(); 
        public Form1()
        {
            InitializeComponent();
    
            // Привязка именованных параметров к 
            // сохраняемым свойствам элементов управления
            /*////////////////////////////////////////////////////
            txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
            txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
            txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
            txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
            txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
            txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
            ////////////////////////////////////////////////////*/
        }
  • Командой Project/App7 Properties откройте панель конструктора проектов на вкладке Settings и настройте ее как показано на снимке

Если щелкнуть на пиктограмме View Code вкладки Settings, то оболочка создаст в корневом каталоге проекта файл Settings.cs с еще одной частью класса Settings (первая часть находится в Properties\Settings.Designer.cs ) для более тонкого управления параметрами.

  • Сохраните документ командой File/Save All - оболочка создала конфигурационный файл приложения app.config и добавила в него наши настройки

Теперь осталось подключить эти настройки к коду приложения.

  • Модифицируйте в файле Form1.cs члены класса формы следующим образом
// Объявили и инициализировали поле для объекта параметров
        //MySettings settings = new MySettings(); 
        Properties.Settings settings = new App7.Properties.Settings();
        public Form1()
        {
            InitializeComponent();
    
            // Привязка именованных параметров к 
            // сохраняемым свойствам элементов управления
            /*////////////////////////////////////////////////////
            txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
            txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
            txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
            txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
            txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
            txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
            ////////////////////////////////////////////////////*/
    
            // Чтение параметров
            //*////////////////////////////////////////////////////
            txtMin.Text = settings.Min;
            txtStep.Text = settings.Step;
            txtCount.Text = settings.Count;
            txtFileName.Text = settings.FileName;
            txtDirTarget.Text = settings.DirTarget;
            txtSeries.Text = settings.Series;
            ////////////////////////////////////////////////////*/
        }
    
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Запись параметров
            //*////////////////////////////////////////////////////
            settings.Min = txtMin.Text;
            settings.Step = txtStep.Text;
            settings.Count = txtCount.Text;
            settings.FileName = txtFileName.Text;
            settings.DirTarget = txtDirTarget.Text;
            settings.Series = txtSeries.Text;
            ////////////////////////////////////////////////////*/
    
            settings.Save();
        }
  • Запустите проект в режиме Release, измените настройки и закройте приложение - параметры сохраняются
  • В панели Solution Explorer щелкните на пиктограмме Show All Files - к исполнимой сборке добавился файл конфигурации App7.exe.config

Теперь без этого файла приложение работать не будет и при развертывании приложения на других компьютерах файл придется включать в установочный пакет (это не страшно, только бы не забыть!). Файл App7.exe.config имеет такое же содержимое (откройте его!), что и app.config, но носит имя приложения.

Действием, приведенным в последнем листинге, нам пришлось добавить код чтения и записи параметров, потому что перед этим мы закомментировали (но не удалили!) код их привязки к свойствам интерфейсных элементов. Если теперь закомментировать свежие блоки кода, а раскомментировать код привязки, то функциональность приложения не изменится. Проверьте это, просто убрав или добавив слэш перед открывающей комментарной скобкой.

Способ 3.

Этот способ выполняет создание и привязку параметров через панель свойств и подробно описан в документации http://msdn.microsoft.com/ru-ru/library/wabtadw6%28v=VS.90%29.aspx

Как мы сейчас увидим, в конечном итоге это то же самое, что и предыдущий способ сохранения параметров на этапе разработки. Только он больше подходит для сохранения свойств именно компонентов, а не прочих объектов приложения. Рассмотрим его применительно к нашей задаче.

  • Командой Project/App7 Properties откройте панель конструктора проектов на вкладке Settings и удалите прежние параметры (по одному или сразу все)

  • Через Solution Explorer удалите файл app.config из корневого узла текущего проекта
  • В режиме Design выделите элемент txtMin, в панели Properties раскройте узел ApplicationSettings и щелкните на кнопке с многоточием в поле свойства PropertyBinding

  • Выделите привязываемое свойство Text элемента txtMin и в раскрывающемся списке выполните ссылку New для вызова окна создания нового параметра

  • Заполните окно как показано на снимке

  • Аналогичным образом создайте и настройте параметры и для остальных элементов со значениями из предыдущих способов

Обратите внимание, что начальные значения параметров (если они есть) подставляются оболочкой из свойств компонентов, которые мы определяли при их создании. В итоге мы получим точно такой же результат, что и в предыдущем способе. Поэтому код в классе Form1, связанный с настройками, менять не будем - он у нас уже есть.

  • Запустите проект - параметры сохраняются, но опять при развертывании (распространении программы) нужно таскать и файл app.config (или точно такой же App7.exe.config )

Итак, в данном упражнении мы спроектировали инструмент, который может оказаться удобным при формировании некоторых номеров случайным образом (чтобы враги уж совсем не угадали, можно еще и зашифровать). При этом применили знания по теме данной работы. Из расссмотренных способов мне больше нравится первый. Чтобы к нему вернуться, нужно вновь добавить в проект файл MySettings.cs и удалить из него все, связанное с двумя последними способами.

Следует заметить, что содержимое файла Settings.Designer.cs в папке Properties проекта, полученное после настроек с использованием любого из последних двух способов, почти полностью идентично содержимому файла MySettings.cs, который мы формировали вручную в первом способе. Отсюда мораль: если не хотим таскать за собой файл app.config, то вначале быстренько создаем параметры через оболочку, затем перетаскиваем созданный ею код в свой класс. После этого опять удаляем все параметры (как перед выполнением способа 3) и файл конфигурации, а используем уже наш класс с параметрами программным образом. (Что называется, - 'немного подъехать' на возможностях оболочки!)


Алексей Бабушкин
Алексей Бабушкин

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

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

Сергей Гутько
Сергей Гутько
Россия, Минск