Симон Робинсон - C# для профессионалов. Том II
Инструкция StartPosition = FormStartPosition.Manual; говорит форме взять свою начальную позицию из свойства DeskTopLocation вместо используемого по умолчанию положения в Window (поведение по умолчанию). Возможные значения берутся из перечисления FormStartPosition.
SelfPlacingWindow также является одним из немногих приложений в этой книге, для которого существенно используется добавление кода в метод Dispose(). Напомним, что Dispose() вызывается, когда приложение завершается нормально, так что это идеальное место для сохранения конфигурационной информации в реестре. Это делается с помощью другого метода, который будет написан,— SaveSettings():
/// <summary>
/// Очистить все использованные ресурсы
/// </summary>
public override void Dispose() {
SaveSettings();
base.Dispose();
if(components != null) components.Dispose();
}
SaveSettings() и ReadSettings() являются методами, которые содержат код для работы с интересующим нас реестром, но прежде чем их рассматривать, необходимо разобраться с обработкой события, возникающего при нажатии пользователем на кнопку. Это предполагает вывод диалогового окна выбора цвета и задание цвета фона в соответствии с выбором пользователя:
void OnClickChooseColor(object Sender, EventArgs e) {
if (ChooseColorDialog.ShowDialog() == DialogResult.OK)
BackColor = ChooseColorDialog.Color;
}
Теперь посмотрим, как сохраняются настройки:
void SaveSettings() {
RegistryKey SoftwareKey = Registry.LocalMachine.OpenSubKey("Software", true);
RegistryKey WroxKey = SoftwareKey.CreateSubKey("WroxPress");
RegistryKey SelfPlacingWindowKey = WroxKey.CreateSubKey("SelfPlacingWindowKey");
SelfPlacingWindowKey.SetValue("BackColor", (object)BackColor.ToKnownColor());
SelfPlacingWindowKey.SetValue("Red", (object)(int) BackColor.R);
SelfPlacingWindowKey.SetValue("Green", (object)(int)BackColor.G);
SelfPlacingWindowKey.SetValue("Blue", (object)(int)Backcolor.В);
SelfPlacingWindowKey.SetValue("Width", (object)Width);
SelfPlacingWindowKey.SetValue("Height", (object)Height);
SelfPlacingWindowKey.SetValue("X", (object)DesktopLocation.X);
SelfPlacingWindowKey.SetValue("Y", (object)DesktopLocation.Y);
SelfPlacingWindowKey.SetValue("WindowState", (object)WindowState.ToString());
}
Мы начали с перемещения в реестре, чтобы получить ключ реестра HKLM/Software/WroxPress/SelfPlacingWindow с помощью продемонстрированной выше техники, начиная со статического свойства Registry.LocalMachine, которое представляет улей HKLM:
RegistryKey SoftwareKey = Registry.LocalMachine.OpenSubKey("Software" , true);
RegistryKey WroxKey = SoftwareKey.CreateSubKey("WroxPress");
RegistryKey SelfPlacingWindowKey = WroxKey.CreateSubKey("SelfPlacingWindowKey");
Мы используем метод RegistryKey.OpenSubKey(), а не RegistryKey.CreateSubKey(), позволявший добраться до ключа HKLM/Software. Так происходит вследствие уверенности, что этот ключ уже существует, в противном случае имеется серьезная проблема с компьютером, так как этот ключ содержит настройки для большого объема системного программного обеспечения. Мы также указываем, что нам требуется доступ для записи в этот ключ. Это вызвано тем, что если ключ WroxPress еще не существует, нам нужно будет его создать, что включает запись в родительский ключ.
Следующий ключ для перехода — HKLM/Software/WroxPress, но так как мы не уверены, что ключ уже существует, то используем CreateSubKey() для его автоматического создания, если он не существует. Отметим, что CreateSubKey() автоматически предоставляет доступ для записи к рассматриваемому ключу. Когда мы достигнем HKLM/Software/Wrox. Press/SelfPlacingWindow, то останется просто вызвать метод RegistryKey.SetValue() несколько раз, чтобы создать или задать соответствующие значения. Существуют, однако, некоторые осложнения.
Первое. Можно заметить что мы задействуем пару классов, которые раньше не встречались: свойство DeskTopPosition класса Form указывает позицию верхнего левого угла экрана и имеет тип Point. Рассмотрим структуру Point в главе GDI+. Здесь необходимо знать только, что она содержит два целых числа — X и Y, которые представляют горизонтальную и вертикальную позиции на экране. Мы также используем три свойства члена класса Color: R, G и B. Color представляет цвет, а его свойства задают красный, зеленый и синий компоненты, которые составляют цвет и имеют тип byte. Также применяется свойство Form. WindowState, содержащее перечисление, которое задает текущее состояние окна — minimized, maximized или restored.
Отметим, что при преобразовании типов SetValue() получает два параметра: строку, которая задает имя ключа, и экземпляр System.Object, содержащий значение. SetValue имеет возможность выбора формата для хранения значения, он может сохранить его как REG_SZ, REG_BINARY или REG_DWORD, и он в действительности делает правильный выбор в зависимости от заданного типа данных. Поэтому для WindowsState передается строка и SetValue() определяет, что она должна быть преобразована в REG_SZ. Аналогично для различных позиций и размеров, которые мы передаем, целые значения будут преобразованы в REG_DWORD. Однако компоненты цвета являются более сложными, но мы хотим, чтобы они также хранились как REG_DWORD, потому что они имеют числовые типы. Однако если метод SetValue() видит, что данные имеют тип byte, он будет сохранять их гак строку REG_SZ в реестре. Чтобы избежать этого, преобразуем компоненты цвета в int.
Мы также явно преобразуем все значения в тип object. На самом деле мы не обязаны этого делать, так как преобразование из любого типа данных в тип object выполняется неявно, но мы делаем это, чтобы в действительности показать происходящее и напомнить, что SetValue определен для получения объектной ссылки в качестве второго параметра.
Метод ReadSettings() немного длиннее, так как каждое считанное значение нужно также проинтерпретировать, чтобы вывести значение в окне списка и сделать подходящие изменения в соответствующем свойстве основной формы. ReadSettings() выглядит следующим образом:
bool ReadSettings() {
RegistryKey SoftwareKey = Registry.LocalMachine.OpenSubKey("Software"));
RegistryKey WroxKey = SoftwareKey.OpenSubKey("WroxPress");
if (WroxKey == null) return false;
RegistryKey SelfPlacingWindowKey = WroxKey.OpenSubKey("SelfPlacingWindow");
if (SelfPlacingWindowKey == null) return false;
else
listBoxMessages.Items.Add("Successfully opened key " + SelfPlacingWindowKey.ToString());
int RedComponent = (int)SelfPlacingWindowKey.GetValue("Red");
int GreenComponent = (int)SelfPlacingWindowKey.GetValue("Green");
int BlueComponent = (int)SelfPlacingWindowKey.GetValue("Blue");
BackColor = Color.FromArgb(RedComponent, GreenComponent, BlueComponent);
listBoxMessages.Items.Add("Background color: " + BackColor.Name);
int X = (int(SelfPlacingWindowKey.GetValue("X");
int Y = (int)SelfPlacingWindowKey.GetValue("Y");
DesktopLocation = new Point(X, Y);
listBoxMessages.Items.Add("Desktop location: " + DesktopLocation.ToString());
Height = (int)SelfPlacingWindowKey.GetValue("Height");
Width = (int)SelfPlacingWindowKey.GetValue("Width");
listBoxMessages.Items.Add("Size: " + new Size(Width, Height).ToString());
string InitialWindowState = (string)SelfPlacingWindowKey.GetValue("WindowState");
listBoxMessages.Items.Add("Window State: " + InitialWindowState);
WindowState =
(FormWindowState)FormWindowState.Parse(WindowState.GetType() , InitialWindowState)
return true;
}
В ReadSettings() мы должны сначала перейти в ключ реестра HKLM/Software/WroxPress/SelfPlacingWindow. При этом, однако, мы надеемся найти ключ, чтобы его можно было прочитать. Если его нет, то, вероятно, пример выполняется в первый раз. В этом случае мы хотим прервать чтение ключей, и, конечно, не желаем создавать какие-либо ключи. Теперь мы все время используем метод RegistryKey.OpenSubkey(). Если на каком-то этапе OpenSubkey() возвращает ссылку null, то мы знаем, что ключ реестра отсутствует, и можем вернуть значение false в вызывающий код.
В реальности для считывания ключей используется метод RegistryKey.GetValue(), который определен как возвращающий объектную ссылку, это означает, что такой метод может на самом деле вернуть экземпляр практически любого класса, который он выберет подобно SetValue(), он возвращает класс объекта, соответствующий типу данных, которые он найдет в ключе. Поэтому можно предполагать, что ключ REG_SZ будет выдан как строка, а другие ключи — как int. Мы также преобразуем соответственно возвращаемую из SetValue() ссылку. При возникновении исключения, если кто-то делал какие-то манипуляции с реестром и исказил типы данных, наше преобразование породит исключение, которое будет перехватываться обработчиком в конструкторе Form1.
Остальная часть кода использует еще один тип данных, структуру Size, выглядящую пока незнакомой, потому что она будет рассматриваться только в главе GDI+. Структура Size аналогична Point, но используется для представления размеров, а не координат. Она имеет два свойства члена — Width и Height, и мы используем структуру Size в данном случае просто как удобный способ представления размера формы для вывода в поле списка.