Надень шкуру на окно

Написал John Frost, в Статьи » .Net.
Автор: John Frost
testskins.rar [171.21 Kb] (cкачиваний: 24)

Ты, наверное, частенько видел, как у разного софта можно было менять пользовательский интерфейс за счет, так называемых skins или шкурок по-русски. Хочешь научится делать свои окошки изменяемыми? ОК! Тогда я покажу тебе, насколько просто и легко, это реализуется. Мы с тобой создадим небольшую библиотеку, которую ты сможешь подключать к своим проектам, и затем, написав всего две строчки кода, заставишь свои окошки и кнопки быть такими, какими ты захочешь – без затрагивания исходников.

Для начала определимся, что же мы хотим.
1) Возможности для окна
А) Изменение цвета фона.
Б) Изменение заголовка
В) Изменение фоновой картинки
Г) Изменение положения картинки (растянуто, замостить, посередине и т.д.)
2) Возможности для контролов
А) Имя контрола (button1 к примеру), не изменяется
Б) Изменение заголовка
В) Изменение цвета фона
Г) Изменение положения
Д) Изменение размера
Е) Изменение видимости (можно будет скрывать или отображать, меня тем самым функциональность)
Ж) Изменение доступности (блокированный или доступный элемент)
И) Изменение привязки (по левому краю, во всю область и т.д.)
К) Изменение шрифта и его размера
Л) Изменение цвета текста
М) Изменение фоновой картинки
Н) Изменение положения картинки (растянуто, замостить, посередине и т.д.)

Обязательным условием является то, что встроить поддержку шкурок в любой проект должно быть элементарно (несколько строк кода), и не должно предъявляться никаких требований к ранее созданным окнам. Т.е. как есть, так и есть.
Так, теперь подумаем, как же мы будем все это реализовывать. Control - определяет базовый класс для элементов управления, являющихся компонентами с визуальным представлением. Вспомним, как создается и настраивается типичный контрол на форме в коде:
private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
//т.д.
this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
            this.groupBox1.Location = new System.Drawing.Point(12, 12);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(536, 175);
            this.groupBox1.TabIndex = 0;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Объекты";
}

Это стандартный код, который генерится Visual Studio’ей. Если по началу у вас появится стойкое и непреодолимое желание перегрузить эту функцию и вставить туда код загрузки скинов – то пожалуйста, обратитесь к психотерапевту. К чему я привел этот код? Да к тому, что как видишь, все параметры можно изменять на лету, т.е. пусть вызывается функция инициализации, затем мы отловливаем события загрузки формы и там уже меняем нужным элементам их свойства.
Так стоп! Что загружать, если мы еще не сохранили данные о форме в файл? Значит займемся сохранением (о да, детка, давай займемся сохранением!). Чтобы не мучиться с придумыванием формата, заюзаем XML. И поможет нам в этом бодрый и хороший класс XmlWriter,он представляет средство записи, обеспечивающее быстрый прямой способ (без кэширования) создания потоков файлов, содержащих XML-данные.
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
writer = XmlWriter.Create(Path+"\\"+f.Name+".jfskin", settings);


Для начала мы создаем объект для настроек, затем создаем сам ХmlWriter, и заметь создаем мы его не конструктором, это абстрактный класс, мы вызываем статический метод, который и возвращает нам экземпляр этого класса. Теперь, чтобы записать свойство Text в файл, мы должны выполнить вот такой код:
writer.WriteStartElement("Text");
writer.WriteElementString("value", c.Text);
writer.WriteEndElement();

В файле это будет выглядеть вот так:
<Text>
    <value>ЙА АЦЦКАЯ ПРОГРАММА</value>
</Text>


Вот мы и готовы к тому, чтобы написать функцию, которая сохраняет в файл нужные свойства одного контрола. Главное не забыть, что любой контрол может тоже содержать в себе контролы, поэтому функция будет рекурсивно вызывать сама себя, если внутри контрола, будут еще контролы (это предложение взрывает мне мозг).
private void SaveControl(Control c)
       {
           if (c.Name == "")//не сохраняем если нет имени у элемента
           {
               return;
           }

           writer.WriteStartElement("Control");//первый элемент

           writer.WriteStartElement("Name");
           writer.WriteElementString("value", c.Name);
           writer.WriteEndElement();

           writer.WriteStartElement("Text");
           writer.WriteElementString("value", c.Text);
           writer.WriteEndElement();

           writer.WriteStartElement("BackColor");
           writer.WriteElementString("value", c.BackColor.R + ";" + c.BackColor.G + ";" + c.BackColor.B);
           writer.WriteEndElement();

           writer.WriteStartElement("Location");
           writer.WriteElementString("value", c.Location.X.ToString() + ";" + c.Location.Y.ToString());
           writer.WriteEndElement();

           writer.WriteStartElement("Size");
           writer.WriteElementString("value", c.Size.Width.ToString() + ";" + c.Size.Height.ToString());
           writer.WriteEndElement();

           writer.WriteStartElement("Visible");
           writer.WriteElementString("value", c.Visible.ToString());
           writer.WriteEndElement();

           writer.WriteStartElement("Enabled");
           writer.WriteElementString("value", c.Enabled.ToString());
           writer.WriteEndElement();

           writer.WriteStartElement("Dock");
           writer.WriteElementString("value", (c.Dock).ToString());
           writer.WriteEndElement();

           writer.WriteStartElement("Font");
           writer.WriteElementString("value", (c.Font.FontFamily).Name + ";" + c.Font.Size.ToString() + ";" + c.Font.Bold.ToString() + ";" + c.Font.Italic.ToString());
           writer.WriteEndElement();

           writer.WriteStartElement("ForeColor");
           writer.WriteElementString("value", c.ForeColor.R + ";" + c.ForeColor.G + ";" + c.ForeColor.B);
           writer.WriteEndElement();

           writer.WriteStartElement("BackgroundImage");
           writer.WriteElementString("value", " ");
           writer.WriteEndElement();

           writer.WriteStartElement("BackgroundImageLayout");
           writer.WriteElementString("value", (c.BackgroundImageLayout).ToString());
           writer.WriteEndElement();


           writer.WriteEndElement();
           foreach (Control con in c.Controls)
           {
               SaveControl(con);
           }
       }

Почему мы не сохраняем если нету имени у котрола? Да потому что по имени мы и будет потом загружать эти данные. Так, теперь чтобы сохранить все контролы, какие есть на форме и саму форму, мы должны будем всеголишь выполнить такой код:
public  bool SaveForm(Form f)//сохранить все настройки элементов формы в файл
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.NewLineOnAttributes = true;
            settings.ConformanceLevel = ConformanceLevel.Auto;
            writer = XmlWriter.Create(Path+"\\"+f.Name+".jfskin", settings);
           // writer.WriteStartDocument();
            writer.WriteStartElement("Form");
            //-----------------------------
            writer.WriteStartElement("BackColor");
            writer.WriteElementString("value", f.BackColor.R + ";" + f.BackColor.G + ";" + f.BackColor.B);
            writer.WriteEndElement();

            writer.WriteStartElement("Text");
            writer.WriteElementString("value", f.Text);
            writer.WriteEndElement();

            writer.WriteStartElement("BackgroundImage");
            writer.WriteElementString("value", " ");
            writer.WriteEndElement();

            writer.WriteStartElement("BackgroundImageLayout");
            writer.WriteElementString("value", (f.BackgroundImageLayout).ToString());
            writer.WriteEndElement();
            //--------------------------
            writer.WriteEndElement();
            foreach (Control c in f.Controls)
            {

                SaveControl(c);
              
            }
          // writer.WriteEndDocument();
            writer.Flush();
            writer.Close();
            return true;
        }

В папке с бинарником появится файл примерно вот такого содержания:
Надень шкуру на окно
Все, с сохранение разобрались, теперь попробуем написать метод, который загружает эти данные. Процесс чтения, как вы уже наверное догадались, происходит с помощью XmlReader и выглядит примерно вот так:
reader.ReadStartElement("Text");
c.Text = reader.ReadElementString();
reader.ReadEndElement();

Вообщем идет весь процесс наоборот, так что описывать тут код нет смысла. Единственное что здесь нужно добавить, это конструктор и функцию получения доступных шкурок.
public Skin(string exepath)//конструктор, принимает путь откуда экзе запускается
        {
            Path = exepath;
            skins = GetAvailableSkins();
        }
        public DirectoryInfo[] GetAvailableSkins()//дать список доступных шкурок
        {
            DirectoryInfo di = new DirectoryInfo(Path+"\\skins");
            DirectoryInfo[] ret = di.GetDirectories();
            return ret;
        }  

Ну вот и готово! Теперь в событии открытия формы прописываем вот эту строчку:
skin.SaveForm(this);

Форма сохранится, создаем папку в \\skins, кидаем туда этот файл, затем заменяем предыдущую строку на:

skin.CurrentSkin = "blackskin";
skin.OpenForm(this);


Изменяем в скине нужные параметры, запускаем и наслаждаемся!
Надень шкуру на окно
#1 Вангер |  

о, вот и от других авторов статьи
в фирменном стиле Джонни )))
Дата публикации: 8 мая 2009 16:35 | ICQ: --
цитировать
#2 John Frost |  

Ну это не совсем статья, так мини-обзор в стиле "How do" :-) Надеюсь кому-нибудь поможет
Дата публикации: 8 мая 2009 23:46 | ICQ: 488251
цитировать
#3 Lex Luthor |  

Комментарии «+1, зачёт» и т.п будут удалятся!
Дата публикации: 9 мая 2009 14:39 | ICQ: --
цитировать
#4 DeXPeriX |  

Ых. Уж надеялся, что что-нибудь интересное увижу sad
Со скинами пипец как пришлось потрахаться с http://dexperix.net/soft/free/vertex-engine+npi+ru.html . Там чтобы прорисовать кнопочку не грузя изображений рисую её циклом по одной линии определённого цвета. Но это получился действительно свой внешний вид, а не только переопределение цвета. Приложение выглядит красивым когда не только цвета сбалансированны, но и формы подобраны правильно. И ещё всё это должно гармонировать с системой. Т.е. как минимум нужно "Standart windows skin" включить, который будет считывать СИСТЕМНЫЕ значения цветов. Как следствие - подходить как под XP-style, так и под 9x, так и под Vista-style.
Дата публикации: 9 мая 2009 15:21 | ICQ: 606986
цитировать
#5 John Frost |  

Ты вообще представляешь чего можно добиться используя только цвета и правильно подобранные картинки?
http://coderszone.info/uploads/posts/2009-05/1241871323_skin
.jpg
http://coderszone.info/uploads/posts/2009-05/1241871809_skin
2.jpg
http://coderszone.info/uploads/posts/2009-05/1241871854_skin
3.jpg
на трех скриншотах одна и та же программа, использует такие же простые скины...
Хочешь сказать не смотрится?
Дата публикации: 9 мая 2009 16:25 | ICQ: 488251
цитировать
#6 DeXPeriX |  

Представляю. Ещё как. Особенно если как у тебя в этих примерах убрать нах стандартное вендошное оформление и оставить только картинки. Только это - частное решение. А я говорю про общее. На примере той же кнопки - чтоб ей прям в проге можно было динамически менять текст и размеры и чтобы она прилично смотрелась. А не при каждом изменении новую картинку поверх рисовать... (которую до этого ещё хрен знает сколько фотошопить)
ЗЫ А слайдеры у тебя вообще не заскинённые. Из-за них косяк может выползти...
Дата публикации: 9 мая 2009 22:02 | ICQ: 606986
цитировать
#7 John Frost |  

Короче все вышло как обычно:) Написал калькулятор, сказали что это плохой медиаплеер :-)
Я показал простой и легкий для новичка способ создания скинов :-)
Если ты можешь показать чтото круче - будем только рады увидеть здесь твое творение:-)
Дата публикации: 10 мая 2009 00:15 | ICQ: 488251
цитировать
#8 Vanger |  

ухаха я пооффтоплю )))
Джон Фрост живет на Кавказе, и он написал статью про СКИНОВ )))) Джонни, а твои коллеги на работе знают? ))))
Дата публикации: 19 мая 2009 19:11 | ICQ: --
цитировать
#9 John Frost |  

Знают wink
Дата публикации: 22 мая 2009 13:50 | ICQ: 488251
цитировать

Добавить комментарий


Включите эту картинку для отображения кода безопасности
обновить код



 

Лучшие новости

Наш опрос

Мы в интернете