مخطط المكدّس StackLayout في Xamarin - الجزء الثاني


حسام برهان

تحدثنا في الدرس السابق من هذه السلسلة عن المبادئ الأساسيّة للتعامل مع مخطّط المكدّس 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 معها.

الخلاصة

تعلّمنا في هذا الدرس كيفيّة التعامل المتقدّم مع مخطّط المكدّس، حيث تناولنا كيفيّة تموضع اللُصيقات ضمن مخطّط المكدّس سواءً كانت اتجاهه رأسيًّا أم أفقيًّا. في الحقيقة، يمكن تطبيق المعلومات الواردة هنا على أيّ عنصر مرئي تقريبًا، وليس فقط على اللُصيقات. أنصحك بأن تجري الكثير من التجارب على هذا الموضوع، وأن تضع أي استفسار قد يصادفك كسؤال ضمن التعليقات على هذا الدرس، لكي يتسنّى للجميع المشاركة فيه.

.~lock.06-xamarin-stacklayout-part02.docx#

fig01.png

fig02.png

fig03.png

fig04.png

fig05.png

fig06.png

fig07.png

fig08.png

fig09.png

fig10.png





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن