تحدثنا في الدرس السابق من هذه السلسلة عن المبادئ الأساسيّة للتعامل مع مخطّط المكدّس StackLayout
. سنتابع في هذا الدرس العمل معه، حيث سنقدّم مفاهيم أساسيّة لتموضع العناصر ضمن مخطّط المكدّس. سننشئ تطبيق بسيط يمكننا من خلاله فهم خيارات التموضع الرأسيّة والأفقيّة للعناصر ضمن مخطّط مكدّس StackLayout
.
أنشئ تطبيقًا جديدًا (كما تعلّمنا في الدروس السابقة) وسمّه LabelPositionsApp
. وأبق فقط على المشروعين LabelPositionsApp (Portable
) و LabelPositionsApp.Droid
ضمنه.
فهم خيارات التموضع الرأسيّة
انقر بزر الفأرة الأيمن على المشروع LabelPositionsApp (Portable)
وأضف صفحة محتوى ContentPage جديدة (كما تعلّمنا من الدرس السابق) سمّها VerticalOptionsPage
. سننشئ ضمن بانية الصنف VerticalOptionsPage
مخطّط مكدّس وسنضيف إليه ثلاث لُصيقات. ستبدو هذه البانية على الشكل التالي:
public VerticalOptionsPage() { Content = new StackLayout { Children = { new Label { Text = "LayoutOptoins.Start", VerticalOptions = LayoutOptions.Start, BackgroundColor = Color.Accent, TextColor = Color.Black }, new Label { Text = "LayoutOptoins.Center", VerticalOptions = LayoutOptions.Center, BackgroundColor = Color.Aqua, TextColor = Color.Black }, new Label { Text = "LayoutOptoins.End", VerticalOptions = LayoutOptions.End, BackgroundColor = Color.Yellow, TextColor = Color.Black } } }; }
لاحظ أنّنا قد استخدمنها الإسناد المباشر للخصائص أثناء إنشاء كائنات اللُصيقات. استخدمنا هذه المرّة الخاصيّة VerticalOptions
لكل لصيقة أنشأناها. تُحدّد هذه الخاصيّة التموضع الرأسي لكل لصيقة ضمن مخطّط المكدّس. هذه الخاصيّة هي من نوع البنية LayoutOptions
التي تحتوي على 8 حقول ساكنة static
تُعبّر عن جميع خيارات التموضع الممكنة للعناصر المرئيّة. يتناول تطبيقنا البسيط السابق ثلاثةً من هذه الحقول وهي: LayoutOptions.Start
للصيقة الأولى، وLayoutOptions.Center
للصيقة الثانية، وLayoutOptions.End
للصيقة الثالثة كما هو واضح من الشيفرة البرمجيّة. بالنسبة للخاصيتين BackgroundColor
(لون الخلفية) وTextColor
(لون النص) فهما موجودتان من باب تمييز اللُصيقات على الشاشة فحسب. هناك أمرٌ آخر، وهو أنّ مخطّط المكدّس في الشيفرة السابقة سيُعتبر رأسيًّا بشكل افتراضي، وذلك لأنّ خاصية الاتجاه Orientation
له تحمل القيمة StackOrientation.Vertical
بشكل افتراضيّ.
انتقل إلى الملف App.cs
واحرص على أن تكون بانيته على الشكل التالي:
public App() { // The root page of your application MainPage = new VerticalOptionsPage(); }
شغّل التطبيق باستخدام F5 لتحصل على شكل شبيه بما يلي:
أضف الصورة fig01
كما وسبق أن أوضحنا في درس سابق أنّنا عندما نُسند القيمة LayoutOptions.Start
للخاصية VerticalOptions
للصيقة فإنّها ستظهر أعلى الشاشة، أمّا عند إسناد القيمة LayoutOptions.Center
لهذه الخاصية فستظهر اللصيقة في المنطقة الوسط للشاشة، أمّا القيمة LayoutOptions.End
فستودّي إلى إظهار اللصيقة في المنطقة السفلية للشاشة، والمسافات الفارغة بين اللُصيقات السابقة هي نتيجة القيمة الافتراضيّة للخاصية Spacing
لمخطّط المكدّس. ولكن هذا لا يحدث تمامًا بالنسبة لتطبيقنا السابق، فكلّ اللُصيقات تظهر كما لو أنّها تقع في جهة واحدة من الشاشة، وكلّها ذات ارتفاع ثابت، وواضح أيضًا أنّ هناك مساحة فارغة تمامًا غير مستخدمة (مساحة حرّة) تقع في الأسفل. سنتعلّم كيفيّة التحكم بمواضع بهذه اللُصيقات بصورة أكبر، ولكن من أجل هذا التطبيق علينا أن نفهم الفرق بين Start
وCenter
وEnd
.
لنجري الآن بعض التعديلات البسيطة على تطبيقنا السابق. استبدل محتويات بانية الصنف VerticalOptionsPage
بالشيفرة التالية:
public VerticalOptionsPage() { Content = new StackLayout { Children = { new Label { Text = "LayoutOptoins.StartAndExpand", VerticalOptions = LayoutOptions.StartAndExpand, BackgroundColor = Color.Accent, TextColor = Color.Black }, new Label { Text = "LayoutOptoins.Center", VerticalOptions = LayoutOptions.Center, BackgroundColor = Color.Aqua, TextColor = Color.Black }, new Label { Text = "LayoutOptoins.End", VerticalOptions = LayoutOptions.End, BackgroundColor = Color.Yellow, TextColor = Color.Black } } }; }
أجريت تعديلًا بسيطًا على اللصيقة الأولى، حيث أسندت القيمة LayoutOptions.StartAndExpands
للخاصية VerticalOptions
لها. عندما تنفّذ التطبيق هذه المرّة ستحصل على الشكل التالي:
أضف الصورة fig02
القيمة StartAndExpand
تعني ببساطة: تموضع أولًا ثم تمدّد. ستتموضع اللصيقة الأولى أوّل الشاشة كما حدث مع التطبيق السابق، ولكنّها ستشغل المساحة الحرّة (الفارغة) التي كانت موجودة مسبقًا، أي أنّ التّمدّد يكون على هذه المساحة الحرّة في حين أنّ النص الخاص باللصيقة سيكون بالأعلى (بسبب وجود Start
). ودليل ذلك أنّ اللصيقتين التاليتين قد ظهرتا أسفل هذه المساحة الفارغة والتي أصبحت مشغولة الآن من قِبَل اللصيقة الأولى، ولو لم يصبح لون الخلفيّة لهذه المساحة الحرّة مماثلًا للون الخلفية للصيقة الأولى. لكي نفهم الموضوع بشكل أفضل عدّل الشيفرة الموجودة ضمن بانية الصنف VerticalOptionsPage
لتحمل اللصيقة الثانية القيمة LayoutOptions.CenterAndExpand
للخاصية VerticalOptions
. في حين تحمل اللصيقتين الأولى والثالثة القيمتين Start
وEnd
على الترتيب (عدّل النص الذي سيظهر على اللُصيقات للتوضيح). لتحصل على الشكل التالي:
أضف الصورة fig03
انظر كيف شغلت اللصيقة الثانية المساحة الحرّة بالكامل هذه المرّة، في حين أنّ النص الخاص باللصيقة سيكون هذه المرّة بالوسط (بسبب وجود Center
). ولعلّك تستطيع الآن تخمين الشكل الذي ستحصل عليه في حال أسندت القيمة LayoutOptions.EndAndExpand
للخاصية VerticalOptions
للصيقة الثالثة وأعدت اللصيقتين الأولى والثانية إلى القيمتين Start
وCenter
على الترتيب. ستحصل على شكل شبيه بما يلي:
أضف الصورة fig04
أصبحت اللصيقة الثالثة تشغل المساحة الحرّة بالكامل، ويظهر نصّها في الأسفل (بسبب وجود End
). وهكذا نكون قد ناقشنا ثلاث حالات يكون في كلّ منها إحدى اللُصيقات فقط هي من تحمل ميزة التّمدّد Expand
. ولكن ماذا لو حملت لصيقتين أو أكثر ميزة التّمدّد Expand
بنفس الوقت؟ الجواب بسيط، ستتقاسم هذه اللُصيقات المساحة الحرّة فيما بينها وتتموضع حسب قيم Start
وCenter
وEnd
الخاصّة بها. عدّل بانية الصنف VerticalOptionsPage
لتحتوي على الشيفرة البرمجيّة التالية:
public VerticalOptionsPage() { Content = new StackLayout { Children = { new Label { Text = "LayoutOptoins.StartAndExpand", VerticalOptions = LayoutOptions.StartAndExpand, BackgroundColor = Color.Accent, TextColor = Color.Black }, new Label { Text = "LayoutOptoins.CenterAndExpand", VerticalOptions = LayoutOptions.CenterAndExpand, BackgroundColor = Color.Aqua, TextColor = Color.Black }, new Label { Text = "LayoutOptoins.EndAndExpand", VerticalOptions = LayoutOptions.EndAndExpand, BackgroundColor = Color.Yellow, TextColor = Color.Black } } }; }
نفّذ البرنامج لتحصل على الشكل الجميل التالي:
أضف الصورة fig05
ستقسّم المساحة الحرّة الآن بين اللُصيقات الثلاث بالتساوي، ويتموضع النص في كل لصيقة بحسب القيم Start
وCenter
وEnd
لكلّ منها.
بقيت حالتان لم نناقشهما بعد، وهما Fill
وFillAndExpand
. ليس للقيمة LayoutOptions.Fill
في الواقع أيّ دور عند إسنادها للخاصيّة VerticalOptions
في حال كان الاتجاه Orientation
لمخطّط المكدّس رأسيًا (كما في الأمثلة التي تناولناها حتى الآن)، في حين تعمل القيمة LayoutOptionsFillAndExpand
بنفس الأسلوب ولكن بإشغال المساحة الحرّة المتاحة بحيث يمتد لون الخلفيّة في هذه المرّة ليشمل جميع المساحة المشغولة. انظر إلى الأشكال الأربعة التالية التي أعددتها للمقارنة، والتي توضّح تأثير وضع مزيج من قيم LayoutOptions
معًا في نفس التطبيق السابق:
أضف الصورة fig06
الفرق الوحيد بين الشكلين السابقين هو في قيمة الخاصية VerticalOptions
للصيقة الثالثة في كل منهما. ففي الشكل A تكون هذه القيمة Fill
لذلك فهي لا تملك أيّ تأثير كما أسلفنا، في حين أنّه في الشكل B تصبح FillAndExpand
لذلك تملأ هذه اللصيقة كامل المساحة الفارغة المتبقيّة. لننتقل الآن إلى المجموعة الثانية:
أضف الصورة fig07
في الشكل C تقتسم اللُصيقات الثلاث المساحة الحرّة فيما بينها لأنّ كلّا منهما يحمل الميزة Expand
. أمّا في الشكل D فيتكرّر نفس الأمر مع ملاحظة أنّ اللصيقة الثانية أصبحت FillAndExpand
والثالثة EndAndExpand
، وهذا كلّ شيء.
ملاحظة
من الواضح أنّ النص يظهر دومًا في أعلى اللصيقة التي تحمل خاصيّتها VerticalOptions
القيمة FillAndExpand
. يعود سبب ذلك إلى خاصيّة أخرى في اللصيقة وهي التي تتحكّم بمحاذاة النص رأسيًّا في هذه الحالة. اسم هذه الخاصيّة VerticalTextAlignment
وتقبل قيمًا من المعدودة TextAlignment
. لا تعمل هذه الخاصيّة في حال كان اتجاه مخطّط المكدّس رأسيًّا، إلّا إذا كانت اللصيقة FillAndExpand
.
فهم خيارات التموضع الأفقيّة
إذا كانت خيارات التموضع الرأسيّة واضحة بالنسبة إليك، فستكون خيارات التموضع الأفقيّة سهلة الفهم وبسيطة للغاية. فكل الكلام السابق لخيارات التموضع الرأسية سيبقى صحيحًا ولكن بالشكل المناسب لخيارات التموضع الأفقيّة.
أضف صفحة محتوى ContentPage
جديدة للمشروع LabelPositionsApp
(Portable) وسمّها HorizontalOptionsPage
. سننشئ ضمن بانية الصنف HorizontalOptionsPage
مخطّط مكدّس وسنضيف إليه أيضًا ثلاث لُصيقات. ستبدو هذه البانية على الشكل التالي:
public HorizontalOptionsPage() { Content = new StackLayout { Orientation = StackOrientation.Horizontal, Children = { new Label { Text = "Start", HorizontalOptions = LayoutOptions.Start, BackgroundColor = Color.Accent, TextColor = Color.Black }, new Label { Text = "Center", HorizontalOptions = LayoutOptions.Center, BackgroundColor = Color.Aqua, TextColor = Color.Black }, new Label { Text = "End", HorizontalOptions = LayoutOptions.End, BackgroundColor = Color.Yellow, TextColor = Color.Black } } }; }
لاحظ كيف أسندنا القيمة StackOrientation.Horizontal
للخاصيّة Orientation
لمخطّط المكدّس لكي يُصبح اتجاهه أفقيًّا. ولاحظ أيضًاً أنّنا نستخدم في هذه المرّة الخاصية HorizontalOptions
(بدلًا من VerticalOptions
) لكل لصيقة. عند تنفيذ البرنامج ستحصل على شكل شبيه بما يلي:
أضف الصورة fig08
لاحظ هنا أنّ عرض كل لصيقة يتناسب مع طول النص الموجود فيها، وهذا أمر لم نكن نصادفه في مخطّط المكدّس الرأسيّ. كما تجدر ملاحظة أنّني قد استخدمت في هذا التطبيق الأسماء Start
وCenter
وEnd
لعرضها ضمن محتويات اللُصيقات (ضمن الخاصيّة Text
لها) بدون اسم البنية LayoutOptoins
طلبًا للاختصار.
سنجري الآن بعض المقارنات بإسناد مزيج من خيارات LayoutOptions
عندما يكون مخطّط المكدّس أفقيًّا. يمكنك التعديل على الشيفرة البرمجيّة للبرنامج السابق بحسب ما تراه ضمن الأشكال التالية لتحصل على نفس النتيجة.
أضف الصورة fig09
أضف الصورة fig10
ملاحظة
تحدثنا مسبقًا عن محاذاة النص ضمن اللصيقة. في الحقيقة توجد قاعدة مهمّة ولكنّها بسيطة في طريقة التعامل مع محاذاة النصوص ضمن اللُصيقات. تنص هذه القاعدة على أنّه إذا كان مخطّط المكدّس رأسيًّا عندها لن تعمل إلّا الخاصيّة HorizontalTextAlignment
لمحاذاة النص أفقيًّا. أمّا إذا كان المكدّس أفقيًّا فلن تعمل عندها إلّا الخاصيّة VerticalTextAlignment
لمحاذاة النص رأسيًّا أي دومًا بعكس اتجاه مخطّط المكدّس. ويُستثنى من هذه القاعدة حالتان:
الحالة الأولى إذا كان مخطط المكدّس رأسيًّا وكانت اللصيقة FillAndExpand
فعندها يمكن استخدام الخاصيّة VerticalTextAlignment
معها.
الحالة الثانية إذا كان مخطط المكدّس أفقيًّا وكانت اللصيقة FillAndExpand
فعندها يمكن استخدام الخاصيّة HorizontalTextAlignment
معها.
الخلاصة
تعلّمنا في هذا الدرس كيفيّة التعامل المتقدّم مع مخطّط المكدّس، حيث تناولنا كيفيّة تموضع اللُصيقات ضمن مخطّط المكدّس سواءً كانت اتجاهه رأسيًّا أم أفقيًّا. في الحقيقة، يمكن تطبيق المعلومات الواردة هنا على أيّ عنصر مرئي تقريبًا، وليس فقط على اللُصيقات. أنصحك بأن تجري الكثير من التجارب على هذا الموضوع، وأن تضع أي استفسار قد يصادفك كسؤال ضمن التعليقات على هذا الدرس، لكي يتسنّى للجميع المشاركة فيه.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.