الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

تطوير الفورم في الدوت نت باستخدام +gdi – الكلاس Skinner

بدأه a_pess في 9 مارس 2009 · 6 رد · 2,333 مشاهدة · في Microsoft Visual C#.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

تطوير الفورم في الدوت نت باستخدام +GDI – الكلاس Skinner

اللغة المستخدمة: الفيجوال C#

التطبيق: فيجوال استوديو 2005 و 2008

المستوي: التقييم متروك للقارئ

إعداد: مهندس / عمر أمين إبراهيم

مقدمة

من المؤكد أننا جميعا قد لاحظنا كيف يبدو شكل الفورم في الويندوز فيستا ولقد أتاحت شركة مايكروسوفت لمستخدمي نظام الفيستا إمكانية التعديل علي ألوان الفورم والمقصد هنا تحديدا هو التعديل في ألوان الجزء الذي يطلق عليه Non Client Area وكما تعلمون جميعا فإن هذه الخاصية ليست مدعومة أو ليست متاحة لمصممي البرامج من خلال الفيجوال دوت نت و بما أن مايكروسوفت لم تسمح بالتعديل علي ألوان Non Client Area من خلال الفيجوال استوديو لذا علينا أن نصمم ذلك بأن نضيف خصائص الي برامجنا تتيح لنا التغيير في شكل الفورم.

عموما أو بشكل عام كل ذلك يحدث باستخدام GDI+ ومن المؤكد أننا جميعا قد قرأنا عن GDI+ أو علي الأقل نعلم معني هذه الكلمة ولنعطي فكرة سريعة عن ماهية هذه الكلمة أو معناها

كلمة GDI+ هي نسخة محسنة أو نسخة مطورة من GDI التي تمثل الثلاثة الأحرف الأولي لكلمة Graphics Device Interface و GDI+ هي جزء من نظام التشغيل في الويندوز إكس بي Windows XP وهي المسئولة عن التعامل مع الرسم والصور وفي الدوت نت يعتبر System.Drawing Namespace هو المسئول عن إعطاؤنا الإمكانيات للدخول الي GDI+ والتعامل معها لذلك أفترض أن القارئ الذي سيتابع هذا الموضوع لدية دراية أو خلفية مناسبة عن كيفية التعامل مع +GDI بالإضافة الي ما يمكن أن نطلق عليه Dot Net Drawing Library وهي مجموعة من الكلاس الموجودة داخل System Drawing Namespace وهي التي سوف تساعدنا في أن نتعامل مع كثير من الأشياء مثل الرسم و الصور و الفونت و التكست و الألوان و الأيقونات و أيضا الأشكال الهندسية المختلفة الي أخره من هذه الأشياء. إذن كيف نغير شكل الفورم؟ أو كيف نصنع شيئا مماثلا للفيستا فورم ؟ هذا هو هدفنا هنا من هذا الموضوع وسنحاول الإجابة علي هذا السؤال من خلال طرح ومناقشة مجموعة من الأفكار التي ستساهم بشكل كبير في مساعدتنا علي تصميم الفورم

رسم الفورم

كما تعلمون فإن الرسم علي الفورم يحدث داخل الحدث Paint أو عن طريق عمل Override للحدث OnPaint أو للحدث OnPaintBackground وليس شرطا أن نستخدم أي من هذه الأحداث مباشرة ولكن أيضا يمكننا أن نرسم علي الفورم من أي مكان نريده داخل الكود و مجموعة الأمثلة التالية توضح بعض الأفكار عن كيفية الرسم داخل الفورم

المثال الأول: وهنا سوف نرسم مستطيل داخل الفورم ولتنفيذ المثال افتح مشروع جديد وفي داخل الحدث Paint الخاص بالفورم ضع الكود التالي


{
Rectangle rect = new Rectangle(0, 0, 100, 100);
SolidBrush sb = new SolidBrush(Color.Aqua);
e.Graphics.FillRectangle(sb, rect);
}
		 private void Form1_Paint(object sender, PaintEventArgs e)

ولنلقي نظرة علي الكود أعلاه وهو كود بسيط جدا حيث هنا نرسم مستطيل ما حيث عرض المستطيل = 100 وطول المستطيل يساوي 100 ونقطة بداية رسم المستطيل علي الفورم هي النقطة صفر وصفر وهي النقطة التي تقع في اعلي يسار الفورم ثم قمنا بتعريف فرشاة SolidBrush حيث سنستخدمها في تلوين المستطيل باللون المطلوب ثم رسمنا المستطيل داخل الفورم

المثال الثاني: وهنا سوف نقوم بتعريف مستطيلا واحدا ثم سنرسم هذا المستطيل في مكانان مختلفان داخل الفورم ولتنفيذ المثال افتح مشروع جديد وفي داخل الحدث Paint الخاص بالفورم ضع الكود التالي


private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle rect = new Rectangle(0, 0, 100, 100);
SolidBrush sb = new SolidBrush(Color.Aqua);
e.Graphics.FillRectangle(sb, rect);
e.Graphics.TranslateTransform(100, 100);
e.Graphics.FillRectangle(sb, rect);
e.Graphics.ResetTransform();
}

ولنلقي نظرة علي الكود أعلاه وهو أيضا كود بسيط جدا حيث هنا نرسم مستطيل ما حيث عرض المستطيل = 100 وطول المستطيل يساوي 100 ونقطة بداية رسم المستطيل علي الفورم وهي ما يمكن أن نطلق عليها نقطة الأصل وهي النقطة صفر وصفر ثم قمنا بتعريف فرشاة SolidBrush حيث سنستخدمها في تلوين المستطيل باللون المطلوب ثم رسمنا المستطيل داخل الفورم ثم بعد ذلك نقلنا نقطة الرسم علي الفورم الي النقطة 100 و 100 وذلك باستخدام TranslateTransform ثم رسمنا نفس المستطيل بنفس الأبعاد ولكن نقطة الرسم هنا هي النقطة 100 و 100ثم استخدمنا ResetTransform وذلك لكي نعود الي نقطة الأصل علي الفورم وهي النقطة صفر وصفر ولماذا نفعل ذلك لأننا لو حاولنا الاستمرار في الرسم بدون عمل ResetTransform فإن الرسم سيتم ولكن نقطة الصفر الجديدة ستكون هي النقطة 100 و 100

المثال الثالث: وهو صورة أخري للكود أعلاه ولكننا فقط رسمنا المستطيل مائلا بزاوية 30 وما أريد توضيحه هنا هو أننا عندما نستخدم RotateTransform أو TranslateTransform يجب أن لا ننسي أبدا أن نستخدم ResetTransform لكي نعود دائما بالرسم الي نقطة الأصل ولتنفيذ المثال افتح مشروع جديد وفي داخل الحدث Paint الخاص بالفورم ضع الكود التالي


private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle rect = new Rectangle(0, 0, 100, 100);
SolidBrush sb = new SolidBrush(Color.Aqua);
e.Graphics.FillRectangle(sb, rect);
e.Graphics.TranslateTransform(100, 100);
e.Graphics.RotateTransform(45);
e.Graphics.FillRectangle(sb, rect);
e.Graphics.ResetTransform();

}

المثال الرابع: وبما أننا هنا نتحدث هنا عن تطوير الفورم لذا لنكتب نفس الكود الموجود في المثال الثالث أعلاه بطريقة أخري بحيث نربط الكود مع أبعاد الفورم نفسه مثل عرض الفورم مثلا وأيضا بدلا من استخدام SolidBrush لنستخدم LinearGradientBrush ثم نرسم الفورم في نقطة الأصل وهي النقطة التي تقع في أقصي يسار أعلي الفورم ثم بعد ذلك و بدلا من استخدام TranslateTransform سنقوم بتعريف Matrix أخري لعكس اتجاه الرسم بحيث نجعل نقطة الصفر للرسم تبدأ من أقصي يسار أسفل الفورم ثم نرسم المستطيل مرة أخري ولتنفيذ المثال افتح مشروع جديد وفي داخل الحدث Paint الخاص بالفورم ضع الكود التالي


private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle rect = new Rectangle(0, 0, this.ClientRectangle .Width , 24);
LinearGradientBrush lgb = new LinearGradientBrush(rect, Color.Blue, Color.Aqua, 90, true);
Matrix m_matrix = new Matrix(1, 0, 0, -1, 0, this.ClientRectangle.Height);
e.Graphics.FillRectangle(lgb, rect);
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgb, rect);
e.Graphics.ResetTransform();
}

سنلاحظ أثناء تجربة الكود الموجود بالمثال أعلاه وتحديدا عند محاولة تكبير الفورم أو تغيير أبعاده أن الفورم لا يعيد رسم نفسه بالشكل الصحيح لذلك سنقوم بتعديل الكود أعلاه قليلا بحيث نجبر الفورم علي إعادة رسم نفسه مرة أخر عند تغيير أبعاده لذلك وفي الحدث Resize علينا أن نستخدم Invalidate وذلك لكي نجبر الفورم علي رسم نفسه بالشكل الصحيح والكود سيكون كالتالي


private void Form1_Resize(object sender, EventArgs e)
{
Invalidate();
}

ملحوظة هامة:

لو قمت عزيزي القارئ بتغيير قيمة Opacity للفورم وجعلتها مثلا 85 % ستكتشف شيئا طريفا جدا وهو أن الفورم وبالرغم من أنه يعيد رسم نفسه حيث أننا استخدمنا Invalidate في الحدث Resize ولكن عند تغيير قيمة Opacity ستلاحظ أن الفورم لا يرسم المستطيلات التي حددناها سابقا بالضبط وخصوصا عندما يكون في حالة Maximize لذلك ستلاحظ فرق ضئيل جدا في أقصي اليمين لا يتم رسمه تماما وأيضا ستلاحظ أن الرسم يهتز قليلا أي أن هناك ما يسمي Flickering أثناء محاول تغيير أبعاد الفورم ولكي نتخلص من هذه الأشياء البسيطة كل ما علينا أن نفعله هو تغيير قيمة الصفة DoubleBuffered وهي عبارة عن Boolean بأن نجعل قيمتها تساوي True

أيضا لو قمت عزيزي القارئ بإضافة كنترول ما مثل Panel علي سبيل المثال الي الفورم ولنجعل قيمة الصفة Dock لهذه Panel تساوي Fill ثم نقوم بتجربة نفس المثال أعلاه سنكتشف وبالرغم من أننا رسمنا داخل الفورم فإن Panel وهي في حالة Dock = Fill قد قامت بتغطية ما رسمناه داخل الفورم ولحل هذه المشكلة البسيطة كل ما عليك أن تفعله هو أن تتلاعب قليلا بقيمة الصفة Padding الخاصة بالفورم ويتم ذلك إما عن طريق التعامل مع الفورم في مرحلة Design أو أننا نكتب ذلك في الحدث Load الخاص بالفورم

المثال الخامس: لنعيد صياغة الكود الموجود بالمثال الرابع ونجعله أكثر فعالية بحيث نرسم الشكل النهائي للفورم ولتنفيذ المثال افتح مشروع جديد ثم اكتب الكود بالشكل التالي


private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle rectTop = new Rectangle(0, 0, this.ClientRectangle.Width, 24);
Rectangle rectBottom = new Rectangle(0, 0, this.ClientRectangle.Width, 5);
Rectangle rectLeft = new Rectangle(0, 5, 5, this.ClientRectangle .Height - 24);
Rectangle rectRight = new Rectangle(this.ClientRectangle.Width -5, 5, 5, this.ClientRectangle.Height - 24);
LinearGradientBrush lgb = new LinearGradientBrush(rectTop, Color.Maroon, Color.Red, 90, true);
LinearGradientBrush lgbt = new LinearGradientBrush(rectLeft, Color.Maroon, Color.Red, 90, true);
Matrix m_matrix = new Matrix(1, 0, 0, -1, 0, this.ClientRectangle.Height);
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbt, rectLeft);
e.Graphics.FillRectangle(lgbt, rectRight);
e.Graphics.ResetTransform();
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgb, rectBottom);
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(lgb, rectTop);
Icon ico = new Icon(this.Icon , 16, 16);
float mFontSize;
mFontSize = this.Font.Size;
mFontSize += 2.0F;
Font mfont = new Font(this.Font.Name, mFontSize, this.Font.Style, this.Font.Unit);
e.Graphics.DrawIcon(ico, rectTop.X +2, 4);
e.Graphics.DrawString(this.Text, mfont, new SolidBrush(Color.White), rectTop.X + 18, 4);
}

private void Form1_Resize(object sender, EventArgs e)
{
Invalidate();
}

private void Form1_Load(object sender, EventArgs e)
{
this.Padding = new System.Windows.Forms.Padding(5, 24, 5, 5);
this.DoubleBuffered = true;
this.Opacity = .85;
this.Left = (Screen.PrimaryScreen.WorkingArea.Width - this.Width) / 2;
this.Top = (Screen.PrimaryScreen.WorkingArea.Height - this.Height) / 2;
this.FormBorderStyle = FormBorderStyle.None;
}

اعتقد أن الكود اعلاه واضح فما هو إلا عبارة عن تجميع أفكار للكود التي كتباه في المثال الأول حتى المثال الرابع وعلي العموم الخطوات التالية نوضح سريعا فكرة الكود بالمثال الخامس

في الحدث Paint

تم تعريف مجموعة من المتغيرات وهي عبارة عن أربعة مستطيلات وهم علي التوالي rectTop و rectBottom و rectLeft و rectRight وتم ربط أبعاد هذه المستطيلات بأبعاد الفورم أو تحديدا مع المستطيل الذي يعبر عن أبعاد الفورم وهو ما يطلق عليه اسم ClientRectangle

تم تعريف متغيران عبارة عن فرشاة ألوان وهما من النوع LinearGradientBrush وتم استخدامهم في تلوين المستطيلات الأربعة

تم تعريف Matrix حيث ستستخدم في نقل نقطة الأصل للرسم لكي نرسم بعض المستطيلات في أماكنها علي الفورم

تم رسم المستطيلات الأربعة علي الفورم

وأخيرا تم رسم أيقونة الفورم وأيضا الفورم تكست

في الحدث Resize

تم استخدام Invalidate لكي نجبر الفورم علي إعادة رسم نفسه

في الحدث Load

تم تحديد بعض الصفات الخاصة بالفورم وتم تحديد قيمتها مثل DoubleBuffered و Opacity وأيضا مكان الفورم ليكون في وسط شاشة الكمبيوتر تحديدا وذلك عن طريق استخدام Me.Left و Me.Top ثم أخيرا قمنا بإلغاء FormBorder

لربما أن الفورم حتى هذه المرحلة قد يفي باحتياجات بعض القراء ولكن لنفكر قليلا ماذا نريد أيضا من الفورم لكي يظهر بشكل احترافي ولو قليلا اعتقد مبدئيا أننا نريد أن نغير Form Edges أو أطراف الفورم ونجعلها تأخذ شكل القوس ولو قليلا وأيضا يجب أن نعطي المستخدم إمكانية تحريك الفورم علي الشاشة وأيضا يجب أن نعطي المستخدم إمكانية إغلاق الفورم أو أيضا إمكانية وضع الفورم في Task Bar عندما يكون في الحالة Minimized مثلا ولربما أيضا نريد تتغير ألوان الفورم عندما يكون الماوس خارج حدود الفورم أو في حالة أن المستخدم قد ترك الفورم واستخدم برنامج أخر أعتقد انه مازال هناك الكثير من الأشياء التي يجب إضافتها الي الكود أعلاه لكي نجعل شكل الفورم أكثر إحترافية ولكن قبل أن نفعل كل ذلك لنسأل أنفسنا سؤالا هاما جدا وهو هل يجب علينا في كل مرة نصمم برنامجا ما أن نكتب مثل هذا الكود مع كل فورم ؟ من المؤكد أن ذلك ليس منطقيا والأفضل لنا أن نبني كلاس ما ثم يمكننا أن نضيفه الي أي مشروع بعد ذلك كنوع من Reference وهنا سيجب علينا فقط أن نكتب القليل جدا من الكود مع كل فورم وهذا بالتأكيد سيوفر علينا الكثير من الجهد والوقت. أو من الممكن أن نبني هذا الكلاس علي شكل Component يتم إضافتها الي الفورم وهذا بالتأكيد سيكون عمليا أكثر حيث هنا يمكن إضافته الي الفيجوال استوديو Toolbox ومن ثم يمكن أن نستخدمه وقتما نريد مع أي فورم

والأن لنبني معا مثل هذا الكلاس حيث سيكون هو النواة الأساسية التي من الممكن التطوير بها حينما نريد وأيضا يمكن استخدامه بشكل عام في تغيير شكل الفورم وسوف أطلق علي هذا الكلاس إسم Skinner مثلا أو يمكنك أنت عزيزي القارئ أن تختار أي اسم تريده للكلاس

الكلاس ٍSkinner

لو نظرنا الي الكود الموجود بالمثال الخامس لربما نستطيع أن نحدد بشكل مبدئي مجموعة المتغيرات والعناصر التي نحتاجها لبناء الكلاس وعلي سبيل المثال ربما نحتاج أن نضيف الي الكلاس بعض المتغيرات الخاصة بالألوان و الفونت و التكست ومتغير يعبر عن الفورم و بعض Read-only Property التي من الممكن استخدامها لاسترجاع بيانات من الفورم وأيضا بعض Public Property أيضا نحتاج أن نضيف Constructor الي الكلاس وربما نحتاج أن نضيف أيضا بعض Handlers أو Delegates أو نضيف ما نراه مناسبا لكي يؤدي الكلاس الغرض المطلوب منه ألا وهو تغيير شكل الفورم

لتصميم الكلاس لنفتح مشروع جديد ونختار Class Library ونعطي المشروع اسم Skinner ثم نقوم بإضافة بعض References الي المشروع ويتم ذلك من قائمة Project ثم نختار Add Reference ثم نضيف System.Windows.Forms وأيضا نضيف System.Drawing وكما تعلمون بمجرد فتح المشروع سيقوم الفيجوال استوديو بإضافة كلاس الي المشروع ويكون الاسم الافتراضي أو المبدئي هو Public Class Class1 نقوم بتغيير الاسم الافتراضي الي أي اسم نريده وعموما لقد قمت بتغيير الاسم الي Public Class CairoSkin ثم نبدأ في التعامل مع الكلاس عن طريق إضافة ما نريد من الكود داخل الكلاس ويمكن تقسيم الكود المبدئي الذي يمكن إضافته للكلاس الي خمسة عناصر رئيسية وهي ستكون كالتالي

إضافة المتغيرات

في الكود التالي تم تعريف مجموعة من المتغيرات والثوابت حيث سيتم استخدامها في رسم وتلوين الفورم

أيضا تم تعريف متغير يعبر عن الفورم حيث سيتم تمرير هذا المتغير داخل Constructor الخاص بالكلاس لكي نربط الكلاس مع أي فورم نريده


private int FrameThickness = 5;
private int CaptionHeight = 26;
private Font m_Font = new Font("Arial", 9f, System.Drawing.FontStyle.Bold);
private Color m_DarkColor = Color.FromArgb(255, 51, 102, 204);
private Color m_LightColor = Color.FromArgb(255, 0, 245, 255);
private Color m_TextColor = Color.Black;
private Form m_Parent;
private string m_Developer = "Omar Amin Ibrahim";

إضافة Constructor

في الكود التالي تم إضافة Constructor للكلاس وتم ربطه بالفورم حيث يتم هنا تمرير أي فورم الي الكلاس ومن داخل هذا Constructor تم التعامل مع صفات الفورم حيث نقوم بالتعديل علي بعض هذه الصفات وأيضا هنا يمكننا أن نضيف أي Handlers نحتاج إليها


public CairoSkin()
{

}

public CairoSkin(Form m_form)
{

m_Parent = m_form;
m_Parent.Opacity = 0.85;
m_Parent.FormBorderStyle = FormBorderStyle.None;
m_Parent.Padding = new System.Windows.Forms.Padding(FrameThickness, CaptionHeight, FrameThickness, FrameThickness);
m_Parent.Left = (Screen.PrimaryScreen.WorkingArea.Width - m_Parent.Width) / 2;
m_Parent.Top = (Screen.PrimaryScreen.WorkingArea.Height - m_Parent.Height) / 2;

m_form.Paint += OnPaint;
m_form.Resize += OnResize;

}

إضافة Property

في الكود التالي يتم إضافة ما نريده من Property الي الكلاس حيث يقوم بعضها وتحديدا Readonly Property باسترجاع بيانات عن الفورم وبعضها الأخر وتحديدا Public Property يتيح لمستخدم الكلاس التحكم في بعض الألوان المستخدمة في رسم الفورم


public Color TextColor
{
get { return m_TextColor; }

set
{
if ((value != (m_TextColor)))
{

m_TextColor = value;
}
m_Parent.Invalidate();
}

}

public Color LightColor
{
get { return m_LightColor; }
set
{
if ((value != (m_LightColor)))
{

m_LightColor = value;
}

m_Parent.Invalidate();
}
}

public Color DarkColor
{
get { return m_DarkColor; }
set
{
if ((value != (m_DarkColor)))
{

m_DarkColor = value;
}

m_Parent.Invalidate();
}
}

protected string Text
{
get { return m_Parent.Text; }
}

protected Rectangle ClientRectangle
{
get { return m_Parent.ClientRectangle; }
}

protected string Developer
{
get { return m_Developer; }
}

protected Form Parent
{
get { return m_Parent; }
}

إضافة Methods

هنا يتم كتابة أي الكود الخاص بجميع Handlers التي تم إضافتها الي Constructor


protected virtual void OnPaint(object sender, PaintEventArgs e)
{
Rectangle rectTop = new Rectangle(0, 0, this.ClientRectangle.Width, 24);
Rectangle rectBottom = new Rectangle(0, 0, this.ClientRectangle.Width, 5);
Rectangle rectLeft = new Rectangle(0, 5, 5, this.ClientRectangle.Height - 24);
Rectangle rectRight = new Rectangle(this.ClientRectangle.Width - 5, 5, 5, this.ClientRectangle.Height - 24);
LinearGradientBrush lgb = new LinearGradientBrush(rectTop, this.m_LightColor, this.m_DarkColor, 90, true);
LinearGradientBrush lgbt = new LinearGradientBrush(rectLeft, this.m_LightColor, this.m_DarkColor, 90, true);
Matrix m_matrix = new Matrix(1, 0, 0, -1, 0, this.ClientRectangle.Height);
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbt, rectLeft);
e.Graphics.FillRectangle(lgbt, rectRight);
e.Graphics.ResetTransform();
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgb, rectBottom);
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(lgb, rectTop);
Icon ico = new Icon(m_Parent.Icon, 16, 16);
e.Graphics.DrawIcon(ico, rectTop.X + 2, 4);
e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColor), rectTop.X + 18, 4);
}

protected virtual void OnResize(object sender, EventArgs e)
{
m_Parent.Invalidate();

}

إضافة Functions

هنا تم عمل Override للدالة ToString لنسترجع اسم مصمم الكلاس


public override string ToString()
{
return m_Developer;
}

بعد الانتهاء من كتابة الكود فقط علينا أن نقوم بعمل Build للكلاس وسنجد نسخة من الكلاس Skinner.dll موجودة تحديدا تحت الفهرس Debug الموجود تحت فهرس المشروع

كيف نستخدم الكلاس Skinner

لكي تستخدم الكلاس عليك أن تفتح مشروع عادي به فورم ثم تضيف هذا الكلاس الي المشروع عن طريق Add Reference من قائمة Project ثم تقوم بكتابة Import Skinner وهذا بلغة الفيجوال بيسك أو استخدام using Skinner; كما في لغة C# والمثال التالي يوضح كيف نستخدم الكلاس حيث كل ما علينا أن نفعله هو أن نقوم بتعريف متغير يعبر عن الكلاس Skinner ثم نربطه بالفورم في الحدث Load وكما تلاحظون أننا قد وفررنا كثيرا من الوقت والجهد في تكرار كتابة الكود لأننا نستطيع أن نستخدم الكلاس Skinner مع أي فورم وبنفس الأسلوب الذي اتبعناه بالمثال التالي.

حاول عزيزي القارئ أن تضيف أي عدد تريده من الفورم الي المشروع واستخدم نفس عدد الأسطر من الكود أدناه مع كل فورم تضيفه الي المشروع وشاهد ما سيحدث


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

namespace SkinTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private CairoSkin skin;

private void Form1_Load(object sender, EventArgs e)
{
// لو استخدمنا فقط السطر التالي مباشرة
//من الكود بدون تغيير الألوان
//فإن الفورم سيأخذ الألوان التي حددناها دخل الكلاس
skin = new CairoSkin(this);
// هنا نقوم بتغيير ألوان الفورم لو أردنا ذلك
skin.DarkColor = Color.Maroon;
skin.LieghtColor = Color.Red;
skin.TextColor = Color.White;
}
}
}

يتبع في المشاركة التالية......................

Example1.rar

Example2.rar

Example3.rar

Example4.rar

Example5.rar

#2

تغيير شكل الفورم

كما سبق وأوضحنا أن الفورم أو الكلاس Skinner حتى هذه المرحلة قد يفي باحتياجات بعض القراء ولكن أعتقد أنه مازال هناك الكثير من الأشياء التي من الممكن أن نضيفها للفورم لكي يصبح أكثر إحترافية ومن هذه الأشياء تغيير شكل الفورم

مبدئيا الكلاس المسئول عن تغيير شكل الفورم الي أي شكل نريده هو الكلاس Region وغالبا ما يتم استخدام هذا الكلاس مع Rectangle Structure أو RectangleF Structure أو مع GraphicsPath Class أو مع RegionData Class لتعريف الشكل الذي نريده ثم نمرره الي الكلاس Region فيأخذ الفورم الشكل المطلوب وعموما ما يهمنا حاليا هو التركيز علي استخدام GraphicsPath والأمثلة التالية توضح بعض الأفكار عن كيفية تغيير شكل الفورم

المثال السادس: في هذا المثال سنغير شكل الفورم الي أي شكل نريده عن طريق استخدام GraphicsPath ثم نمرره الي Form Region ونري ما سيحدث ولتنفيذ المثال افتح مشروع جديد ثم اكتب الكود بالشكل التالي


{
this.FormBorderStyle = FormBorderStyle.None;
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath Path = new GraphicsPath();

Path.AddArc(21, 21, 21, 21, 180, 90);
Path.AddArc(this.ClientRectangle.Width - 42, 21, 21, 21, 270, 90);
Path.AddArc(this.ClientRectangle.Width - 42, this.ClientRectangle.Height - 42, 21, 21, 360, 90);
Path.AddArc(21, this.ClientRectangle.Height - 42, 21, 21, 90, 90);

Path.CloseFigure();

Path.AddEllipse(new Rectangle(0, 0, 22, 22));
Path.AddEllipse(new Rectangle(this.ClientRectangle.Width - 22, 0, 22, 22));
Path.AddEllipse(new Rectangle(0, this.ClientRectangle.Height - 22, 22, 22));
Path.AddEllipse(new Rectangle(this.ClientRectangle.Width - 22, this.ClientRectangle.Height - 22, 22, 22));

Path.AddArc(21, 0, 21, 21, 180, 90);
Path.AddArc(this.ClientRectangle.Width - 42, 0, 21, 21, 270, 90);
Path.AddArc(this.ClientRectangle.Width - 42, 0, 21, 21, 360, 90);
Path.AddArc(21, 0, 21, 21, 90, 90);

Path.AddArc(21, this.ClientRectangle.Height - 21, 21, 21, 180, 90);
Path.AddArc(this.ClientRectangle.Width - 42, this.ClientRectangle.Height - 21, 21, 21, 270, 90);
Path.AddArc(this.ClientRectangle.Width - 42, this.ClientRectangle.Height - 21, 21, 21, 360, 90);
Path.AddArc(21, this.ClientRectangle.Height - 21, 21, 21, 90, 90);

this.Region = new Region(Path);
LinearGradientBrush lgb = new LinearGradientBrush(this.ClientRectangle, Color.Maroon, Color.Red, 90, true);
e.Graphics.FillRegion(lgb, this.Region);


e.Graphics.FillRectangle(Brushes.Aqua, new Rectangle(0, 0, 22, 22));
e.Graphics.FillRectangle(Brushes.Aqua, new Rectangle(this.ClientRectangle.Width - 22, 0, 22, 22));
e.Graphics.FillRectangle(Brushes.Aqua, new Rectangle(21, 0, this.ClientRectangle.Width - 42, 21));

e.Graphics.FillRectangle(Brushes.Aqua, new Rectangle(0, this.ClientRectangle.Height - 22, 22, 22));
e.Graphics.FillRectangle(Brushes.Aqua, new Rectangle(this.ClientRectangle.Width - 22, this.ClientRectangle.Height - 22, 22, 22));
e.Graphics.FillRectangle(Brushes.Aqua, new Rectangle(21, this.ClientRectangle.Height - 21, this.ClientRectangle.Width - 42, 21));
}
		private void Form1_Load(object sender, EventArgs e)

المثال السابع: في هذا المثال سنغير شكل الفورم أيضا عن طريق استخدام GraphicsPath ثم نمرره الي Form Region ونري ما سيحدث ولتنفيذ المثال افتح مشروع جديد ثم اكتب الكود بالشكل التالي


{
this.FormBorderStyle = FormBorderStyle.None;
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath Path = new GraphicsPath();
Path.AddArc(0, 50, 100, 100, 180, 90);
Path.AddArc(this.ClientRectangle.Width - 100, 50, 100, 100, 270, 90);
Path.AddArc(this.ClientRectangle.Width - 100, this.ClientRectangle.Height - 100, 100, 100, 360, 90);
Path.AddArc(0, this.ClientRectangle.Height - 100, 100, 100, 90, 90);
this.Region = new Region(Path);
e.Graphics.FillRegion(Brushes.Aqua, this.Region);
}
		private void Form1_Load(object sender, EventArgs e)

التحكم في نقل الفورم علي شاشة الكمبيوتر

تحريك الفورم Moving Form علي شاشة الكمبيوتر باستخدام الماوس من الممكن أن تتم بأكثر من أسلوب والفكرة ببساطة أن كل من Rectangle Structure و GraphicsPath Class و أيضا Region Class يمتلك بعض الطرق Methods التي لو استخدمت جيدا مع Mouse Events الأحداث الخاصة بالماوس سيمكننا التحكم بحركة الفورم علي شاشة الكمبيوتر بسهولة وهذه الطرق هي

Rectangle.Contains

GraphicsPath.IsVisible

Region.IsVisible

ولكي نعطي المستخدم ألأفضلية لكي يستطيع نقل الفورم الي أي مكان يريده علي شاشة الكمبيوتر علينا أن نستخدم الطرق المذكورة أعلاه مع أحداث الماوس والأمثلة التالية توضح كيفية استخدام بعض هذه الطرق

المثال الثامن: في هذا المثال سنقوم بتحريك الفورم اعتمادا علي Form Region ولتنفيذ المثال افتح مشروع جديد ثم اكتب الكود بالشكل التالي


private Point isPoint = new Point();

private void Form1_Load(object sender, EventArgs e)
{
this.FormBorderStyle = FormBorderStyle.None;
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath Path = new GraphicsPath();
Path.AddArc(0, 50, 100, 100, 180, 90);
Path.AddArc(this.ClientRectangle.Width - 100, 50, 100, 100, 270, 90);
Path.AddArc(this.ClientRectangle.Width - 100, this.ClientRectangle.Height - 100, 100, 100, 360, 90);
Path.AddArc(0, this.ClientRectangle.Height - 100, 100, 100, 90, 90);
this.Region = new Region(Path);
e.Graphics.FillRegion(Brushes.Aqua, this.Region);
}

private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (this.Region .IsVisible(e.Location))
{
isMove = true;
isPoint.X = e.X;
isPoint.Y = e.Y;
}
}

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (isMove)
{
isMove = false;
}
}

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if ((MouseButtons.Left & e.Button) == MouseButtons.Left)
{
if (isMove)

{
int x = this.Location.X + (e.X - isPoint.X);
int y = this.Location.Y + (e.Y - isPoint.Y);
this.Location = new Point(x, y);
}
}
}
		private bool isMove = false;

المثال التاسع: في هذا المثال سنقوم بتحريك الفورم اعتمادا علي Rectangle ولتنفيذ المثال افتح مشروع جديد ثم اكتب الكود بالشكل التالي


private Point isPoint = new Point();
private Rectangle rect;

private void Form1_Paint(object sender, PaintEventArgs e)
{
rect = new Rectangle(0, 0, 100, 100);
SolidBrush sb = new SolidBrush(Color.Aqua);
e.Graphics.FillRectangle(sb, rect);

}

private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (rect.Contains(e.Location))
{
isMove = true;
isPoint.X = e.X;
isPoint.Y = e.Y;
}

}

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (isMove)
{
isMove = false;
}

}

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if ((MouseButtons.Left & e.Button) == MouseButtons.Left)
{
if (isMove)
{
int x = this.Location.X + (e.X - isPoint.X);
int y = this.Location.Y + (e.Y - isPoint.Y);
this.Location = new Point(x, y);
}
}

}
		private bool isMove = false;

المثال العاشر: في هذا المثال سنقوم بتحريك الفورم اعتمادا علي GraphicsPath وكما تلاحظون فإن الفكرة واحدة تقريبا في كما في المثالين السابقين ولتنفيذ المثال افتح مشروع جديد ثم اكتب الكود بالشكل التالي


private Point isPoint = new Point();
private GraphicsPath path;

private void Form1_Paint(object sender, PaintEventArgs e)
{
path = new GraphicsPath();
path.AddEllipse(0, 0, 200, 200);
e.Graphics.FillPath(Brushes.Aqua, path);

}

private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (path.IsVisible(e.Location))
{
isMove = true;
isPoint.X = e.X;
isPoint.Y = e.Y;
}
}

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (isMove)
{
isMove = false;
}
}

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if ((MouseButtons.Left & e.Button) == MouseButtons.Left)
{
if (isMove)
{
int x = this.Location.X + (e.X - isPoint.X);
int y = this.Location.Y + (e.Y - isPoint.Y);
this.Location = new Point(x, y);
}
}
}
		private bool isMove = false;

يتبع في المشاركة التالية......................

Example6.rar

Example7.rar

Example8.rar

Example9.rar

Example10.rar

#3

تغيير ألوان الفورم

عندما يكون الفورم في حالة Active فإنه يحتفظ بألوانه العادية ولكن عندما يكون الفورم في الحالة Inactive فإنه يأخذ ألوانا أخري وهذا يمكن أن نعتبره كنوع من التنبيه للمستخدم User بأن الفورم مستخدما أو ليس مستخدما. أيضا نفس الحالة تتم إذا قام المستخدم باختيار نافذة أخري لبرنامج أخر مثلا. كيف نفعل ذلك في الفورم الذي نرسمه؟

المثال الحادي عشر: في هذا المثال سنقوم بتغيير ألوان مستطيل مرسوم داخل الفورم عندما لا يكون الفورم مستخدما ولتنفيذ المثال افتح مشروع جديد ثم اكتب الكود بالشكل التالي


private const int WM_ACTIVATEAPP = 0x001C;

private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle rect = new Rectangle(0, 0, 100, 100);
SolidBrush sbA = new SolidBrush(Color.Aqua);
SolidBrush sb = new SolidBrush(Color.Gray);

if (isActive)
{
e.Graphics.FillRectangle(sbA, rect);
}
else
{
e.Graphics.FillRectangle(sb, rect);
}


}

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_ACTIVATEAPP:

isActive = (((int)m.WParam != 0));
this.Invalidate();
break;
}

base.WndProc(ref m);
}

private void Form1_VisibleChanged(object sender, EventArgs e)
{
if (Visible)
{
isActive = true;
}
else
{
isActive = false;
}
}
		private bool isActive = true;

أعتقد أننا بحاجة الأن الي أن نقوم بإعادة صياغة الكود الخاص بالكلاس Skinner بحيث نضيف له بعضا من الأفكار التي استخدمناها في المثال السادس الي المثال الحادي عشر ولكن هناك ملحوظة هامة وهي تحديدا تخص المثال الحادي عشر حيث يجب أن نضع في الحسبان شيئا هاما جدا ألا وهو أننا عندما نحاول أن نضيف Handler للتعامل مع WndProc فلن نستطيع أن نفعل ذلك بالطريقة التقليدية ولو نظرنا الي الكود المبدئي للكلاس Skinner أعلاه فإننا سنلاحظ أنه وعندما كنا نريد إضافة أي Handler الي الكلاس فإن ذلك كان يتم في Constructor الخاص بالكلاس كما هو موضح بالكود التالي


m_form.Resize += OnResize;
			m_form.Paint += OnPaint;

إذن كيف نقوم بإضافة WndProc الي الكلاس CairoSkinner . في واقع الأمر إن ذلك يتم باستخدام الوراثة لواحد من الكلاسات الهامة جدا وهو NativeWindow Class حيث هنا يجب علينا أن نضيف كلاسا جديدا للكلاس Skinner ولقد أطلقت علي هذا الكلاس اسم WindowListener ثم نستخدم الوراثة للكلاس NativeWindow وهنا يمكننا أن نقوم بعمل Override الي WndProc ثم من داخل Constructor الخاص بالكلاس CairoSkinner نقوم بتعريف متغير يعبر عن الكلاس WindowListener ثم نستخدم هنا AssignHandle ونربطه مع الفورم

والكود التالي يوضح الصورة النهائية للكلاس CairoSkinner بعد إضافة التعديلات المطلوبة إليه حيث علينا أن نقوم بعمل rebuild للكلاس وبالتالي يمكن أن نستخدمه مع أي مشروع أخر كيفما نشاء.

وفي المرفقات ستجدون النسخة الكاملة للكلاس Skinner وهي عبارة عن ملف dll بالإضافة الي السورس كود

حيث ستجدون به نسختان لتغيير شكل الفورم وهما الكلاس CairoSkin والكود الخاص به مكتوب أدناه و الكلاس CairoSkinPlus والكود الخاص به مكتوب أدناه أيضا والفارق بينهما هو أن الكلاس CairoskinPlus به باتون لإغلاق الفورم وأيضا باتون لتصغير الفورم

الكلاس CairoSkin


using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Skinner
{
public class CairoSkin
{
#region Variables

private int FrameThickness = 5;
private int CaptionHeight = 29;
private Font m_Font = new Font("Arial", 10f, System.Drawing.FontStyle.Bold);
private Color m_DarkColor = Color.FromArgb(255, 51, 102, 204);
private Color m_LightColor = Color.FromArgb(255, 0, 245, 255);
private Color m_TextColor = Color.Black;

private Color m_DarkColorDeAct = Color.FromArgb(255, Color.Silver);
private Color m_LightColorDeAct = Color.FromArgb(255, Color.White);
private Color m_TextColorDeAct = Color.Gray;

private string m_Developer = "Omar Amin Ibrahim";

private bool isMove = false;
private Point isPoint = new Point();
private Rectangle rectMove;

public static bool isActive = true;
public static Form m_Parent;

#endregion // Variables

#region Constructor

public CairoSkin()
{

}

public CairoSkin(Form m_form)
{

m_Parent = m_form;
m_Parent.FormBorderStyle = FormBorderStyle.None;
m_Parent.Region = SetWindow();
m_Parent.Opacity = 0.85;
m_Parent.Padding = new System.Windows.Forms.Padding(FrameThickness, CaptionHeight, FrameThickness , FrameThickness);

m_form.Paint += OnPaint;
m_form.Resize += OnResize;
m_form.MouseDown += OnMouseDown;
m_form.MouseUp += OnMouseUp;
m_form.MouseMove += OnMouseMove;
m_form.VisibleChanged += OnVisibleChanged;

WindowListener wndlisten = new WindowListener();
wndlisten.AssignHandle(this.Handle);
}

#endregion // Constructor

#region Property

public Color TextColor
{
get { return m_TextColor; }

set
{
if ((value != (m_TextColor)))
{

m_TextColor = value;
}
m_Parent.Invalidate();
}

}

public Color LightColor
{
get { return m_LightColor; }
set
{
if ((value != (m_LightColor)))
{

m_LightColor = value;
}

m_Parent.Invalidate();
}
}

public Color DarkColor
{
get { return m_DarkColor; }
set
{
if ((value != (m_DarkColor)))
{

m_DarkColor = value;
}

m_Parent.Invalidate();
}
}

protected string Text
{
get { return m_Parent.Text; }
}

protected IntPtr Handle
{
get { return m_Parent.Handle; }
}

protected Rectangle ClientRectangle
{
get { return m_Parent.ClientRectangle; }
}

protected string Developer
{
get { return m_Developer; }
}

protected Form Parent
{
get { return m_Parent; }
}

#endregion // Property

#region Method

protected virtual void OnPaint(object sender, PaintEventArgs e)
{
Rectangle rectTop = new Rectangle(0, 0, this.ClientRectangle.Width, CaptionHeight);
Rectangle rectBottom = new Rectangle(0, 0, this.ClientRectangle.Width, FrameThickness);
Rectangle rectLeft = new Rectangle(0, FrameThickness, FrameThickness, this.ClientRectangle.Height - CaptionHeight);
Rectangle rectRight = new Rectangle(this.ClientRectangle.Width - FrameThickness , FrameThickness, FrameThickness , this.ClientRectangle.Height - CaptionHeight);
LinearGradientBrush lgb = new LinearGradientBrush(rectTop, this.m_LightColor, this.m_DarkColor, 90, true);
LinearGradientBrush lgbt = new LinearGradientBrush(rectLeft, this.m_LightColor, this.m_DarkColor, 90, true);

LinearGradientBrush lgbDeAct = new LinearGradientBrush(rectTop, this.m_LightColorDeAct, this.m_DarkColorDeAct, 90, true);
LinearGradientBrush lgbDeActt = new LinearGradientBrush(rectLeft, this.m_LightColorDeAct, this.m_DarkColorDeAct, 90, true);
Matrix m_matrix = new Matrix(1, 0, 0, -1, 0, this.ClientRectangle.Height);
rectMove = new Rectangle(0, 0, this.ClientRectangle.Width, CaptionHeight);
SizeF m_size = e.Graphics.MeasureString(this.Text, m_Font);

if (isActive)
{
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbt, rectLeft);
e.Graphics.FillRectangle(lgbt, rectRight);
e.Graphics.ResetTransform();
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgb, rectBottom);
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(lgb, rectTop);
Icon ico = new Icon(m_Parent.Icon, 16, 16);
e.Graphics.DrawIcon(ico, rectTop.X + 2, (CaptionHeight - ico.Height) / 2);
//e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColor), rectTop.X + 18, 4);
e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColor), rectTop.X + ico.Width + 2, (CaptionHeight - m_size.Height) / 2);

}
else
{
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbDeActt, rectLeft);
e.Graphics.FillRectangle(lgbDeActt, rectRight);
e.Graphics.ResetTransform();
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbDeAct, rectBottom);
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(lgbDeAct, rectTop);
Icon ico = new Icon(m_Parent.Icon, 16, 16);
e.Graphics.DrawIcon(ico, rectTop.X + 2, (CaptionHeight - ico.Height) / 2);
//e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColorDeAct), rectTop.X + 18, 4);
e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColorDeAct), rectTop.X + ico.Width + 2, (CaptionHeight - m_size.Height) / 2);

}

e.Graphics.DrawRectangle(new Pen(Color.Empty), rectMove);

}

protected virtual void OnResize(object sender, EventArgs e)
{
m_Parent.Invalidate();
}

protected virtual void OnMouseMove(object sender, MouseEventArgs e)
{
if ((MouseButtons.Left & e.Button) == MouseButtons.Left)
{
if (isMove)
{
int x = m_Parent.Location.X + (e.X - isPoint.X);
int y = m_Parent.Location.Y + (e.Y - isPoint.Y);
m_Parent.Location = new Point(x, y);
}
}

}

protected virtual void OnMouseUp(object sender, MouseEventArgs e)
{
if (isMove)
{
isMove = false;
}

}

protected virtual void OnMouseDown(object sender, MouseEventArgs e)
{
if (rectMove .Contains (e.Location ))
{
isMove = true;
isPoint.X = e.X;
isPoint.Y = e.Y;
}

}

protected virtual void OnVisibleChanged(object sender, EventArgs e)
{
if (m_Parent .Visible)
{
isActive = true;
}
else
{
isActive = false;
}
}

#endregion // Method

#region Function

public override string ToString()
{
return this.Developer;
}

private Region SetWindow()
{
Region functionReturnValue = default(Region);

GraphicsPath Path = new GraphicsPath();
Path.AddArc(0, 0, 10, 10, 180, 90);
Path.AddArc(this.ClientRectangle.Width - 10, 0, 10, 10, 270, 90);
Path.AddLine(this.ClientRectangle.Width, 10, this.ClientRectangle.Width , this.ClientRectangle.Height );
Path.AddLine(this.ClientRectangle.Width , this.ClientRectangle.Height , 0, this.ClientRectangle.Height );
Path.CloseFigure();
Region rgn = new Region(Path);

functionReturnValue = rgn;
m_Parent.Invalidate();
return functionReturnValue;

}

#endregion // Function

#region Classes

internal class WindowListener : NativeWindow
{

private const int WM_ACTIVATEAPP = 0x1c;
public WindowListener()
{
}

[System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
switch ((m.Msg))
{
case WM_ACTIVATEAPP:
isActive = (m.WParam.ToInt32() != 0);
CairoSkin.m_Parent.Invalidate();
break;
}
base.WndProc(ref m);
}
}

#endregion // Classes
}
}
using System;

الكلاس CairoSkinPlus


using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Skinner
{
public class CairoSkinPlus
{

#region Variables

private int FrameThickness = 5;
private int CaptionHeight = 29;
private Font m_Font = new Font("Arial", 10f, System.Drawing.FontStyle.Bold);
private Color m_DarkColor = Color.FromArgb(255, 51, 102, 204);
private Color m_LightColor = Color.FromArgb(255, 0, 245, 255);
private Color m_TextColor = Color.Black;

private Color m_DarkColorDeAct = Color.FromArgb(255, Color.Silver);
private Color m_LightColorDeAct = Color.FromArgb(255, Color.White);
private Color m_TextColorDeAct = Color.Gray;

private string m_Developer = "Omar Amin Ibrahim";

private bool isMove = false;
private bool isClosing = false;
private bool isMinimizing = false;

private Point isPoint = new Point();
private Rectangle rectMove;
private Rectangle rectClose;
private Rectangle rectMin;

public static bool isActive = true;
public static Form m_Parent;

private Graphics g;

private Bitmap bmp_Close = new Bitmap(reso.Close);
private Bitmap bmp_CloseOver = new Bitmap(reso.CloseHot);
private Bitmap bmp_CloseDown = new Bitmap(reso.ClosePressed);

private Bitmap bmp_Min = new Bitmap(reso.Minimize);
private Bitmap bmp_MinOver = new Bitmap(reso.MinimizeHot);
private Bitmap bmp_MinDown = new Bitmap(reso.MinimizePressed);

#endregion // Variables

#region Constructor

public CairoSkinPlus()

{

}

public CairoSkinPlus (Form m_form)
{
m_Parent = m_form;
m_Parent.FormBorderStyle = FormBorderStyle.None;
m_Parent.Region = SetWindow();
m_Parent.Opacity = 0.85;
m_Parent.Padding = new System.Windows.Forms.Padding(FrameThickness, CaptionHeight, FrameThickness, FrameThickness);

m_form.Paint += OnPaint;
m_form.Resize += OnResize;
m_form.MouseDown += OnMouseDown;
m_form.MouseUp += OnMouseUp;
m_form.MouseMove += OnMouseMove;
m_form.VisibleChanged += OnVisibleChanged;

WindowListener wndlisten = new WindowListener();
wndlisten.AssignHandle(this.Handle);
m_Parent.Invalidate();

}

#endregion // Constructor

#region Property

public Color TextColor
{
get { return m_TextColor; }

set
{
if ((value != (m_TextColor)))
{

m_TextColor = value;
}
m_Parent.Invalidate();
}

}

public Color LightColor
{
get { return m_LightColor; }
set
{
if ((value != (m_LightColor)))
{

m_LightColor = value;
}

m_Parent.Invalidate();
}
}

public Color DarkColor
{
get { return m_DarkColor; }
set
{
if ((value != (m_DarkColor)))
{

m_DarkColor = value;
}

m_Parent.Invalidate();
}
}

protected string Text
{
get { return m_Parent.Text; }
}

protected IntPtr Handle
{
get { return m_Parent.Handle; }
}

protected Rectangle ClientRectangle
{
get { return m_Parent.ClientRectangle; }
}

protected string Developer
{
get { return m_Developer; }
}

protected Form Parent
{
get { return m_Parent; }
}

#endregion // Property

#region CaptionButton Method

private void DrawCloseButtonNormal()
{

Graphics g = Graphics.FromHwnd(m_Parent.Handle);
g.DrawImage(bmp_Close, rectClose);
g.Dispose();

}

private void DrawCloseButtonOver()
{

Graphics g = Graphics.FromHwnd(m_Parent.Handle);
g.DrawImage(bmp_CloseOver, rectClose);
g.Dispose();

}

private void DrawCloseButtonDown()
{

Graphics g = Graphics.FromHwnd(m_Parent.Handle);
g.DrawImage(bmp_CloseDown, rectClose);
g.Dispose();

}

private void DrawMinButtonNormal()
{

// Draw the image to the form
Graphics g = Graphics.FromHwnd(m_Parent.Handle);
g.DrawImage(bmp_Min, rectMin);
g.Dispose();


}

private void DrawMinButtonOver()
{

// Draw the image to the form
Graphics g = Graphics.FromHwnd(m_Parent.Handle);
g.DrawImage(bmp_MinOver, rectMin);
g.Dispose();


}

private void DrawMinButtonDown()
{

// Draw the image to the form
Graphics g = Graphics.FromHwnd(m_Parent.Handle);
g.DrawImage(bmp_MinDown, rectMin);
g.Dispose();


}

#endregion // CaptionButton Method

#region Method

protected virtual void OnPaint(object sender, PaintEventArgs e)
{
if (m_Parent.WindowState != FormWindowState.Minimized)
{
Rectangle rectTop = new Rectangle(0, 0, this.ClientRectangle.Width, CaptionHeight);
Rectangle rectBottom = new Rectangle(0, 0, this.ClientRectangle.Width, FrameThickness);
Rectangle rectLeft = new Rectangle(0, FrameThickness, FrameThickness, this.ClientRectangle.Height - CaptionHeight);
Rectangle rectRight = new Rectangle(this.ClientRectangle.Width - FrameThickness, FrameThickness, FrameThickness, this.ClientRectangle.Height - CaptionHeight);
LinearGradientBrush lgb = new LinearGradientBrush(rectTop, this.m_LightColor, this.m_DarkColor, 90, true);
LinearGradientBrush lgbt = new LinearGradientBrush(rectLeft, this.m_LightColor, this.m_DarkColor, 90, true);

LinearGradientBrush lgbDeAct = new LinearGradientBrush(rectTop, this.m_LightColorDeAct, this.m_DarkColorDeAct, 90, true);
LinearGradientBrush lgbDeActt = new LinearGradientBrush(rectLeft, this.m_LightColorDeAct, this.m_DarkColorDeAct, 90, true);
Matrix m_matrix = new Matrix(1, 0, 0, -1, 0, this.ClientRectangle.Height);
Icon ico = new Icon(m_Parent.Icon, 16, 16);

//(CaptionHeight - ico.Height) / 2)
rectClose = new Rectangle(m_Parent.ClientRectangle.Right - (bmp_Close.Width + 6), (CaptionHeight - bmp_Close .Height )/2, bmp_Close.Width, bmp_Close.Height);
rectMin = new Rectangle(m_Parent.ClientRectangle.Right - (bmp_Close.Width + 8 + bmp_Min.Width), (CaptionHeight - bmp_Min.Height) / 2, bmp_Min.Width, bmp_Min.Height);
rectMove = new Rectangle(0, 0, m_Parent.ClientRectangle.Right - rectMin.Width, CaptionHeight);


//rectMove = new Rectangle(0, 0, this.ClientRectangle.Width, CaptionHeight);
SizeF m_size = e.Graphics.MeasureString(this.Text, m_Font);

if (isActive)
{
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbt, rectLeft);
e.Graphics.FillRectangle(lgbt, rectRight);
e.Graphics.ResetTransform();
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgb, rectBottom);
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(lgb, rectTop);
e.Graphics.DrawIcon(ico, rectTop.X + 2, (CaptionHeight - ico.Height) / 2);
//e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColor), rectTop.X + 18, 4);
e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColor), rectTop.X + ico.Width + 2, (CaptionHeight - m_size.Height) / 2);

}
else
{
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbDeActt, rectLeft);
e.Graphics.FillRectangle(lgbDeActt, rectRight);
e.Graphics.ResetTransform();
e.Graphics.Transform = m_matrix;
e.Graphics.FillRectangle(lgbDeAct, rectBottom);
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(lgbDeAct, rectTop);
e.Graphics.DrawIcon(ico, rectTop.X + 2, (CaptionHeight - ico.Height) / 2);
//e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColorDeAct), rectTop.X + 18, 4);
e.Graphics.DrawString(this.Text, m_Font, new SolidBrush(m_TextColorDeAct), rectTop.X + ico.Width + 2, (CaptionHeight - m_size.Height) / 2);

}

e.Graphics.DrawRectangle(new Pen(Color.Empty), rectMove);
DrawCloseButtonNormal();
DrawMinButtonNormal();

}

}

protected virtual void OnResize(object sender, EventArgs e)
{
m_Parent.Invalidate();
}

protected virtual void OnMouseMove(object sender, MouseEventArgs e)
{
if ((MouseButtons.Left & e.Button) == MouseButtons.Left)
{

if (isMove)
{

int x = m_Parent.Location.X + (e.X - isPoint.X);
int y = m_Parent.Location.Y + (e.Y - isPoint.Y);
m_Parent.Location = new Point(x, y);
}

else if (rectClose.Contains(e.Location))
{
DrawCloseButtonOver();
}

else if (rectMin.Contains(e.Location))
{
DrawMinButtonOver();
}

else if (!rectClose.Contains(e.Location) & isClosing)
{
isClosing = false;
DrawCloseButtonNormal();
}

else if (!rectMin.Contains(e.Location) & isMinimizing)
{
isMinimizing = false;
DrawMinButtonNormal();
}
}
else
{

if (rectClose.Contains(e.Location))
{
DrawCloseButtonOver();
}

else
{
DrawCloseButtonNormal();
}

if (rectMin.Contains(e.Location))
{
DrawMinButtonOver();
}
else
{
DrawMinButtonNormal();
}
}
}

protected virtual void OnMouseUp(object sender, MouseEventArgs e)
{
if (isMove)
{
isMove = false;
}

else if (rectClose.Contains(e.Location) & isClosing)
{
m_Parent.Close();
}
else if (rectMin.Contains(e.Location) & isMinimizing)
{

m_Parent.WindowState = FormWindowState.Minimized;
}
}

protected virtual void OnMouseDown(object sender, MouseEventArgs e)
{
if ((MouseButtons.Left & e.Button) == MouseButtons.Left)
{

if (rectClose.Contains(e.Location))
{
DrawCloseButtonDown();
isClosing = true;
}

else if (rectMin.Contains(e.Location))
{
DrawMinButtonDown();
isMinimizing = true;
}

else if (rectMove.Contains(e.Location))
{

isMove = true;
isPoint.X = e.X;
isPoint.Y = e.Y;
}

}
}

protected virtual void OnVisibleChanged(object sender, EventArgs e)
{
if (m_Parent.Visible)
{
isActive = true;
}
else
{
isActive = false;
}
}

#endregion // Method

#region Function

public override string ToString()
{
return this.Developer;
}

private Region SetWindow()
{
Region functionReturnValue = default(Region);

GraphicsPath Path = new GraphicsPath();
Path.AddArc(0, 0, 10, 10, 180, 90);
Path.AddArc(this.ClientRectangle.Width - 10, 0, 10, 10, 270, 90);
Path.AddLine(this.ClientRectangle.Width, 10, this.ClientRectangle.Width, this.ClientRectangle.Height);
Path.AddLine(this.ClientRectangle.Width, this.ClientRectangle.Height, 0, this.ClientRectangle.Height);
Path.CloseFigure();
Region rgn = new Region(Path);

functionReturnValue = rgn;
m_Parent.Invalidate();
return functionReturnValue;

}

#endregion // Function

#region Classes

internal class WindowListener : NativeWindow
{

private const int WM_ACTIVATEAPP = 0x1c;
public WindowListener()
{
}

[System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
switch ((m.Msg))
{
case WM_ACTIVATEAPP:
isActive = (m.WParam.ToInt32() != 0);
CairoSkinPlus.m_Parent.Invalidate();
break;
}
base.WndProc(ref m);
}
}

#endregion // Classes

}
}
using System;

ملحوظة أخيرة:

من المؤكد الكلاس به بعض Bugs لأنه وكما يعلم الجميع بأن الكمال لله وحده لذلك تعليقاتكم مهمة جدا حيث أنها ستساعد علي تطوير الكلاس بشكل أفضل

من المؤكد أن الكود يمكن تطويره للأفضل وهذا متروك لك عزيزي القارئ ومتروك لإبداعك حيث يمكن أن تصمم الشكل الذي تريده وتعطيه من الألوان ما تريد.

جميع الأمثلة موجودة بالمرفقات وهي بنسخة 2008

بالتوفيق

أخوكم عمر

Example11.rar

Skinner.rar

#4

شكراً لك اخي الكريم على هذه الفوائد القيمة

قولوا لا اله الا الله تفلحوا

ومن يبتغي غير الاسلام دينا فلن يقبل منه وهو في الاخرة من الخاسرين- صدق الله العظيم

#5

تسلم اخي عمر ، وجزاك الله كل خير على هذه المعلومات القيمة.

[وسط]لا اله الا انت سبحانك انى كنت من الظالمين

[/وسط]

#6

السلام عليكــم ورحمـة الله وبركاتــة ،،

منذ مدة طويلة جدا لم أشاهد مشاركات من هذا النوع في المنتدى بأكمله

شكرا جزيلا و جعله الله في ميزان حسناتك

#7

مقال حلو و امثلة حلوة

مشكور على المعلومات

FOTA KHader FAR

سوف اتجة الى عالم الويب

thumb_kujviv1t1.jpg

مواضيع مشابهة