تطبيق Styles وTemplates وResources في WPF مع تمرين عملي

Amine
13/09/2024

في هذا الدرس، سنتناول كيفية استخدام Styles وControl Templates وResources في WPF لتخصيص واجهات المستخدم. سنشرح كيفية إنشاء Styles وتطبيقها على عناصر التحكم، وكيفية استخدام Control Templates لإعادة تصميم واجهات المستخدم بشكل مخصص، بالإضافة إلى كيفية إدارة Resources بفعالية داخل التطبيقات. سننهي الدرس بتمرين عملي لتصميم نموذج تسجيل دخول يحتوي على ثيمات (Themes) متعددة يمكن التبديل بينها.

1. Styles في WPF

1.1 إنشاء وتطبيق Styles في XAML

في WPF، يمكننا استخدام Styles لتعريف مجموعة من الخصائص التي يمكن تطبيقها على عناصر تحكم متعددة. يتم تعريف Styles في XAML داخل <Window.Resources> أو <Application.Resources>، ويمكن تطبيقها على أي عنصر تحكم باستخدام خاصية Style. يمكن للـ Styles أن تجعل التصميم موحدًا وسهل الصيانة عبر جميع عناصر الواجهة.

<Window.Resources>
    <Style x:Key="ButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="LightBlue" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontSize" Value="16" />
    </Style>
</Window.Resources>

<Button Style="{StaticResource ButtonStyle}" Content="تسجيل الدخول" Margin="20" />

في المثال أعلاه، قمنا بإنشاء Style لعناصر التحكم من نوع Button والذي يغير خصائص مثل الخلفية، لون النص، وحجم الخط. يتم تطبيق هذا الـ Style باستخدام StaticResource.

1.2 الوراثة في Styles واستخدام BasedOn

في WPF، يمكن أن تعتمد Styles على Styles أخرى باستخدام الخاصية BasedOn. هذا يعني أنه يمكنك إنشاء Style أساسي ثم تخصيصه لأنماط أخرى دون الحاجة إلى تكرار جميع الخصائص.

<Style x:Key="BasicButtonStyle" TargetType="Button">
    <Setter Property="Background" Value="Gray" />
    <Setter Property="Foreground" Value="White" />
</Style>

<Style x:Key="SpecialButtonStyle" TargetType="Button" BasedOn="{StaticResource BasicButtonStyle}">
    <Setter Property="Background" Value="DarkGreen" />
</Style>

هنا، قمنا بإنشاء Style أساسي BasicButtonStyle ثم استخدمناه كأساس لـ SpecialButtonStyle الذي يُغيّر الخلفية فقط. هذا يقلل من التكرار ويجعل الصيانة أسهل.

1.3 الأنماط الضمنية والمُعلنة (Implicit vs. Explicit Styles)

Styles المُعلنة (Explicit Styles) هي تلك التي تحتاج إلى ربط مباشر مع عنصر التحكم باستخدام مفتاح (Key)، كما في المثال السابق. بينما الأنماط الضمنية (Implicit Styles) تُطبق تلقائيًا على كل العناصر من نفس النوع داخل الواجهة بدون الحاجة إلى تحديد مفتاح.

<Style TargetType="TextBox">
    <Setter Property="FontSize" Value="14" />
    <Setter Property="Margin" Value="5" />
</Style>

<TextBox Text="إدخال نص" /> 

في المثال أعلاه، يتم تطبيق Style تلقائيًا على جميع عناصر TextBox داخل الواجهة دون الحاجة إلى ربط مباشر. هذه الأنماط مفيدة عندما ترغب في تطبيق تصميم معين بشكل شامل.

1.4 إضافة Triggers في Styles

في WPF، يمكن استخدام Triggers داخل Styles لتغيير خصائص عناصر التحكم بناءً على حالات معينة. على سبيل المثال، يمكنك تغيير لون الخلفية عند تمرير الفأرة فوق العنصر باستخدام IsMouseOver.

<Style TargetType="Button">
    <Setter Property="Background" Value="LightGray" />
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="Orange" />
        </Trigger>
    </Style.Triggers>
</Style>

يتم استخدام هذا الـ Trigger لتغيير خلفية الزر إلى البرتقالي عندما يحوم مؤشر الفأرة فوق الزر. هذه الآلية تتيح تفاعلًا ديناميكيًا وسهل التنفيذ.

1.5 MultiTriggers و DataTriggers

يمكنك أيضًا استخدام MultiTrigger و DataTrigger لإنشاء شروط أكثر تعقيدًا. على سبيل المثال، يمكنك استخدام MultiTrigger لتغيير مظهر الزر عندما يكون في حالة محددة وعند تمرير الفأرة عليه:

<Style TargetType="Button">
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsMouseOver" Value="True" />
                <Condition Property="IsEnabled" Value="False" />
            </MultiTrigger.Conditions>
            <Setter Property="Background" Value="Red" />
        </MultiTrigger>
    </Style.Triggers>
</Style>

2. Resources في WPF

2.1 Resource Dictionaries: إنشاء ودمج القواميس

Resources في WPF هي طريقة لتجميع قيم مثل الألوان، Styles، وControl Templates في مكان مركزي لإعادة استخدامها. تُخزن هذه الموارد في ResourceDictionary ويمكن أن تكون على مستوى النافذة، الصفحة، أو التطبيق بالكامل.

<ResourceDictionary>
    <Style x:Key="TitleText" TargetType="TextBlock">
        <Setter Property="FontSize" Value="24" />
        <Setter Property="FontWeight" Value="Bold" />
    </Style>
</ResourceDictionary>

يمكنك دمج قواميس الموارد (Resource Dictionaries) لدمج موارد من ملفات متعددة واستخدامها عبر التطبيق.

2.2 الفرق بين StaticResource و DynamicResource

هناك نوعان من الموارد في WPF: StaticResource و DynamicResource. StaticResource يُحمّل مرة واحدة فقط عند بدء التطبيق ولا يتم تحديثه بعد ذلك، بينما DynamicResource يُحمّل ديناميكيًا ويمكن تغييره أثناء تشغيل التطبيق.

<TextBlock Text="عنوان" Style="{StaticResource TitleText}" />
<TextBlock Text="عنوان ديناميكي" Style="{DynamicResource TitleText}" />

عادةً ما يُفضل استخدام StaticResource في الحالات التي لا تحتاج إلى تحديث ديناميكي، بينما يُفضل DynamicResource عند الحاجة إلى تحديث موارد مثل الثيمات في وقت التشغيل.

2.3 تحسين الأداء باستخدام x:Shared

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

<ResourceDictionary>
    <Style x:Key="MyButtonStyle" TargetType="Button" x:Shared="False">
        <Setter Property="Background" Value="LightGray" />
    </Style>
</ResourceDictionary>

3. Control Templates في WPF

3.1 فهم مفهوم Control Templates في WPF

Control Templates هي آلية في WPF لإعادة تعريف البنية البصرية لعناصر التحكم. بدلاً من استخدام الشكل الافتراضي لعناصر التحكم، يمكنك إنشاء شكل مخصص يناسب متطلبات التطبيق.

<ControlTemplate TargetType="Button">
    <Border Background="{TemplateBinding Background}" BorderBrush="Black" BorderThickness="2">
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Border>
</ControlTemplate>

في المثال أعلاه، قمنا بتعريف قالب مخصص (Template) للزر، حيث يتم استخدام Border لتحديد الإطار الخارجي، ويتم عرض محتوى الزر باستخدام ContentPresenter. الخاصية TemplateBinding تربط خصائص القالب بخصائص عنصر التحكم الفعلي.

3.2 استخدام VisualStateManager مع Control Templates

يمكنك استخدام VisualStateManager داخل ControlTemplate لتعريف الحالات البصرية المختلفة لعناصر التحكم. على سبيل المثال، يمكنك تحديد الحالة عند النقر على الزر أو تمرير الفأرة فوقه:

<ControlTemplate TargetType="Button">
    <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="Black" BorderThickness="2">
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Border>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal" />
            <VisualState x:Name="MouseOver">
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="Yellow" Duration="0:0:0.3" />
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</ControlTemplate>

4. Themes في WPF

4.1 إنشاء وتطبيق Themes مخصصة

الثيمات (Themes) في WPF تُستخدم لتغيير المظهر الكامل للتطبيق. يمكنك إنشاء ثيمات مخصصة باستخدام Resources مثل الألوان، Styles، وقوالب التحكم (Control Templates). يمكنك أيضًا إنشاء نظام لتبديل الثيمات أثناء تشغيل التطبيق، مما يوفر تجربة مستخدم أكثر مرونة.

4.2 تبديل الثيمات أثناء وقت التشغيل

لتبديل الثيمات أثناء تشغيل التطبيق، يمكنك تغيير الموارد الديناميكية في وقت التشغيل. على سبيل المثال، لتبديل لون الخلفية بناءً على ثيم داكن أو فاتح:

Application.Current.Resources["PrimaryColor"] = new SolidColorBrush(Colors.DarkGray);

لتحقيق تبديل كامل للثيمات، يمكن استخدام ResourceDictionary وتغييرها في وقت التشغيل:

private void ChangeTheme(string theme)
{
    var dict = new ResourceDictionary();
    switch (theme)
    {
        case "Dark":
            dict.Source = new Uri("DarkTheme.xaml", UriKind.Relative);
            break;
        case "Light":
            dict.Source = new Uri("LightTheme.xaml", UriKind.Relative);
            break;
    }
    Application.Current.Resources.MergedDictionaries.Clear();
    Application.Current.Resources.MergedDictionaries.Add(dict);
}

5. تمرين عملي: تصميم نموذج تسجيل دخول مع الثيمات

في هذا التمرين العملي، سنقوم بإنشاء نموذج تسجيل دخول يحتوي على عناصر تحكم مخصصة (Custom Styled Controls) باستخدام Styles وControl Templates. سنضيف آلية لتبديل الثيمات (Theme Switcher) بين الوضع الفاتح والداكن، مما يوفر تجربة مستخدم ديناميكية.

5.1 إنشاء قواميس الموارد للثيمات

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

إنشاء ملف LightTheme.xaml:

  • انقر بزر الفأرة الأيمن على المشروع في Solution Explorer.
  • اختر Add > New Item….
  • اختر Resource Dictionary من القائمة، واسم الملف “LightTheme.xaml”.
  • أضف المحتوى التالي إلى الملف:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Color x:Key="PrimaryColor">White</Color>
    <SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}" />
    <!-- Add other styles and resources for light theme -->
</ResourceDictionary>

إنشاء ملف DarkTheme.xaml:

  • كرر الخطوات السابقة، ولكن اسم الملف هذه المرة “DarkTheme.xaml”.
  • أضف المحتوى التالي إلى الملف:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Color x:Key="PrimaryColor">Black</Color>
    <SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}" />
    <!-- Add other styles and resources for dark theme -->
</ResourceDictionary>

5.2 تصميم نموذج تسجيل الدخول

سوف نصمم واجهة المستخدم لنموذج تسجيل الدخول باستخدام DynamicResource للإشارة إلى موارد الألوان التي ستتغير بناءً على الثيم المختار.

<Window x:Class="LoginForm"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="نموذج تسجيل الدخول" Height="200" Width="300">
    <Grid Background="{DynamicResource PrimaryBrush}">
        <StackPanel>
            <TextBox Margin="10" />
            <TextBox Margin="10" />
            <Button Content="تسجيل الدخول" Margin="10"/>
            <Button Content="تبديل الثيم" Margin="10" Click="SwitchTheme_Click"/>
        </StackPanel>
    </Grid>
</Window>

5.3 إنشاء آلية تبديل الثيمات (الوضع الفاتح/الداكن)

سنقوم الآن بإنشاء دالة لتبديل الثيمات. تعتمد هذه الدالة على قواميس الموارد التي أنشأناها سابقًا وتقوم بتغييرها في وقت التشغيل.

public partial class MainWindow : Window
{
    private bool _isDarkMode = true;
    public MainWindow()
    {
        InitializeComponent();
    }

    private void SwitchTheme_Click(object sender, RoutedEventArgs e)
    {
        // Logic to determine current theme can be added here
        _isDarkMode = !_isDarkMode /* Determine the current theme */;            
        ChangeTheme(_isDarkMode ? "Light" : "Dark");
    }

    private void ChangeTheme(string theme)
    {
        var dict = new ResourceDictionary();
        switch (theme)
        {
            case "Dark":
                dict.Source = new Uri("DarkTheme.xaml", UriKind.Relative);
                break;
            case "Light":
                dict.Source = new Uri("LightTheme.xaml", UriKind.Relative);
                break;
        }
        Application.Current.Resources.MergedDictionaries.Clear();
        Application.Current.Resources.MergedDictionaries.Add(dict);
    }
}

5.4 تشغيل التطبيق وتجربة تبديل الثيمات

عند تشغيل التطبيق، يمكنك النقر على زر “تبديل الثيم” لتغيير مظهر التطبيق بين الوضع الفاتح والداكن. بهذه الطريقة، يمكن للمستخدم اختيار الثيم الذي يفضله أثناء تشغيل التطبيق.

WPF light theme example
Wpf dark theme example

يمكنك العثور على المشروع الكامل على GitHub من خلال الرابط التالي: WPF Light/Dark Mode Project.

الخلاصة

في هذا الدرس، تعلمنا كيفية استخدام Styles وControl Templates وResources في WPF لتخصيص واجهات المستخدم بشكل احترافي. قمنا أيضًا بإنشاء تمرين عملي لإنشاء نموذج تسجيل دخول مع آلية لتبديل الثيمات، مما يتيح تصميم واجهات مستخدم قابلة للتخصيص وفقًا لرغبة المستخدم. تعتبر هذه المهارات أساسية في تطوير واجهات WPF متقدمة وجذابة.

التعليقات

اترك تعليقاً