أنشئ تطبيق WPF جديد
سنقوم بالبداية بإنشاء عمودين داخل الشبكة الخاصة بالنافذة بحيث يكون الثاني بضعف حجم الأول وذلك بإدخال الكود التالي بعد <Grid> مباشرة وذلك في محرر XAML الخاص بنافذة المشروع
<Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="2*"/> </Grid.ColumnDefinitions>
كما سنقوم بقسم الشبكة إلى سطرين ولكن سنترك للبرنامج تحديد حجمهما ديناميكيا وذلك بإدخال الكود التالي بعد الكود السابق
<Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions>
سنضيف الآن بعض التحكمات للنموذج وسنبدأ بإضافة تحكم Label بحيث تكون محتوياته Street Address و التجانب الأفقي لليمين والشاقولي للوسط كما سنضيف صندوق نصوص يحتوي النص Enter Street Address وسيكون التجانب الأفقي لليسار والشاقولي للوسط وذلك بإدخال الكود التالي مباشرة بعد الكود السابق
<Label Content="Street Address" HorizontalAlignment="Right" VerticalAlignment="Center"></Label> <TextBox Text="Enter a street address" HorizontalAlignment="Left" VerticalAlignment="Center"></TextBox>
سنحدد الآن اسما لصندوق النصوص وليكن txtAddress وذلك بتحديد قيمة الخاصية x:Name إلى القيمة المطلوبة كما سنحدد مكانه في الشبكة أيضا بتحديد قيمة الخاصيتين Grid.Column و Grid.Row فيصبح الكود الخاص بصندوق النصوص على الشكل التالي بعد التعديل
<TextBox Grid.Column="1" Grid.Row="0" x:Name="txtAddress" Text="Enter a street address" HorizontalAlignment="Left" VerticalAlignment="Center"></TextBox>
كما سنقوم بتحديد مكان الـ Label في الشبكة باستخدام Grid.Column و Grid.row وذلك بنفس الطريقة السابقة إضافة إلى أننا سنقوم بربط الـ Lable بصندوق النصوص باستخدام الخاصية Target لـ Label فيصبح الكود الخاص بـ Label على الشكل التالي
<Label Grid.Column="0" Grid.Row="0" Target="{Binding ElementName=txtAddress, Mode=OneWay}"
Content="Street Address" HorizontalAlignment="Right" VerticalAlignment="Center"></Label>الآن سنقوم بإضافة زر أوامر باستخدام الكود التالي
<Button Grid.Column="1" Grid.Row="1" Content="Save Street Address" HorizontalAlignment="Left" VerticalAlignment="Top" />
أضف المحرف _ في بداية نص Contenet الخاص بـ Label لتصبح قيمة تلك الخاصية
Content="_Street Address"
وذلك حتى يمكننا استخدام الخاصية Target للانتقال مباشرة لصندوق النصوص باستخدام الاختصار Alt-S بما أننا وضعنا المحرف _ قبل الحرف S
شغل البرنامج واختبره ويكون قد أصبح كود xaml الخاص بالنافذة كما يلي
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test WPF Skinning" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Target="{Binding ElementName=txtAddress, Mode=OneWay}"
Content="_Street Address" HorizontalAlignment="Right" VerticalAlignment="Center"></Label>
<TextBox Grid.Column="1" Grid.Row="0" x:Name="txtAddress" Text="Enter a street address"
HorizontalAlignment="Left" VerticalAlignment="Center"></TextBox>
<Button Grid.Column="1" Grid.Row="1" Content="Save Street Address"
HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
</Window>انتقل إلى Solution Explorer وقم بفتح الملف Application.xaml كي نقوم بتحديد المظهر الخاص بأدواتنا هنا وسنبدأ بتحديد المظهر الخاص بـ Label وذلك بإدراج قسم Style التالي بعد <Application.Resources> مباشرة حيث سنقوم بتحديد قيمة اللون الأمامي وحجم الخط
<Style TargetType="{x:Type Label}" x:Key="LabelStyle">
<Setter Property="Foreground" Value="Blue"></Setter>
<Setter Property="FontSize" Value="12px"></Setter>
</Style>وسنقوم الآن بتحديد مظهرين فارغين لكلا صندوق النصوص وزر الأوامر بحيث لن يغيرا شيئا في الوقت الحالي على مظهر التحكمين وذلك بإدخال الكود التالي مباشرة بعد الكود السابق
<Style TargetType="{x:Type TextBox}" x:Key="TextBoxStyle"></Style>
<Style TargetType="{x:Type Button}" x:Key="ButtonStyle"></Style>وبهذا يصبح الكود الخاص بـ Application.xaml حتى الآن كالتالي
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<Style TargetType="{x:Type Label}" x:Key="LabelStyle">
<Setter Property="Foreground" Value="Blue"></Setter>
<Setter Property="FontSize" Value="12px"></Setter>
</Style>
<Style TargetType="{x:Type TextBox}" x:Key="TextBoxStyle"></Style>
<Style TargetType="{x:Type Button}" x:Key="ButtonStyle"></Style>
</Application.Resources>
</Application>ولربط هذه المظاهر الجديدة مع التحكمات نعود لكود xaml الخاص بالنافذة ونضيف الخاصية Style لكل من الـ Label و صندوق النصوص و زر الأوامر فيصبح الكود الخاص بالتحكمات الثلاثة على الشكل
<Label Grid.Column="0" Grid.Row="0" Target="{Binding ElementName=txtAddress, Mode=OneWay}"
Content="_Street Address" HorizontalAlignment="Right" VerticalAlignment="Center"
Style="{DynamicResource LabelStyle}"></Label>
<TextBox Grid.Column="1" Grid.Row="0" x:Name="txtAddress" Text="Enter a street address"
HorizontalAlignment="Left" VerticalAlignment="Center"
Style="{DynamicResource TextBoxStyle}"></TextBox>
<Button Grid.Column="1" Grid.Row="1" Content="Save Street Address"
HorizontalAlignment="Left" VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}"/>ونلاحظ هنا أننا استخدمنا DynamicResource في الخاصية Style لربط مع الشكل المراد حيث نحدد أن هذه الخاصية يمكن أن يتم تغييرها في زمن التشغيل ديناميكيا وأن المظهر المطلوب قد يكون موجودا وقد لا يكون موجودا بينما لو استخدمنا StaticResource في هذه النقطة سنخبر البرنامج أننا نتوقع وجود ذلك المظهر وأنه لن يتم تغييره
شغل البرنامج واختبره
أضف الخاصية Click لتعريف الزر وذلك لإضافة إجراء لمعالجة حدث النقر على الزر بحيث تكون على الشكل
Click="Button_Click"
ويصبح تعريف الزر في النهاية على الشكل
<Button Grid.Column="1" Grid.Row="1" Content="Save Street Address" Click="Button_Click"
HorizontalAlignment="Left" VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}"/>ثم انتقل لمحرر الكود الخاص بالملف Window1.xaml.vb وأضف إجراء معالجة حدث النقر على الزر وأضف استيرادا لـ System.IO و لـ System.Windows.Markup في بداية الملف وأنشئ إجراءا فارغا لحدث النقر على الزر كما يظهر في الكود التالي
Imports System.IO Imports System.Windows.Markup Class Window1 Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) End Sub End Class
اختر Add New Item من القائمة Project ثم أضف ملفا من النوع WPF Resource Dictionary للمشروع وقم بتسميته CrazyStyle.xaml وقم بتعديل الخاصية Copy to output directory الخاصة به إلى Copy if newer وأيضا قيمة الخاصية Build Action إلى Content حيث سنستخدمه كملف Skin للنافذة بحيث أننا سنستخدم الخاصية Dynamic Resources في WPF لتعمل كما لو أننا نستخدم Skin للمشروع وقم بتعديل محتويات كود xaml الخاص به ليصبح كما في الكود التالي حيث سيحتوي على الأشكال الخاصة بتحكمات نافذة المشروع وبنفس الطريقة التي استخدمناها منذ قليل
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Label}" x:Key="LabelStyle">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontSize" Value="16px" />
</Style>
<Style TargetType="{x:Type TextBox}" x:Key="TextBoxStyle">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Offset=".1" Color="AliceBlue" />
<GradientStop Offset=".5" Color="AntiqueWhite" />
<GradientStop Offset="1" Color="Aquamarine" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}" x:Key="ButtonStyle">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontSize" Value="16px" />
</Style>
</ResourceDictionary>الآن عد إلى إجراء معالجة الحدث Click للزر الذي أنشأناه سابقا وقم بتعديله ليصبح كما في الكود التالي الذي يقوم بتعريف المتغير rd ليكون من النوع ResourceDictionary ثم يستخدم FileStream لفتح الملف CrazyStyle.xaml للقراءة ثم نستخدم XamlReader.Load لقراءة محتويات الملف مستخدمين الدالة CType لتحويل نوع الناتج المقروء إلى ResourceDictionary ثم نقوم بوضع تلك القيمة في المتغير Rd الذي سنضبطه ليكون المصدر الحالي Current Resource للتطبيق وذلك بإسناد قيمته إلى Application.Current.Resources
Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim rd As ResourceDictionary = Nothing
Using fs As New FileStream("CrazyStyle.xaml", FileMode.Open, FileAccess.Read)
rd = CType(XamlReader.Load(fs), ResourceDictionary)
End Using
Application.Current.Resources = rd
End Subشغل التطبيق واختبره
يرجى عدم طلب إرفاق مثال مع الموضوع لأن الشرح واضح أولا وثانيا أريد أن أرى مثالا يعمل بشكل صحيح مرفق من قبل أحد الإخوة الذين قرأوا الموضوع