تطوير تطبيقات سطح المكتب

برمجة تطبيق سطح مكتب متكامل بلغة البرمجة سي شارب - 3

متابعة التعريف بتقنية WPF لتطوير تطبيقات سطح المكتب ومدخل الى Databinding

في WPF يمكننا الاستفادة من قيم خصائص العناصر واعطاءها لخصائص اخرى ضمن عناصر اخرى، قد تبدو العبارة إنشائية اكثر من اللازم، لكن لنقوم بتحليل الامر قليلاً

  1. جمعينا نعرف العنصر Checkbox وهو عنصر يحوي خاصية IsChecked من نوع Boolean إما نعم true او لا false، عندما نضغط على هذا العنصر تظهر علامة الصح داخل المربع þ ، لتشير بأن الخيار مفعل، وبالضغط عليه مرة اخرى، تختفي علامة الصح ، مشيرة إلى ان الخيار غير مفعل.
  2. جميعنا نعرف أيضاً، ان غالبية العناصر، تحوي على الخاصية IsEnabled، من نوع Boolean  ايضا، وهي تشير إلى ان العنصر مفعل true او معطل false.
  3. اذا قد تختلف خصائص العناصر من حيث الاسم والوظيفة، لكن في النهاية فإنها تعود لنفس النوع.
  4. الآن لنفترض ، باننا نقوم بتصميم احد التطبيقات، ويوجد لدينا احد المتطلبات بالشكل التالي:

(عندما نقوم بادخال بيانات الموظف، يجب ان نحدد فيما اذا كان الموظف يملك رقم هاتف ارضي أم لا، وفي حال كان يملك رقم هاتف أرضي يجب ان يظهر لدينا حقل نصي لنقوم بادخال هذا الرقم).

حل المشكلة

سنقوم بحل المشكلة بطريقتين: الطريقة الكلاسيكية، وطريقة Databinding التي تقدمها WPF
أولا سنقوم باستبدال الكود الموجود في النافذة MainWindow بالكود التالي، على ان نعود اليه لاحقاً لاكمال المثال السابق الخاص بالأزرار.

<Window x:Class="DesktopApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="500">
    <StackPanel>
        <CheckBox Content="Has Phone Number" Margin="5" Name="phoneCheckBox"
                  Click="phoneCheckBox_Click"/>
        <TextBox Text="" Name="phoneTextBox" IsEnabled="False" Margin="5"/>
    </StackPanel>
</Window>


في هذا الكود قمنا بتعريف العنصر CheckBox واعطيناه الاسم phoneCheckBox  وقمنا ايضا بتعريف العنصر TextBox واعطيناه الاسم phoneTextBox. سيكون لدينا شكل واجهة التصميم كالتالي

الان لنبدأ بتجريب الطريقتين.

الطريقة الكلاسيكية

  1. بداية يكون العنصر CheckBox غير مفعل، بالتالي يجب ان يكون العنصر TextBox غير مفعل أيضاً، لذلك اضفنا الخاصية IsEnabled إلى العنصر TextBox، واسندنا لها القيمة False
  2. عندما يقوم المستخدم بالضغط على المربع CheckBox ستتغير حالته الى true، بالتالي نحن بحاجة لمعالجة حدث الضغط Click، لذلك قمنا باضافة هذا الحدث phoneCheckBox_Click الى العنصر CheckBox، وهذه هي طريقة تعريف الاحداث باستخدام XAML
  3. ضمن كود السي شارب الخاص بهذه النافذة، (يمكن الوصول اليه بالضغط على المفتاح F7 أو الضغط بالزر الأيمن على XAML واختيار View Code) يتولد لدينا دالة خاصة بهذا الحدث، سنقوم فيها فقط بتفعيل الحقل النصي في حال كان مربع الاختيار CheckBox مفعلا، وتعطيل الحقل النصي في حال كان مربع الاختيار معطلاً، اذا كل ما نحتاجه كتابة السطر التالي داخل الحدث Click:
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void phoneCheckBox_Click(object sender, RoutedEventArgs e)
    {
        phoneTextBox.IsEnabled = phoneCheckBox.IsChecked.Value;
    }
}

الآن لو قمنا بتشغيل البرنامج، سنلاحظ انه يعمل بالشكل المطلوب.
النتيجة: ان قيمة تفعيل مربع الاختيار CheckBox هي نفسها قيمة تفعيل الحقل النصي phoneCheckBox، لذلك قمنا بتحديد الوقت الذي تتغير فيه هذه القيمة (وهو حدث الضغط على مربع الاختيار) ثم قمنا باستخدام بعض مهاراتنا البرمجية في كتابة السطر السابق.

وهنا يطرح السؤال

ألا يوجد طريقة افضل يمكنني من خلالها ربط هاتين الخاصتين المتشابهتين ببعض، دون اللجوء لكود السي شارب، خصوصا واننا نمتلك التحكم الكامل بالعناصر عن طريق XAML كما تم ذكره سابقاً! لنتابع اذا مع الطريقة الثانية!

طريقة Databinding

نجيب بنعم على السؤال السابق، فهو اكثر منطقية في العمل من الطريقة الأولى، لذلك سنعمل على الخطوات التالية:

  1. بدل ان نسند الخاصية IsEnabled  في الحقل النصي phoneTextBox إلى القيمة False، كما فعلنا في المثال السابق، سنقوم مباشرة بربطها مع الخاصية IsChecked التابعة للعنصر phoneCheckBox، قد يتبادر إلى الذين بأن الطريقة ستكون اشبه بالتالي: 
    IsEnabled="phoneCheckBox.IsChecked"

     

  2. طبعا للأسف ليست العملية بهذه السهولة، لان الخاصية IsEnabled هي من نوع Boolean بالتالي لا يمكن ان نسند لها الا قيمة من نفس النوع، بينما بهذا الشكل قمنا باسناد قيمة نصية ليس اكثر.
  3. لاسناد القيمة بالشكل الصحيح، علينا ان نخبر XAML بالطريقة التي نود فيها اسناد او جلب القيمة المطلوبة، وهو سيقوم بالباقي، اذا نحن الان بحاجة لتعريف الية الربط Databinding، وهي تتم كالتالي:
    • داخل الخاصية المطلوبة وداخل قوسين متعرجين، نكتب Binding، لنخبر XAML باننا بصدد الربط مع عنصر اخر
    • بعدها نحدد اسم العنصر المراد الربط معه ElementName
    • بعدها نحدد الخاصية المطلوبة من خصائص هذا العنصر Path
  4. لاحظ الالية في الكود التالي، ثم قم باستبدال كود XAML السابق به. ولاحظ اننا تخلصنا من الحدث Click في XAML وايضا في سي شارب
<Window x:Class="DesktopApp.MainWindow"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="MainWindow" Height="400" Width="500">
<StackPanel>
    <CheckBox Content="Has Phone Number" Margin="5" Name="phoneCheckBox" />
    <TextBox Text="" Name="phoneTextBox" 
             IsEnabled="{Binding ElementName=phoneCheckBox, Path=IsChecked}" Margin="5"/>
 </StackPanel>
</Window>

 

 الآن لنقوم بتشغيل البرنامج ونلاحظ نفس النتيجة السابقة!

تقييم الحل

ربما قد تتساءل الآن بأن الحل الثاني كان جميلاً، واختصر علي بعض الجهد، ولكني لم اجد ذلك الفرق الكبير الذي قد يجعلني اعتمد على هذه الطريقة، وانسى الطريقة الأولى!
نعم قد لا يوجد هناك الفرق الكبير، لكن ماذا لو اخبرتك، بانك الآن تستطيع ان تربط كل ما قد يخطر ببالك من الخصائص مع بعضها البعض، فكر مثلا بقائمة تحوي على مجموعة موظفين، وبأنني اريد التعديل على موظف معين، لكن يجب ان اقوم باختيار الموظف اولا من القائمة ثم اضغط على زر تعديل؟ نعم قد نقوم بالتحقق من اختيار او عدم اختيار هذا الموظف ضمن كود السي شارب، ولا نسمح بالتعديل الا في حالة تم اختيار الموظف.
لكن ألا ترى معي، بانه من الأفضل ان يكون زر التعديل مثلا معطلاً، ولا يتفعل الا باختيار موظف من القائمة، وفي حال تم الغاء التحديد، يعود زر التعديل معطلا! نعم يمكنك فعل هذا بسهولة. مثلا يمكننا ان نستخدم الخاصية SelectedItem والتي تمثل الموظف ضمن القائمة، ونربطها مع زر التعديل، بحيث يتفعل الزر فقط في حالة كانت هذه الخاصية تحوي على قيمة وليس Null
قد تتساءل وكيف يمكن ان اربط انواع غير متوافقة من الخصائص مع بعضها البعض في حين اننا اشترطنا ان تكون الخصائص من نفس النوع؟ وهنا لدي SelectedItem من نوع object ولدي IsEnabled من نوع Boolean؟
بالتأكيد ذلك غير ممكن، لكن لو قمنا بتحويل نوع احدى الخصائص الى نوع الخاصية الثانية، يصبح الامر سهلا،  ولهذا تقدم لنا WPF ميزة جديدة تسمى ValueConverter وهي تفيد في التحويل بين انواع الخصائص، اي اننا سنقوم بتحويل object إلى Boolean كما سيمر معنا لاحقاً.

العودة للمثال السابق

الآن بعد ان انتيهنا من مثالنا الجديد، دعونا نعود إلى مثالنا السابق الخاص بالازرار، ونرى كيف يمكننا تلوين الازرار المتبقية، واعتقد ان فكرة الحل اصبحت واضحة لديك الآن، وهي ان نقوم بربط خصائص الأزرار الباقية، مع خصائص الزر الأول؟ بالتاكيد هذا هو الحل.
قم باستبدال كود XAML الحالي، بالكود الحالي الخاص بالأزرار:

<Window x:Class="DesktopApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="500">
    <Grid>
        <Button Background="Red" Foreground="#FFFFFFFF" Content="Left Top" 
                HorizontalAlignment="Left" VerticalAlignment="Top" Name="btn"/>
        <Button Content="Left Center" HorizontalAlignment="Left" VerticalAlignment="Center"/>
        <Button Content="Left Bottom" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
        
        <Button Content="Center Top" HorizontalAlignment="Center" VerticalAlignment="Top"/>
        <Button Content="Center Center" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Button Content="Center Bottom" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
        
        <Button Content="Right Top" HorizontalAlignment="Right" VerticalAlignment="Top"/>
        <Button Content="Right Center" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <Button Content="Right Bottom" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
    </Grid>
</Window>

لاحظ أننا قمنا باعطاء الزر الأول الإسم btn، لكي نستطيع الوصول اليه، الآن اصبح ربط الخصائص اكثر سهولة. سنذهب الى الزر الثاني ونقوم بربط خصائصة مع خصائص الزر الأول كما يلي:

<Button Content="Left Center" HorizontalAlignment="Left" 
        VerticalAlignment="Center"
        Background="{Binding ElementName=btn, Path=Background}"
        Foreground="{Binding ElementName=btn, Path=Foreground}"/>

لاحظ تحول لون خلفية وخط الزر الثاني، لنفس الوان الزر الأول. يمكنك الان تطبيق نفس الأمر على باقي الأزرار، وستلاحظ انها قامت بنسخ نفس قيم الخصائص من الزر الأول، واصبحت واجهة التصميم بهذا الشكل:

قد تعترض مجدداً، بأنني لم اقم باختصار اي كود، بالعكس قمت بكتابة المزيد منه. واعتراضك صحيح، كون المثال حاليا لا يساعد في ابراز اهمية هذه الميزات الجديدة، لكن دعنا نمضي الآن، وسنرى لاحقاً الفائدة الحقيقية لهذه الميزات.
سأطلب منك الآن أن تقوم بتغيير لون الزر الأول إلى اللون الاخضر (بإمكانك استبدال قيمة الخاصية Background من الأحمر Red  إلى الأخضر Green) ولاحظ ماذا حدث!
نعم لقد تغيير الوان جميع الازرار الباقية، اذاً التعديل على خصائص العنصر، سيؤدي إلى التعديل على خصائص جميع العناصر المرتبطة معه، بدون تدخل من المستخدم او المطور.
بنفس هذا المفهوم سنرى لاحقا، كيف سنقوم باستخدام Databinding بكثافة لربط مكونات مشروعنا، وجعله اكثر مرونة، وتجاوبا مع تفاعلات المستخدم.

اخبار سيئة!

في التطبيقات الحقيقية قد لا نلجاً لمثل هذا المثال، واعني ان نقوم بربط خصائص العناصر مع بعضها بهدف تغيير المظهر، لأن التحكم بمظهر العناصر في WPF يتم باستخدام Styles و Resources، لكن لا تقلق، بالتأكيد لن نتخلى عن هذه الميزات الرائعة، لانه تم تصميمها لتلبية حاجات اكثر من هذا المثال. لذلك الآن سنقوم بالتعديل على كود XAML الخاص بالشاشة الرئيسية  MainWindow بالكود التالي:

<Window x:Class="DesktopApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="500">
    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="Background" Value="Red"/>
            <Setter Property="Foreground" Value="White"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Button Content="Left Top" HorizontalAlignment="Left" VerticalAlignment="Top"/>
        <Button Content="Left Center" HorizontalAlignment="Left" VerticalAlignment="Center"/>
        <Button Content="Left Bottom" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
        
        <Button Content="Center Top" HorizontalAlignment="Center" VerticalAlignment="Top"/>
        <Button Content="Center Center" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Button Content="Center Bottom" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
        
        <Button Content="Right Top" HorizontalAlignment="Right" VerticalAlignment="Top"/>
        <Button Content="Right Center" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <Button Content="Right Bottom" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
    </Grid>
</Window>

في هذا المثال، قمنا بمسح الخصائص الخاصة بلون الخلفية ولون الخط من جميع الازرار، وقمنا بتعريف جديد هو Style ضمن موارد هذه النافذةWindow Resources .
ما يهمنا باختصار هنا، اننا قمنا باخبار XAML، حسناً اذا كان لديك زر، فسوف يتحول لون خلفيته الى اللون الاحمر، ولون الخط الى اللون الابيض، دون ان يقوم المطور بتعريف ذلك بشكل صريح. بالتالي النتيجة ستكون مشابه للنتيجة السابقة، لكن هذه المرة باستخدام Styles. والتي سنستخدما لاحقاً عندما نتحدث عن التحكم بالمظهر.
طبعا ما زال بامكانك التعديل على لون خلفية الزر، وسوف يقوم WPF بتجاوز القيمة المعرفة ضمن Style واعتماد القيمة الجديدة.

ربط البيانات Databinding

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

مثال تجريبي

1. من نافذه Solution Explorer نضغط على اسم المشروع بالزر الأيمن ثم نختار Add ثم Class

2. من نافذة Add New Item التي ظهرت، نعطي الكلاس الجديد اسم Employee ثم نضغط على Add

3. بعد ان يقوم فيجوال ستوديو باضافة الكلاس إلى ملفات المشروع، نقوم بمسح محتويات الكلاس، ونستبدلها بالكود التالي:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DesktopApp
{
    public class Employee
    {
        public int EmployeeID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Title { get; set; }        
        public DateTime? BirthDate { get; set; }
        public DateTime? HireDate { get; set; }
        public string Address { get; set; }
        public string City { get; set; }        
        public string Country { get; set; }                
        public string Notes { get; set; }
    }
}

4. هنا قمنا بتعريف مجموعة من الخصائص التي تمثل الموظف، والهدف منها أن نقوم بربطها مع واجهات XAML، وسنرى كيف ستقوم WPF بعرض بيانات الموظف على العناصر دون لحاجة كتابة اي كود سي شارب.

5. الآن سنقوم باستبدال كود XAML الموجود في الشاشة الرئيسية MainWindow بالكود التالي:

<Window x:Class="DesktopApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DesktopApp"
        Title="MainWindow" Height="400" Width="500">
    <Window.DataContext>
        <local:Employee FirstName="Zaid" LastName="Amr" Title="Engineer" 
                        HireDate="01/01/2018" Address="Al Rayyan" City="Doha"
                        Country="Qatar"/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Content="First name"/>
        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding FirstName}"/>

        <Label Grid.Row="1" Grid.Column="0" Content="Last name"/>
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding LastName}"/>

        <Label Grid.Row="2" Grid.Column="0" Content="Title"/>
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Title}"/>

        <Label Grid.Row="3" Grid.Column="0" Content="Birth date"/>
        <DatePicker Grid.Row="3" Grid.Column="1" SelectedDate="{Binding BirthDate}"/>

        <Label Grid.Row="4" Grid.Column="0" Content="Hire date"/>
        <DatePicker Grid.Row="4" Grid.Column="1" SelectedDate="{Binding HireDate}"/>

        <Label Grid.Row="5" Grid.Column="0" Content="Address"/>
        <TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Address}"/>

        <Label Grid.Row="6" Grid.Column="0" Content="City"/>
        <TextBox Grid.Row="6" Grid.Column="1" Text="{Binding City}"/>

        <Label Grid.Row="7" Grid.Column="0" Content="Country"/>
        <TextBox Grid.Row="7" Grid.Column="1" Text="{Binding Country}"/>

        <Label Grid.Row="8" Grid.Column="0" Content="Notes"/>
        <TextBox Grid.Row="8" Grid.Column="1" Text="{Binding Notes}"/>
    </Grid>
</Window>

6. وسيكون لدينا شكل واجهة التصميم بالشكل التالي (اذا لم تظهر البيانات، قم بعمل Build للمشروع)

7. سنقوم بعد قليل بشرح كود XAML، ولكن ما اريد منك ملاحظته، ان العناصر اخذت القيم واظهرتها بشكل صحيح، رغم عدم اخبارنا لها بذلك، فقد جرت العادة في تطبيقات Windows Forms وأغلب التطبيقات الأخرى، أننا عندما نريد اعطاء قيمة لعنصر معين مثل الحقل النصي، نقوم باسنادها برمجياً بالشكل التالي:

...
txtFirstName.Text = "Zaid";
txtLastName.Text = "Amr";
txtTitle.Text = "Engineer";
...

لاحظ كيف قمنا باسناد القيم الى العناصر باستخدام لغة سي شارب. لكن حقيقةً في مثالنا لا نحتاج لمثل هذا الكود، كما شاهدنا منذ قليل، لذلك دعونا نقوم بشرح ما حدث في XAML سطرا بسطر.

<Window.DataContext>
    <local:Employee FirstName="Zaid" LastName="Amr" Title="Engineer" 
                    HireDate="01/01/2018" Address="Al Rayyan" City="Doha"
                    Country="Qatar"/>
</Window.DataContext>

هنا قمنا بتعريف الخاصية DataContext التابعة للشاشة MainWindow واسندنا لها كائن object من نوع Employee من الكلاس الذي قمنا بتعريفه منذ قليل
 نعم تسمح لنا XAML بتعريف كائن object لكن بطريقتها الخاصة المشابه ل XML، هنا قمنا باسناد قيم مباشرة الى الكائن Employee مثل الاسم الاول والاسم الاخير وغيرها، بالتالي فإن الواجهة MainWindow اصبحت تحوي على كائن من نوع Employee وفيه بعض القيم. اضف على ذلك ان تعريف هذا الكائن تم ضمن DataContext بالتالي اصبح كمصدر بيانات عام للواجهة باكملها، بمعنى اي عنصر يقع داخل الواجهة سيمكنه الوصول إلى هذا الكائن.
الفائدة من الوصول لهذا للكائن، هي فعليا في الوصول لخصائصه Properties كالأسم الاول والاخير.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
</Grid>

هنا قمنا بتعريف اسطر واعمدة داخل العنصر Grid والهدف هو تحويلها لشكل يشبه الجدول، فنلاحظ مثلا اننا قمنا بالبداية بتعريف عمودين داخل الخاصية :

Grid.ColumnDefinitions

كما نلاحظ ايضا ان العمود الاول لديه عرض محدد بالقيمة auto، اي سيقوم العمود بالامتداد افقيا ليتسع للمحتوى الذي سيوضع فيه. بينما العمود الثاني اخذ عرض محدد بالقيمة *، وهي تعنى خذ كامل المساحة المتبقية من Grid بعد ان يقوم العمود الاول باخذ الحجم الذي يناسبه، اذا الاولوية للعمود الأول.
أما بالنسبة للأسطر فقط تم تعريفها داخل الخاصية:

Grid.RowDefinitions

ايضا هنا قمنا بتعريف مجموعة من 9 أسطر، وقمنا باعطاء كل سطر الارتفاع المناسب ليتسع للمحتوى بداخله، طبعا يمكننا تعريف اي عدد من الاسطر واي عدد من الاعمدة بالعدد الذي يناسبنا.

<Label Grid.Row="0" Grid.Column="0" Content="First name"/>

هنا قمنا بتعريف عنصر Label لاظهار قيمة نصية بجانب الحقل النصي، لكن الجديد هنا وجود خاصتين

Grid.Row="0" Grid.Columnd="0"

وهي تعني بان هذا العنصر Label سيتم وضعه في السطر الاول والعمود الاول من Grid التي تحوية، وبنفس الطريقة قمنا بتعريف عنصر نصي  TextBox تحته مباشرة، وقمنا بوضعه في السطر الأول والعمود الثاني، لذلك سيظهر بجانب Label، بتكرار نفس الخطوة، قمنا بتعريف جميع العناصر على الواجهة.
الجديد هنا، اننا قمنا بملىء الخاصية Text بتعبير برمجي، يفهمه XAML كما تحدثنا سابقا:

Text="{Binding FirstName}"

اي قمنا باخبار XAML بأن قيمة الخاصية Text لهذا الحقل سنقوم بربطها مع الكائن Employee الذي تم تعريفه في الأعلى، وكما اشرنا بأن هذا الكائن اصبح بمثابة مصدر بيانات لكامل عناصر الواجهة، اذا الوصول لخصائصة اصبح مباشرا، لذلك وضعنا خاصيته FirstName مباشرة.
الآن ستقوم XAML باستخراج قيمة FirstName من الكائن Employee واظهارها في الحقل النصي. قمنا بتكرار الامر مع باقي الحقول، حتى وصلنا الى الحقل الخاص بالتاريخ:

<DatePicker Grid.Row="4" Grid.Column="1" SelectedDate="{Binding HireDate}"/>

الفرق هنا، اننا بدل ان نربط الخاصية Text مع HireDate قمنا بربط الخاصية SelectedDate وهي اكثر منطقية، فالموظف يحوي على خاصية من نوع DateTime تمثل تاريخ تعيينه HireDate، والعنصر DatePicker يحوي على خاصية اسمها SelectedDate من نوع DateTime ايضا، لذلك طريقة الربط هذه اكثر منطقية.

الآن اعتقد انك تفكر، بانه يمكن ان يكون لدينا عدد غير منتهي من الخصائص، ويمكننا ربطها جميعا مع مختلف العناصر بجميع الاشكال الممكنة، الجواب نعم بالطبع، لكن ضف على ذلك العديد من ميزات الربط سنتطرق لها لاحقا.

الآن لنقم بتشغيل البرنامج F5، ونلاحظ ان القيم تظهر داخل العناصر بشكل صحيح، حاول التعديل على احدى الحقول FirstName مثلاً، ستجد انه لا يوجد اي مشكلة في التعديل، ويكون سؤال ختام المقالة كالتالي:

هل اذا قمنا بالتعديل على احدى القيم الظاهرة داخل العناصر، ستغيير القيمة في الكائن Employee أم أننا بحاجة لنستخدم الطريقة الكلاسيكية في اسناد القيم للكائن؟

employee.FirstName = txtFirstName.Text

سأترك لك التفكير بمنطقية في هذا السؤال، واعتقد انك بدأت تفهم الميزات التي نتحدث عنها في سلسلتنا.


بالتوفيق،،، 


انضم لمجموعة الواتساب

https://chat.whatsapp.com/8D0EfjMGpK46oNaxVIso2f


رابط الكود على GitHub 

https://github.com/parmajiat/desktop-app-course/tree/wpf-databinding


المقالة السابقة:

التعريف بتقنية WPF لتطوير تطبيقات سطح المكتب

Hash Tag


Leave a comment

Tarek Jihad

Author