PDA

Просмотр полной версии : [Руководство] Создаем свой внешний вид окна (WinForms). C# version


Kitsune
31.08.2010, 23:26
> Создаем свой внешний вид окна (WinForms) <

В данной статье речь пойдет о том как придать уникальности нашему приложени и в тоже время украсить.

Конечный материал данной статьи (скрин):


[Ссылки могут видеть только зарегистрированные и активированные пользователи]


Теория:

В данной статье мы будем рассматривать самый простой пример, а именно мы немного изобретем велосипед :)
Как же получить вместо обычного WinForm окна, окошко наподобе моего?
Все очень просто. Сначала скрываем существующие оформление, потом рисуем своё. Просто, не так ли?


Практика - Дизайнер:

Для начала создадим новый WinForms проект
Затем изменим пару свойств у нашей формы:



FormBorderStyle = None
Убираем стиль окна
Size = 400; 300
Устанавливаем размер
StartPosition = CenterScreen
Устанавливаем начальную позицию окна
TransparencyKey = Magneta (255; 0; 255)
Выставляем ключ прозрачности (По скольку мы рассматриваем примитивный вариант, то речи об Alpha канале быть не может и прозрачность мы зададим как ярко фиолетовый цвет.
Это означает, что любой пиксель на форме этого цвета будет прозрачным)



Разместим на форуме 3 PictureBox и выставим для них пару свойств:



1ый - мы назовем pTitleBarLeft
Location = 0; 0
Size = 65; 24
2ой - мы назовем pTitleBarCenter
Anchor = Top, Left, Right
Location = 65; 0
Size = 330; 24
3ий - мы назовем pTitleBarRight
Anchor = Top, Right
Location = 395; 0
Size = 5; 24



Размеры pTitleBarLeft и pTitleBarRight выставляются такие же, как и ваши картинки.
Размер pTitleBarCenter выставляется такой, чтобы заполнить все пустое пространство между левой и правой частью.


После этих изменений, у нас должны быть такая картина:

[Ссылки могут видеть только зарегистрированные и активированные пользователи]


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



buttons_normal.png - левая часть Тайтла(обычное состояние)
buttons_hover.png - левая часть Тайтла(активное состояние)
bg.png - фон центральной части(Тейл) Не путать с Тайтлом.
right.png - правая часть Тайтла



Тейл* - Это вид наложения спрайтов. Смысл в том, что мы берем текустуру 1х24 (bg.png) и высталяем её как BackgroundImage.
При отрисовке будет происходить следующее, сначала нарисуется 1 текстура, рядом другая такая же и т.д. пока все свободное место блока, в котором она рисуется не будет занято.


Чтобы добавить ресуры в проект, нам необходимо открывать Редактор ресурсов.
Для этого два раза кликните по файлу: Resources.resx в обозревателе решения.

[Ссылки могут видеть только зарегистрированные и активированные пользователи]


В появившемся окне, выберите закладку из выпадающего списка Images.

[Ссылки могут видеть только зарегистрированные и активированные пользователи]


Перетащите файлы туда с рабочего стола или любой папки или же добавьте с помощью соседнего выпадающего списка Add resource -> Add existing file... В конечном счете у вас должно получится так:

[Ссылки могут видеть только зарегистрированные и активированные пользователи]


Практика - Код:

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

// Свойство: для хранения заголовка окна
public String CustomTitleText = "Leopard Style Window © 2010 TBX1n";
// Свойство: для хранения шрифта заголовка окна
public Font CustomTitleFont = new Font("Tahoma", 10f, FontStyle.Bold);

// Свойство: флаг, зажата ли кнопка мыши, чтобы тащить окно
private bool movingWindow = false;
// Свойство: объект для хранения положения курсора до начала перемещения
private Point oldCursorPosition;

В конструктор формы добавим следующий код:

PrepairForm();

// Событие: рисование формы
this.Paint += new PaintEventHandler(Main_Paint);
// Событие: рисование центральной части Тайтла
pTitleBarCenter.Paint += new PaintEventHandler(pTitleBarCenter_Paint);
// Событие: зажатие кнопки мыши на центральной части Тайтла
pTitleBarCenter.MouseDown += new MouseEventHandler(pTitleBarCenter_MouseDown);
// Событие: отжатие кнопки мыши на центральной части Тайтла
pTitleBarCenter.MouseUp += new MouseEventHandler(pTitleBarCenter_MouseUp);
// Событие: перемещение курсора по центральной части Тайтла
pTitleBarCenter.MouseMove += new MouseEventHandler(pTitleBarCenter_MouseMove);
// Событие: попадание курсора в левую область Тайтла (с кнопками)
pTitleBarLeft.MouseEnter += new EventHandler(pTitleBarLeft_MouseEnter);
// Событие: выход курсора из левой части Тайтла
pTitleBarLeft.MouseLeave += new EventHandler(pTitleBarLeft_MouseLeave);
// Событие: клик по левой части Тайтла
pTitleBarLeft.MouseClick += new MouseEventHandler(pTitleBarLeft_MouseClick);

А рядом с конструктором опишем метод PrepairForm, который мы вызываем в конструкторе формы.

private void PrepairForm()
{
// Устанавливаем стили окна
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, true);

// Устанавливаем стандартные фоновые изображения для Тайтла
pTitleBarLeft.BackgroundImage = global::VisualTweak.Properties.Resources.buttons_n ormal;
pTitleBarCenter.BackgroundImage = global::VisualTweak.Properties.Resources.bg;
pTitleBarRight.BackgroundImage = global::VisualTweak.Properties.Resources.right;
}

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

private void Main_Paint(object sender, PaintEventArgs e)
{
DrawBorder(e.Graphics);
}

private void pTitleBarCenter_Paint(object sender, PaintEventArgs e)
{
DrawTitle(e.Graphics);
}

private void pTitleBarCenter_MouseDown(object sender, MouseEventArgs e)
{
// Перед началом перемещения окно сохраняем текущие координаты курсора мышки
oldCursorPosition = new Point(e.X, e.Y);
// Устанавливаем флаг на Да
movingWindow = true;
}

private void pTitleBarCenter_MouseUp(object sender, MouseEventArgs e)
{
// Когда мы отпускаем кнопку мышки, устанавливаем флаг на Нет
movingWindow = false;
}

private void pTitleBarCenter_MouseMove(object sender, MouseEventArgs e)
{
//Если флаг имеет значение Да
if (movingWindow)
{
// Получаем текущее положение курсора
Point newCursorPosition = new Point(e.X, e.Y);
// Получаем разницу между текущим положением курсора и тем, что было
// до того как мы начали перемещать курсор
newCursorPosition.X = newCursorPosition.X - oldCursorPosition.X;
newCursorPosition.Y = newCursorPosition.Y - oldCursorPosition.Y;
// К текущему положению окна прибавляем разницу, чтобы получить сдвиг
// в нужные стороны
this.Location = new Point(this.Location.X + newCursorPosition.X,
this.Location.Y + newCursorPosition.Y);
}
}

private void pTitleBarLeft_MouseEnter(object sender, EventArgs e)
{
// Когда курсор мыши попадает в область левой части Тайтла, то мы меняем фон
// на другой (в нашем случаи это фон с нажатыми кнопками)
pTitleBarLeft.BackgroundImage = global::VisualTweak.Properties.Resources.buttons_h over;
}

private void pTitleBarLeft_MouseLeave(object sender, EventArgs e)
{
// Когда курсор мыши уходит из области левой части Тайтла, то мы возвращаем фон
// в обычное состояние
pTitleBarLeft.BackgroundImage = global::VisualTweak.Properties.Resources.buttons_n ormal;
}

private void pTitleBarLeft_MouseClick(object sender, MouseEventArgs e)
{
CommandButtonsClick(e.Location);
}

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

private void CommandButtonsClick(Point e)
{
// Проверка по высоте
if (e.Y > 1 && e.Y <= 23)
{
// Красная кнопка (Закрыть)
if (e.X > 1 && e.X <= 26)
{
this.Close();
}
// Оранжевая кнопка (Свернуть)
else if (e.X > 26 && e.X <= 47)
{
this.WindowState = FormWindowState.Minimized;
}
// Зеленая кнопка (Развернуть)
else if (e.X > 47 && e.X <= 65)
{
if (this.WindowState == FormWindowState.Maximized)
this.WindowState = FormWindowState.Normal;
else this.WindowState = FormWindowState.Maximized;

// Вызываем принудительную перерисовку окно,
// чтобы наши рамки формы отрисовались правильно
this.Invalidate();
}
}
}

/// <summary>
/// Метод, который рисует текст в Тайте
/// </summary>
private void DrawTitle(Graphics g)
{
// Устанавливаем сглаживание на максимальное
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality ;

// Узнаем сколько место займет наш текст
// Это нам необходимо, чтобы определить в каком точно месте расположить его
SizeF CustomTitleTextSize = g.MeasureString(CustomTitleText, CustomTitleFont);

// Рисуем текст
// Передаем: текст, шрифт, кисточку и координаты
// (Ширина прямоугольника в котором мы рисуем / 2) - (ширина текста / 2)
// (Высота прямоугольника в котором мы рисуем / 2) - (высота текста /2)
// В конечном счете дадут нам X, Y по по которым мы нарисуем наш текст и будет
// Это точный центр для текста с данными размерами
g.DrawString(CustomTitleText, CustomTitleFont, Brushes.Black,
new PointF(
pTitleBarCenter.Width / 2 - CustomTitleTextSize.Width / 2,
pTitleBarCenter.Height / 2 - CustomTitleTextSize.Height / 2));

// Возвращаем сглаживание на обычное
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
}

private void DrawBorder(Graphics g)
{
// Рисуем прямоугольник, он же серая рамка вокруг формы
g.DrawRectangle(
Pens.DimGray,
ClientRectangle.X, ClientRectangle.X,
ClientRectangle.Width - 1, ClientRectangle.Height - 1);
}

Собственно на этом код заканчивается :) Ниже приведен блок с полным кодом главной формы моего приложения:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace VisualTweak
{
public partial class Main : Form
{
// Свойство: для хранения заголовка окна
public String CustomTitleText = "Leopard Style Window © 2010 TBX1n";
// Свойство: для хранения шрифта заголовка окна
public Font CustomTitleFont = new Font("Tahoma", 10f, FontStyle.Bold);

// Свойство: флаг, зажата ли кнопка мыши, чтобы тащить окно
private bool movingWindow = false;
// Свойство: объект для хранения положения курсора до начала перемещения
private Point oldCursorPosition;

public Main()
{
InitializeComponent();

PrepairForm();

// Событие: рисование формы
this.Paint += new PaintEventHandler(Main_Paint);
// Событие: рисование центральной части Тайтла
pTitleBarCenter.Paint += new PaintEventHandler(pTitleBarCenter_Paint);
// Событие: зажатие кнопки мыши на центральной части Тайтла
pTitleBarCenter.MouseDown += new MouseEventHandler(pTitleBarCenter_MouseDown);
// Событие: отжатие кнопки мыши на центральной части Тайтла
pTitleBarCenter.MouseUp += new MouseEventHandler(pTitleBarCenter_MouseUp);
// Событие: перемещение курсора по центральной части Тайтла
pTitleBarCenter.MouseMove += new MouseEventHandler(pTitleBarCenter_MouseMove);
// Событие: попадание курсора в левую область Тайтла (с кнопками)
pTitleBarLeft.MouseEnter += new EventHandler(pTitleBarLeft_MouseEnter);
// Событие: выход курсора из левой части Тайтла
pTitleBarLeft.MouseLeave += new EventHandler(pTitleBarLeft_MouseLeave);
// Событие: клик по левой части Тайтла
pTitleBarLeft.MouseClick += new MouseEventHandler(pTitleBarLeft_MouseClick);
}

#region :: Methods ::
private void CommandButtonsClick(Point e)
{
// Проверка по высоте
if (e.Y > 1 && e.Y <= 23)
{
// Красная кнопка (Закрыть)
if (e.X > 1 && e.X <= 26)
{
this.Close();
}
// Оранжевая кнопка (Свернуть)
else if (e.X > 26 && e.X <= 47)
{
this.WindowState = FormWindowState.Minimized;
}
// Зеленая кнопка (Развернуть)
else if (e.X > 47 && e.X <= 65)
{
if (this.WindowState == FormWindowState.Maximized)
this.WindowState = FormWindowState.Normal;
else this.WindowState = FormWindowState.Maximized;

// Вызываем принудительную перерисовку окно,
// чтобы наши рамки формы отрисовались правильно
this.Invalidate();
}
}
}

private void PrepairForm()
{
// Устанавливаем стили окна
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, true);

// Устанавливаем стандартные фоновые изображения для Тайтла
pTitleBarLeft.BackgroundImage = global::VisualTweak.Properties.Resources.buttons_n ormal;
pTitleBarCenter.BackgroundImage = global::VisualTweak.Properties.Resources.bg;
pTitleBarRight.BackgroundImage = global::VisualTweak.Properties.Resources.right;
}

/// <summary>
/// Метод, который рисует текст в Тайте
/// </summary>
private void DrawTitle(Graphics g)
{
// Устанавливаем сглаживание на максимальное
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality ;

// Узнаем сколько место займет наш текст
// Это нам необходимо, чтобы определить в каком точно месте расположить его
SizeF CustomTitleTextSize = g.MeasureString(CustomTitleText, CustomTitleFont);

// Рисуем текст
// Передаем: текст, шрифт, кисточку и координаты
// (Ширина прямоугольника в котором мы рисуем / 2) - (ширина текста / 2)
// (Высота прямоугольника в котором мы рисуем / 2) - (высота текста /2)
// В конечном счете дадут нам X, Y по по которым мы нарисуем наш текст и будет
// Это точный центр для текста с данными размерами
g.DrawString(CustomTitleText, CustomTitleFont, Brushes.Black,
new PointF(
pTitleBarCenter.Width / 2 - CustomTitleTextSize.Width / 2,
pTitleBarCenter.Height / 2 - CustomTitleTextSize.Height / 2));

// Возвращаем сглаживание на обычное
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
}

private void DrawBorder(Graphics g)
{
// Рисуем прямоугольник, он же серая рамка вокруг формы
g.DrawRectangle(
Pens.DimGray,
ClientRectangle.X, ClientRectangle.X,
ClientRectangle.Width - 1, ClientRectangle.Height - 1);
}
#endregion

#region :: Actions ::
private void pTitleBarLeft_MouseClick(object sender, MouseEventArgs e)
{
CommandButtonsClick(e.Location);
}
#endregion

#region :: Move Window ::
private void pTitleBarCenter_MouseDown(object sender, MouseEventArgs e)
{
// Перед началом перемещения окно сохраняем текущие координаты курсора мышки
oldCursorPosition = new Point(e.X, e.Y);
// Устанавливаем флаг на Да
movingWindow = true;
}

private void pTitleBarCenter_MouseUp(object sender, MouseEventArgs e)
{
// Когда мы отпускаем кнопку мышки, устанавливаем флаг на Нет
movingWindow = false;
}

private void pTitleBarCenter_MouseMove(object sender, MouseEventArgs e)
{
//Если флаг имеет значение Да
if (movingWindow)
{
// Получаем текущее положение курсора
Point newCursorPosition = new Point(e.X, e.Y);
// Получаем разницу между текущим положением курсора и тем, что было
// до того как мы начали перемещать курсор
newCursorPosition.X = newCursorPosition.X - oldCursorPosition.X;
newCursorPosition.Y = newCursorPosition.Y - oldCursorPosition.Y;
// К текущему положению окна прибавляем разницу, чтобы получить сдвиг
// в нужные стороны
this.Location = new Point(this.Location.X + newCursorPosition.X,
this.Location.Y + newCursorPosition.Y);
}
}
#endregion

#region :: Draw Title Bar ::
private void pTitleBarLeft_MouseEnter(object sender, EventArgs e)
{
// Когда курсор мыши попадает в область левой части Тайтла, то мы меняем фон
// на другой (в нашем случаи это фон с нажатыми кнопками)
pTitleBarLeft.BackgroundImage = global::VisualTweak.Properties.Resources.buttons_h over;
}

private void pTitleBarLeft_MouseLeave(object sender, EventArgs e)
{
// Когда курсор мыши уходит из области левой части Тайтла, то мы возвращаем фон
// в обычное состояние
pTitleBarLeft.BackgroundImage = global::VisualTweak.Properties.Resources.buttons_n ormal;
}

private void pTitleBarCenter_Paint(object sender, PaintEventArgs e)
{
DrawTitle(e.Graphics);
}

private void Main_Paint(object sender, PaintEventArgs e)
{
DrawBorder(e.Graphics);
}
#endregion
}
}



Заключение:

После прочтения, вникания и практических занятий у вас должно получится приложение точно такой же как и на первом скриншоте.
Вы научитесь примитивной работе с GDI+ а так же получите много другого опыта разрабатывая нечно подобное.
Готовый проект на Visual Studio 2010 вы можете скачать из вложения.

Geni
01.09.2010, 16:49
да как открать эту прогу через которую вы делаете этот фейк не понятно:reddy:

Kitsune
01.09.2010, 16:53
Geni, здесь фейки никто не делает. Название статьи прочитайте сударь...

ultrazzz
03.02.2011, 18:04
Сложный гайд скрины хоть залейте =(

w1nsis
06.02.2011, 16:11
TBX1n сколько учился на программиста чтоб этому всему научиться?

Sashok7714444
22.01.2012, 00:26
Нормальный гайд! По ходу дела, + есть исходник..

Sinyss
22.01.2012, 03:13
TBX1n сколько учился на программиста чтоб этому всему научиться?
Учится на программиста... даже звучит стремно....

Sleepy_Master
22.01.2012, 18:15
Скрины не отображаются , не видно конечный результат , реализовать сам код проблем не составляет , но время в пустую тоже тратить не хочется.

[RO]jkpro
22.01.2012, 19:00
Скрины не отображаются , не видно конечный результат , реализовать сам код проблем не составляет , но время в пустую тоже тратить не хочется.

Да именно поэтому под примером находится исходник, там же и бинарник

(WinForms)

Для лохов же /dgs

Kitsune
25.01.2012, 22:59
jkpro;2396364"]Для лохов же
Высказывание "Windows для лохов" равносильно. WinForms такая же живая технология как и сотни других.

Pupsiks
26.04.2016, 21:48
Обновите пожалуйста скрины к уроку.

gre3nsis
08.05.2016, 03:19
Спасибо полезно! Adrenaline