الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

دروة wPF من البداية (التركيز على MVVM)

مثبّترائج
بدأه زكيري عبد العالي في 2 أبريل 2011 · 31 رد · 21,911 مشاهدة · في SilverLight
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

ملاحضة الكود بالدلفي و السي شارب

السلام عليكم

اخواني كما عودتكم اني حينما اتعلم شئء جديد اكتب عنه منذو فقط يومين بدات العمل على wPF و هذا ما جعلني ارجع الى منتداي الغالي لكتابة دروس ان شاء الله مفيدة .

بعد التجول في عدت منتديات وجدة هذه المفاهيم :

مقدمة

ماهي wPF

كتابة تطبيقك Wpf Applicationi

MVVM

- المثال 1 في MVVM مثال بسيط

- المثال 2 في MVVM عرض قاءمة اشحاص

- تابع اضافة اكثر SKIN الى الواهة

* استعمال تعليمات في STYL

- تابع تعدد اللغات في الواجهة استعمال ملفات الموارد Ressources Files

- تابع اضافة واجه اخري تضهر العنصر المحدد في القاءمة

- تابع التحكم في الازرار و عمل onclick للحدف و اضافة ... سجل

- وضع validation control على الواجهة

- الاتصال بقاعدة الباينات بواسطة الهينرنت

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 19 يونيو 2011 في 23:47

3
#2

المثال الاول

1- انشاء مشروع جديد لاحظ الصورة

post-1034-024412900 1301765382_thumb.jpg

المرفقات
WPF-ex1-1.JPG
#3

Windows Presentation Foundation (WPF) Overview

[quote

]

رابطة هنا /index.php?showtopic=113042

سنناقش في هذا الموضوع تقنية WPF ومزاياها وكيفية البرمجة باستخدم اكواد XAML

احد شروط المشاركة في النقاش هو ان يكون لدى الشخص فكرة عن الموضوع وحسب معرفتي بهذة التقنية

اظنها ما زالت غريبة ويكسوها الغموض بالنسبة لاكثرية المبرمجين العرب ولا تكاد تجد مرجع واحد باللغة العربية يشرحها

بل لن تجد بين مواقع الانترنت العربية من يبعد ذلك الغموض عن هذة التقنية فرئيت ان واجبي العلمي ان ابعد ذلك الغموض

واكون نقطه البداية لمن يريد تعلم هذة التقنية وتهتم هذة التقنية بجانب Design ويعتبر احد اهم اسباب نجاح النظام من فشله

ولقد قمت باعداد هذة المقدمة البسيطة كي يتم النقاش بمعرفة جيدة لما يتم النقاش عنه كي نحصد اكبر فائدة مرجوة من الموضوع

وكي يشارك ويستفيد من النقاش اكبر عدد من الاعضاء وبعد قرئة المقدمة اجبني عن هذة الاسئلة وناقشني فيما لفة الغموض فيما قرئتة

1- لماذا لايوجد الكثير من المواضيع عن هذة التقنية باللغة العربية حتى الان ؟

2- هل تعرفت الى مميزات هذة التقنية مقارنة مع WinForm من الافضل بنظرك ولماذا ؟

3- ماهو الذي مازلت تبحث عنة في تصميم واجهات العرض ولم تجدة بهذة التقنية ؟

4- هل تعتقد ان لواجهات العرض تاثير في بيع النظم ام انها شئ ثانوي ؟

5- ماهو الذي ينقصك حتى الان للبدء في تعلم هذة التقنية ؟

الجواب بقلم samerselo مراقب اداري

1- لماذا لايوجد الكثير من المواضيع عن هذة التقنية باللغة العربية حتى الان؟

أعتقد لقلة المهتمين بتتبع التقنيات الجديدة وإن وجد فإن المهتمين المتقدمين تكون لغتهم الانكليزية جيدة ويستقون المعرفة من مصادرها الأجنبية

2 - هل تعرفت الى مميزات هذة التقنية مقارنة مع WinForm من الافضل بنظرك ولماذا؟

تتميز هذه التقنية بالسهولة والديناميكية حيث يمكنك عمل أي تصميم يخطر ببالك والذي يسهل هذا الأمر هو أن كل تحكم هو Conatiner أي أننا يمكننا وضع أي تحكمات نريد داخله

ستكون تقنية WPF أفضل من WinForms مستقبلا عندما يتم توفير مجال واسع من التحكمات تخدمها بمستوى مقارب للتحكمات الموجودة للتقنية الأخرى وذلك بسبب مرونة تصميم الواجهات

4 - هل تعتقد ان لواجهات العرض تاثير في بيع النظم ام انها شئ ثانوي ؟

طبعا تصميم واجهات العرض له أثر كبير في اقبال المستخدم على البرنامج وخاصة عندما تكون جميلة ومنظمة بشكل منطقي يسهل تذكره واستخدامه

5 - ماهو الذي ينقصك حتى الان للبدء في تعلم هذة التقنية ؟

بدأت فعلا ببعض التجارب على هذه التقنية ومشكلتي الرئيسية هي في تصميم الواجهات باستخدام Blend لأنه لا يمكنني إبداع واجهات رسومية مع أنه يمكنني برمجة أي شئ أحتاجه تقريبا

------------------------ لماذا لايوجد الكثير من المواضيع عن هذة التقنية باللغة العربية حتى الان ؟

بل توجد, ولكن قليلة ولا تغطي التقنية من كافة جوانبها.

قلة الخبرات العربية بخصوص هذه التقنية حتى الآن, وحتى إن كانت توجد بوفرة فإن مخاضها العلمي لن يظهر إلا بعد مضي فترة لا بأس بها.

المبرمج العربي هذه الأيام تحت تحدً كبير خصوصأ أمام قلة المصادر العربية التي تتحدث عن التقنيات الجديدة, لذلك يفترض به أن يتجه مباشرة نحو المصادر الأجنبية للتعلم, وألا يبقى مكتوف الأيادي ينتظر الفرج! عليه أن يتحرك حتى وإن تطلب الأمر أخذ كورس في اللغة الإنجليزية.

2- هل تعرفت الى مميزات هذة التقنية مقارنة مع WinForm من الافضل بنظرك ولماذا ؟

أجل تعرفت عنها منذ فترة , وبالتأكيد هي أفضل من WinForms لعدة أسباب:

أنها حلّت الكثير من المشاكل التي كانت تتسبب فيها WinForms بالإضافة إلى دعمها الغني لتعدد الوسائط , الأمر الذي جعلنا في WinForms سابقاً نعتمد على مكتبات خارجية مثل WMP و DirectX Libraries, نفس الأمر بالنسبة للمستندات جيث كنا نعتمد على دائمًا مكوانت خارجية Third Party Components الأمر الذي يستبب في الكثير من المتاعب.

أيضًا اختفت مشاكل Threading التي كنا تواجهنا في WinForms بفضل الاعتماد على Dispatcher Object.

خلاصة القول: هذه التقنية جمعت كل المتطلبات التي يحتاجها أي مبرمج مهما كانت حاجته أو اهتمامه البرمجي.

3- ماهو الذي مازلت تبحث عنة في تصميم واجهات العرض ولم تجدة بهذة التقنية ؟

أعتقد أن الوقت لازال مبكرًا للإجابة عن هذا السؤال .

4- هل تعتقد ان لواجهات العرض تاثير في بيع النظم ام انها شئ ثانوي ؟

بالتأكيد , أعتقد أن الاعتماد على هذه التقنية في هذا الوقت خصوصاً والذي لا زالت تتنافس فيه تطبيقات ال WinForms في سوق البرمجيات سيحقق رواجاً ومكسبًا كبيرًا لمن يبرمج بهذه التقنية فهي ستقدم تجربة جديدة ونوعية للمستخدم , ولكن للأسف لم يستغل الكثيرون هذه الفرصة.

5- ماهو الذي ينقصك حتى الان للبدء في تعلم هذة التقنية ؟

في الواقع, لست مهتماً هذه الأيام بتطوير التطبيقات المكتبية , ولكن مهتم بتطوير المكتبات و المكونات Components واعتمادي على WPF ينحصر فقط في تجربة تلك المكونات للتحقق من عملها بالشكل المطلوب.

اقتباس
مقدمة الى WPF

Windows Presentation Foundation (WPF) وهي أعظم خطوة في تطوير واجهات المستخدم

قامت بها Microsoft وتعتبر الجيل ألرسومي الجديد لنظم العرض على كلتي منصات العمل

Windows- web-based applications

وتقنيته WPF الرسومية تعتمد على DirectX أي أنها اخف وأسرع للترجمة من GDI/GDI+

اذا أصبحت واجهات المستخدم تمتلك كل الإمكانيات المتاحة على اجهزة العرض (video cards)

وتمثل WPF اتحاد بين الشكل والوظيفة ومنافس قوي ل Flash , HTML وتوفر الحريه التامة

والسهولة في التصميم وتتطلب وجود (Framework 3.0) أو اعلي منه.

بقلم الاخ المخلب الجارح

الرابطة

اقتباس
AliBazzi

الـ WPF عندما ظهر كانت مايكروسفت تحاول حشره أيضا في الويب , وما زلت أذكر المشروع الذي عملت عليه قبل 3 سنوات بالـ WPF لكن كتطبيق يعمل في المتصفح (تستطيع عمل ذلك الاّن عن طريق القالب الذي يحمل إسم WPF Browser Application ) ,لكن التعقيد الذي قٌدم من خلالها في الحاجة إلى Certificate لكي يعمل كـ Full Trust Application لا يطاق , وإلا ستكتفي بتطبيق WPF يعمل في الـ Browser بإمكانات ضعيفة للغاية , عندها Microsoft قدمت Silver light الذي كان يشبه في إصدارته الأولى Mini WPF يعمل في المتصفح فقط , لكن أهم ميزة هي قدرته العمل على جميع المنصات Windows , Linux , Mac ...

وبالتالي , دعت الحاجة للعمل على فكرة عبور المنصات في الإصدارات المقبلة للـ Sliver light اكثر و أكثر

طيب , جميل , اليوم ما هو موقع كل من WPF و Silver light ?

ببساطة Silver light يحقق اليوم ما عجز الـ .Net نفسه عن تحقيقة (ليس لعجز فيه , ولكن لقرار "إستراتيجي" من مايكروسوفت) , ألا و هو عبور المنصات , Silver light حاليا ليس إلا Mini .Net يعمل في كل المنصات و مدعوم من مايكروسوفت (وهو يكبر يوم بعد يوم ) وبالتالي , إذا أدرت الـ Web فلا أرا أفضل من Silver light و الـ WPF يقف عاجز هنا لأن الـ WPF Browser Application يعمل فقط في بيئات Windows , إذا ما تجاهلنا التعقيد الناجم طبعا عن تشغيله كـ Full Trust Application ...

وأخيرا شيء ملفت للنظر حقا , قدمت Microsoft في PDC 2009 الـ Silver light 4 الذي يعمل كـ Desktop Application في كل المنصات , هذا بحد ذاته إيمان من مايكروسوفت نفسها في أن الـ Silver Light سينظر له عاجلا أم اّجلا كـ Better .net يعمل في كل المنصات , عكس الـ .Net الذي نعرفه أتمنى أن تكون قد وضحت الأمور

هنالك نوعان من Application ضمن WPF

1- standalone applications

المشاريع المستقلة التى يتم استخدامها ضمن جهاز واحد ونستخدم معها الكائن Window

2- Browser applications

وهي المشاريع التى يتم استخدامها ضمن شبكة كالانترنت مثلا ونستخدم معها الكائن Page , NavigationWindow

كتابة تطبيقك Wpf Application

محمد سامر أبو سلو

تصميم تطبيق WPF لا يختلف كثيرا عن تصميم تطبيق Windows Forms حيث يمكنك إضافة التحكمات على سطح التصميم ومع ذلك فهناك بعض الاختلافات فبالإضافة لنوافذ التصميم المألوفة ستجد نافذة

اقتباس
إضافية تعرض كود XAML حيث تستخدم هذه اللغة لإنشاء واجهة المستخدم. فعندما تقوم بتطوير تطبيقات النوافذ التقليدية تقوم بسحب التحكمات من الـ ToolBox أو إن رغبت يمكنك كتابة كود لإنشاء تلك التحكمات فعندما تقوم بسحب التحكم إلى النموذج يتم إنشاء الكود آليا من أجلك وبشكل مشابه عندما تقوم بإنشاء تطبيق WPF يمكنك إنشاء التحكم بكتابة كود XAML أو بسحب التحكم إلى نافذة WPF.

ولغة XAML منظمة إلى عناصر ببنية شجرية مشابهة لطريقة كتابة ملفات XML حيث تجد أن العنصر مضمن ضمن قوسين حادين وهناك تحديد فتح وتحديد إغلاق لكل عنصر فمثلا قد يكون لديك عنصر Button يمكن أن يظهر بالشكل <Button></Button> ويمكنك أن تصف كيف سيبدو ذلك العنصر بضبط الخصائص attributes كـ Location أو Height أو Width وهي تظهر داخل الأقواس الافتتاحية للعنصر بشكل زوج اسم وقيمة مفصولين بعلامة = حيث تكون القيمة محصورة ضمن علامتي تنصيص مثل

<Button Height="23"></Button>

وعندما تقوم بسحب تحكمات WPF من الـ ToolBox إلى نافذة المصمم تولد بيئة التطوير كود XAML آليا لذلك التحكم فالكود التالي يتم توليده نتيجة عملية إضافة تحكم من النوع System.Windows.Controls.Button

<Button Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" 
	Name="Button1" VerticalAlignment="Top" Width="75">Button</Button>

اقتباس
دعنا نقم ببعض التجارب تطبيقا على ما ورد سابقا:

• أنشئ تطبيقا جديدا من النوع WPF Application و سمه WPFWindow ثم اضغط OK فيتم إنشاء تطبيق WPF جديد من أجلك وسترى قسم XAML جديد تطبيقات WPF وستبدو محتوياته كالتالي

<Window x:Class="Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="Window1" Height="300" Width="300">
	<Grid>

	</Grid>
</Window>

اقتباس

• انقر على Window1 لاختيارها وفي نافذة محرر XAML غير قيمة الخاصية Title إلى WPF Application فنلاحظ تغير نص عنوان النافذة إلى WPF Application كما يمكنك تجريب تغيير قيمة بعض الخصائص الأخرى

• قم بسحب TextBox من الـ ToolBox وضعه في الزاوية اليمينية العليا لنافذة التطبيق ثم انقر عليه لاختياره وفي نافذة الخصائص اضبط قيمة HorizontalAlignment إلى Left ثم قم بضبط الخاصية VerticalAlignment إلى Top والخاصية Width إلى 75 والخاصية Height إلى 26

• في محرر XAML قم بتغيير الخاصية Width إلى 140 والعنصر Margin إلى 30, 56, 0, 0 كما يبدو في المثال

<TextBox Height="26" HorizontalAlignment="Left" Margin="30,56,0,0" 
	Name="TextBox1" VerticalAlignment="Top" Width="140" />

اقتباس
• قم بسحب تحكم Button إلى سطح نافذة التطبيق وقم بالنقر المزدوج عليه لإنشاء إجراء معالجة الحدث Click الخاص به ثم اكتب الكود التالي في الإجراء

MsgBox("Event handler was created by double-clicking the button.")

• أضف تحكم Button آخر للنافذة وقم باختياره ثم قم بإضافة خاصية باسم Click للعنصر Button في محرر كود XAML واضبط قيمتها إلى ButtonOKClicked وهذا الاسم سوف تعطيه لإجراء معالجة الحدث في الكود مع ملاحظة أنه عندما تنشئ إجراء معالجة الحدث بالنقر المزدوج لا يتم إضافة الخاصية Click إلى كود XAML ولكن يستخدم في الكود عبارة Handles عوضا عن ذلك

• انقر بزر الفأرة اليميني على نافذة التصميم واختر ViewCode ثم قم بإضافة الإجراء التالي

Sub ButtonOKClicked(ByVal Sender As Object, _
	ByVal e As RoutedEventArgs)

	MsgBox("Event handler was created manually.")

End Sub

بعد هذه المعلومات الجميلة يمكننا الدا بالامثل التطبيقية

شرح الواجهة الاولى ل vs2010

post-1034-008960900 1301765984_thumb.jpg

المرفقات
WPF-ex1-2.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 18 يونيو 2011 في 19:07

#4

نضع العنصر Label

post-1034-093452800 1301766490_thumb.jpg

المرفقات
WPF-ex1-3.JPG
#5

و الان سنحاول عرض خاصية لصنف نضيفه في Code-Behind

نغير في

1-----------------------------

XAML

-------------------------------

<Grid>
        <Label 
            Name="label1" 
            Content="{Binding Path=GetLabelContent}"
            Margin="30,50,30,50" 
       />
  </Grid>

----------------------------2

Window1.xaml.asp

------------------------------

namespace WPFApplication1;

interface

type
  //انشاء صنف جديد باسم 
  HalloWordClass = public class
      public property GetLabelContent: string read 'Hallo Word';
  end;
  //التعريف بصنف الواجهة 
  Window1 = public partial class(System.Windows.Window)
  public constructor;
  end;

implementation

constructor Window1;
begin
    InitializeComponent();
    DataContext := new HalloWordClass();
end;

end.

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 2 أبريل 2011 في 21:32

#6

مــجهود جبار .. تشكر عليه كثيراً أخي العزيز

سجلني متابعاً عنـدك ...

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 3 أبريل 2011 في 00:26

#7

ربط البيانات DataBinding في WPF

اقتباس

بقلم الاستاذ Islam Ibrahim مشرف المنتدى VB

رابطه الدرس http://vb4arab.com/vb/showthread.php?t=70850

الافتراضي WPF الأساسيات : ربط البيانات DataBinding في WPF

توضح هذه المقالة خطوة بخطوة كيف يمكنك ربط مصادر البيانات Data Sources مع أدوات تحكم WPF Controls - WPF وصفيًا من خلال لغة XAML., أو يدويًا (إجرائيًا) من خلال الكود, هذه المقالة تركّز بشكل أكبر على إيضاح مبادئ ربط البيانات في WPF بشكل عام. ولا تُناقش كيفية استخدام مصادر البيانات بشكل خاص, محتويات المقالة كالتالي:

* مقدمّة

* نظام التبعيّة Dependency System في WPF, ومفاهيم ربط البيانات الأساسية

* ربط البيانات من خلال XAML

* ربط البيانات إجرائيًا

* أوضاع الربط والتحديث Data Binding/Updating Modes

* خيارات الربط Data Binding Options

مقدمة

ربط البيانات Data Binding هو آلية تسمح بتكوين ارتباط بين مصادر البيانات مع واجهة الاستخدام المرئية Graphical User Interface, مصادر البيانات قد تختلف حسب طبيعة التطبيق, عادةً مصادر البيانات تقيم في إحدى طبقات التطبيق والتي تسمّى Business Logic Layer, هذا الارتباط يُمكن أن يكون أُحادي الاتجاه أو ثنائي الاتجاه, في الوضع الأول يمكن فقط لأحد الطرفين (مصدر البيانات أو عنصر تحكم واجهة الاستخدام) التأثير على الطرف الآخر, وفي الوضع الثاني يكون التأثير بينهما متبادلاُ.

في WPF عادةً ما يُشير المُصطلح Data Source إلى كائن في بيئة الدوت نت( .NET object ) هذا الكائن قد يكون مجموعة Collection مثل Lists أو Arrays , ملف XML, أو كائن خدمة ويب Web service object, أو كائن يشير إلى جدول في قاعدة بيانات Data table, أو كائن POCO بسيط ((Plain Old CLR Object, أو حتى عنصر تحكّم من عناصر WPF مثل Button .

نظام التبعيّة Dependency System في WPF, ومفاهيم ربط البيانات الأساسية

أضاف WPF نوعًا جديدًا من الخصائص, وهي خصائص التبعيّة Dependency Properties, والتي تم إدخالها لتبسيط المهام التي من أجلها تم تصميم WPF, مثل تنسيق المظهر Styling, ربط البيانات التلقائي Automatic Data Binding, التحريك Animation, والمزيد, قد تبدو هذه الإضافة غامضةُ بعض الشيء بالنسبة لك, لأنها ستُعقّد نوعًا ما الانطباع الذي أخذته عن كائنات Microsoft.NET الكلاسيكيّة: مجرّد حقول Fields, وإجرءات Methods وخصائص Properties بسيطة, لكن بمُجرّد أن تفهم المشاكل التي يحُلها نظام التبعيّة , ستُرحب كثيرًا بهذه الإضافة.

خصائص التبعيًة Dependency Properties تعتمد في الحصول على القيم الخاصة بها على مًوفّر القيمة Value Provider, وقد تعتمد على أكثر من مُوفّر واحد, وقد يكون هذا المُوفّر إما تحريكًا Animation يقوم تلقائيًا بتغيير قيمة تلك الخاصيّة, أو عنصر أبوي Parent Element ينشر قيم خصائصه نحو العناصر الأبناء Child Elements, وهكذا. من أهم ميزات خصائص التبعيّة هو اعتمادها على النظام المبني على التنبيه عند حدوث تغيّر في القيم التي تحملها Change Notifications.

الدافع وراء هذه الإضافة هو تمكين الميزات الغنيّة التي يُقدّمها WPF مباشرةً من خلال لغة تمييز التطبيقات القابلة للامتداد XAML, وما يجعل WPF يعتمد بشكل كبير على استخدام لغة التمييز هذه هو مبالغة نظام WPF في استخدام الخصائص, على سبيل المثال, عنصر التحكّم Button يحتوي على 111 خاصيّة (منها 98 موروثة من النوع Control وفئاته القاعدية), يُمكن تعيين قيم الخصائص لعناصر WPF بسهولة من خلال XAML (بطريقة مباشرة أو باستخدام أدوات التصميم مثل مُصمّم WPF المُضمن مع Visual Studio) , ولكن من دون اللجوء لكتابة بعض الكود الإضافي لن يكون من المُمكن الحصول على النتيجة المُنتظرة فقط بمجرد إسناد بعض القيم من خلال XAML, فلا تتوّقع أنك ستحصل على ميزات خصائص التبعيّة مثل الربط التلقائي باستخدام الخصائص العادية CLR Properties فقط.

كيفية تعريف خصائص التبعيّة

في الواقع كمطوّر WPF لست مُجبرًا على الإلمام بجميع التفاصيل الدقيقة لكيفية تعريف وكتابة خصائص التبعيّة, إلا إذا كنت تخطّط لكتابة أدوات تحكّم خاصة بك Custom Controls, وبشكل عام فإنك تحتاج فقط لفهم كيفية عملها, ومع مرور الوقت ستتمنّى لو كانت جميع الخصائص هي خصائص تبعيّة!

لغات عائلة Microsoft.NET في الواقع لا تتفهّم خصائص التبعيّة, لهذا فإن واجهة برمجة WPF - WPF APIs هي المسؤولة عن إدارة تلك الخصائص, ولهذا السبب عمليًا ستحتاج لاستخدام بعض الفئات الأساسيّة Base Classes الموجودة في WPF APIs من أجل كتابة خصائص التبعيّة, المثال التالي هو أبسط شكل لما يُمكن أن تكون عليه خاصية التبعيّة, وهو عبارة عن تعريف للخاصية Content الموجودة في عنصر التحكّم Button والعناصر الأخرى الموروثة من النوع ContentControl.

namespace ;

interface

type
  ContentControl = public class(Control, IAddChild)
  public class var  ContentProperty: DependencyProperty := DependencyProperty.Register('Content', typeof(Object), typeof(ContentControl),
                    new FrameworkPropertyMetadata(nil, new PropertyChangedCallback(ContentControl.OnContentChanged))); readonly;
  public  property Content: Object read inherited GetValue(ContentProperty) write set_Content;
  public  method set_Content(value: Object);
  private class method OnContentChanged(d: DependencyObject; e: DependencyPropertyChangedEventArgs);
 end;

implementation

method ContentControl.set_Content(value: Object); 
begin
  inherited SetValue(ContentProperty, value);
end;

class method ContentControl.OnContentChanged(d: DependencyObject; e: DependencyPropertyChangedEventArgs);
begin
    ...
end;

الحقل المُشترك ContentProperty هو الذي يُمثّل خاصية التبعيّة, وهو من النوع System.Windows.DependencyProperty, وبالإجماع, جميع الحقول التي تُمثّل خصائص التبعيّة, تكون عامة public, ومُشتركة static, تنتهي تسميتها بالكلمة Property, منصّة WPF الأساسيّة تتطلب اتباع هذا الأسلوب في التعريف للعديد من الأغراض مثل أدوات الترجمة Localization, وأثناء القيام بتحميل ومُعالجة XAM, فإن WPF يخضع في معالجته لهذا الأسلوب.

وفي العادة يتم إنشاء خصائص التبعيّة باستدعاء الإجراء DependencyProperty.Register والذي يتطلب المُدخلات التالية: اسم الخاصًية متبوعًا بالكلمة Propertyكما في المثال السابق(ContentProperty), نوع الخاصيّة (object), ونوع الفئة الحاوية على الخاصيًة (ContentControl), يُمكن اختياريًا باستخدام أحد ال Overloads للإجراء Register السابق تحديد كيفية مُعالجة WPF لتلك الخاصيّة, وكذلك الإجراءات CallBacks التي سيتم استدعاءؤُها عند تغيّر قيمة الخاصيّة, وكذا بعض المعلومات الأخرى. في التعريف السابق تم استدعاء الإجراء Register لإسناد القيمة null للخاصية Content, وتمرير مُفوّض Delegate يشير إلى الإجراء OnContentChanged من أجل التنبيه عند تغيّر قيمة الخاصية Content.

ثم بعد ذلك تأتي الخاصيّة Content والتي يتم من خلال إجرائيها get و set استدعاء الإجرائين GetValue و SetValue على التوالي, والموروثة من النوع System.Windows.DependencyObject, الفئة المنخفضة المستوى, والتي ترث منها جميع الفئات التي تحتوي على خصائص التبعيّة, الإجراء GetValue يقوم باسترداد القيمة التي تمّ تخزينها باستخدام الإجراء SetValue, وفي حال لم يتم استدعاء هذا الإجراء (SetValue) مُسبقًا, سيتم استرداد القيمة الافتراضية والتي تمّ تمريرها للإجراء DependencyProperty.Register. وفي الواقع لست بحاجة لتعريف الخاصية Content, لأنه من المُمكن استدعاء الإجرائين GetValue و SetValue فهما إجراءان عامّان, ولكن استخدام الخصائص يجعل استهلاك خصائص التبعيّة مألوفًا لدى مستخدم هذه الخصائص, ومن جهة أخرى فهي تُمكّن من إسناد الفيم لها من خلال XAML. وقد تستغرب لما لا يستخدم الإجراءان Get/SetValue ال Generics, والسبب مبدئيًا هو ظهور مفاهيم خصائص التبعيّة قبل ظهور Generics.

من جهة أخرى قد تستغرب لما يتم تعريف حقول خصائص التبعيّة على أنها مشتركة static, وحتى تفهم ذلك تخيّل العكس, ماذا لو تم تعريفها على أنها Instance Fields عندها ستحتاج من أجل كل عنصر أو مثيل Instance من Button حوالي 111 حقلاً أي 111 قيمة, ومن كل مثيل من Label حوالي 104 حقلاً أي 104 قيمة, وهكذا... عندها سيزيد استهلاك الذاكرة بشكل مريع! لكن خصائص التبعيّة جاءت لتحل هذه المشكلة, حيث أن الإجرائين Get/SetValue يستخدمان أسلوب فعّال واقتصادي لمنع هذا الاستهلاك الفضيع للذاكرة, حيث هناك من 89 خاصية تبعيّة من بين 111 خاصيّة للنوع Button, و 82 من بين 104 للنوع Label وهذا سيُحسّن بشكل كبير كميّة استهلاك الذاكرة, لا تسن أن الحقول المشتركة Static Fields يتم حفظ قيمها على مستوى النوع وليس على مستوى مثيل ذلك النوع Per-type values .

فوائد خصائص التبعيّة تتجاوز تحسين استهلاك موارد النظام من الذاكرة, وتتعدّى ذلك إلى عزل المطوّر عن الكثير من الأعمال الشاقّة مثل التحقّق من وصول المسارات Threads Access, أو مطالبة العنصر الأبوي Parent Element الذي يحتوي على عنصر تحكّم ما بإعادة رسم نفسه, على سبيل المثال, تغيير قيمة الخاصيًة Background للعنصر Button, يحتاج لمطالبة هذا الأخير بإعادة رسم نفسه, وهذا مًمكن من خلال تمرير القيمة FrameworkPropertyMetaDataOptions.AffectRender للإجراء DependencyProperty.Register, بالإضافة إلى كل ذلك, هناك الفوائد الثلاثة الأخرى التي سيرد ذكرها فيما يلي.

1 - التنبيه بالتغيّرات Change Notification

عندما يحدث تغيّر في قيمة خاصية التبعيّة, يقوم WPF بتشغيل مجموعة من الإجراءات, بناءًا على كيفية تعريف تلك الخاصية, قد يكون هذا الإجراء إعادة رسم عنصر معيّن في واجهة الاستخدام, أو إحداث تغيّر في تصميم واجهة الاستخدام, تحديث ربط البيانات, والمزيد. وإن من أهم ما يُميّز نظام التنبيه بالتغيّرات هذا هو مُشغّلات الخصائص Property Triggers, والتي تسمح لك بتشغيل إجرائك الخاص عند حدوث التغيّر دون الحاجة لكتابة كود إجرائي, على سبيل المثال, قد تريد تغيير لون خلفية عنصر تحكّم Button عند مرور مؤشر الماوس فوقه, واسترداد القيمة السابقة عند مغادرة المؤشر, مبدئيًا ستحتاج للقيام بذلك إلى إرفاق إجرائين للحدثين MouseEnter و MouseLeave,

<Button MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave" />

أما باستخدام مُشغّلات الخصائص, فيُمكنك الحصول على نفس النتيجة دون الاستعانة بالكود الإجرائي, بل يُمكنك الاكتفاء فقط باستخدام XAML, كما يُوضّح المثال التالي:

 <Button>
            <Button.Style>
                <Style TargetType="Button">
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="Background" Value="Blue"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>

هذا المُشغّل يمكنه العمل بحسب قيمة الخاصية IsMouseOver التي تُصبح True في نفس الوقت الذي يتم فيه إطلاق الحدث MouseEnter, وتُصبح False في نفس الوقت الذي يتم فيه إطلاق الحدث MouseLeave, لاحظ أنك لست مُطالبًا بإعادة القيمة Background إلى قيمتها الابتدائيّة, سيقوم WPF بذلك تلقائيًا من أجلك!

,كما لاحظت فإنه ليس بوُسعك إسناد مُشغّل بشكل مُباشر إلى عناصر WPF, بل ستحتاج لتعريف مظهر Style جديد وتعريف هذا المُشغّل بداخله ومن ثم تعيين هذا المظهر إلى العنصر الهدف كما في المثال السابق.

2 - وراثة الخصائص Property Inheritance

وراثة الخصائص أو "وراثة قيم الخصائص" غير مُرتبطة مطلقًا بمفاهيم الوراثة في البرمجة المُوجهّة بالكائنات OOP, بل تعني انتقال قيمة خاصيّة معينّة من عنصر إلى آخر في شجرة العناصر Elements Tree, على سبيل المثال عندما تقوم بتحديد قيمة خاصيّة التبعيّة FontSize للإطار Window فإن هذه القيمة يتم توريثها لجميع العناصر الموجودة ضمن هذا الإطار مثل عناصر تحكّم Button, Label, إلى غير ذلك, جرّب لمثال التالي:

<Window x:Class="WPFDependencyOverview.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525"
        FontSize="18"
        SizeToContent="WidthAndHeight"
        Background="SkyBlue">
    <StackPanel>
        <TextBlock FontSize="36"
                   Text="Property Value Inheritance"
                   HorizontalAlignment="Center" />
        <TextBox HorizontalAlignment="Center"
                 Width="200"
                 Text="Test"
                 TextAlignment="Center" />
        <Button Content="Click me!"
                HorizontalAlignment="Center" />
    </StackPanel>
</Window>

لكن هذه ليست قاعدة في كل الحالات, فهناك بعض الحالات التي لا يمكن فيها لعنصر أبوي أن يُورّث قيم أحد خصائصه لأحد عناصره الأبناء, في هذه الحالة إما أن العنصر الإبن يأخذ قيمته من مُوفّر القيمة ذي الأولوية الأكبر من العنصر الأبوي, كما في المثال السابق حيث أن عنصر TextBlock لم يتأثر بحجم الخط الذي تمّ تعيينه للإطار لأنه قد تم تعيين قيمته المحليّة Local Value إلى 36 بكسل والتي لها أولوية أعلى من العنصر الأبوي (Window), بينما تأثر كلّ من Button و TextBox بالقيمة الأبوية 18 بكسل, وإما أن الخاصيّة نفسها لم يتم تكوينها بالشكل الذي يجعلها قادرةُ على توريث قيمتها. في العادة يُمكن الحصول على هذا السلوك (توريث القيمة) من خلال تمرير القيمة FrameworkPropertyMetaDataOptions.Inherits إلى الإجراء DependencyProperty.Register .

capturenh.png

لكن هذه ليست قاعدة في كل الحالات, فهناك بعض الحالات التي لا يمكن فيها لعنصر أبوي أن يُورّث قيم أحد خصائصه لأحد عناصره الأبناء, في هذه الحالة إما أن العنصر الإبن يأخذ قيمته من مُوفّر القيمة ذي الأولوية الأكبر من العنصر الأبوي, كما في المثال السابق حيث أن عنصر TextBlock لم يتأثر بحجم الخط الذي تمّ تعيينه للإطار لأنه قد تم تعيين قيمته المحليّة Local Value إلى 36 بكسل والتي لها أولوية أعلى من العنصر الأبوي (Window), بينما تأثر كلّ من Button و TextBox بالقيمة الأبوية 18 بكسل, وإما أن الخاصيّة نفسها لم يتم تكوينها بالشكل الذي يجعلها قادرةُ على توريث قيمتها. في العادة يُمكن الحصول على هذا السلوك (توريث القيمة) من خلال تمرير القيمة FrameworkPropertyMetaDataOptions.Inherits إلى الإجراء DependencyProperty.Register .

3 - إمكانيّة تعدّد مُوفّري القيمة Multiple Value Providers Support

تمّ تسمية خصائص التبعيّة Dependency Properties بهذا الإسم لانها تعتمد في الحصول على قيمهتا على مجموعة من موفّري القيمة Value Providers يعتمد WPF على آلية مُعقّدة بعض الشيء , وتتمّ هذه الآلية وفق ترتيب معيّن وفي مراحل. يمُّر WPF حاليًا على 5 مراحل من أجل تحديد تلك القيمة النهائيّة التي ستحملها الخاصيّة, يُمكن تلخيص تلك المراحل فيما يلي:

المرحلة الأولى: تحديد القيمة الابتدائية Base Value:

يشترك أغلب مُوفّري القيمة في عملية احتساب القيمة الابتدائية لخاصيّة مُحدّدة وفق ترتيب مُعيّن يخضع فيه هذا الترتيب لمبدأ الأسبقية (ما يُسمّى بـ Property Precedence), حيث أن مُوفّر القيمة ذي أعلى أسبقية هو الأحق بأن يُعطي قيمته لتلك للخاصية, يُمكن اختصار هذا الترتيب في القائمة التالية للقيم الناتجة من المُوفّر ذو الأسبقية العُليا إلى المُوفّر ذي الأسبقية السُفلى.

* القيمة المحليّة Local Value لخاصيًة العنصر نفسه.

* القيمة التي تمّ تحديدها في مُشفّل خاصيًة في Template العنصر الأبوي (Parent Tempate Trigger).

* القيمة التي تمّ تحديدها في Template العنصر الأبوي (Parent Template).

* القيمة التي تمّ تحديدها في مُشغل خاصية في Style العنصر نفسه (Style Trigger), كما يظهر ذلك في المثال الثاني في هذا المقال والخاص بخلفية Button .

* القيمة التي تمّ تحديدها في مُشغّل خاصيّة في Template العنصر نفسه (Template Trigger).

* القيمة التي تمّ تحديدها في Style العنصر نفسه بواسطة Style Setter.

* القيمة التي تمّ تحديدها في مُشغّل خاصيّة في الـ Theme الحالي (Theme Style Trigger)

* القيمة التي تمّ تحديدها في Style خاص بالـ Theme الحالي بواسطة Style Setter أي (Theme Style Setter)

* القيمة التي تمّت وراثتها بواسطة ميزة Property Value Inheritance كما ورد في الفقرة السابقة "وراثة الخصائص".

* القيمة الافتراضية Default Value لخاصيًة العنصر نفسه (التي تم استخدامها عند تعريف خاصيّة التبعيّة من خلال الإجراء DependencyProperty.Register).

- بالنسبة لـ Triggers و Templates و Themes سيتم الحديث عنها لاحقًا بالتفصيل في موضوع مستقل.

إذا لم يكن بوسعك معرفة مُوفّر القيمة الحالي لخاصيًة مُحدّدة, يمكنك استخدام الإجراء المُشترك DependencyPropertyHelper.GetValueSource, كوسيلة مُساعدة لذلك, هذا الإجراء يُعيد بعض البيانت المفيدة, من بينها قيمة من النوع BaseValueSource Enum والتي تُحدد مصدر القيمة الفعلي, يُمكنك مراجعة MSDN Library لمعرفة المزيد حول هذا الإجراء.

المرحلة الثانية: اختبار القيمة Value Evaluating

إذا كانت القيمة العائدة من المرحلة السابقة هي عبارة عن تعبير Expression (كائن مُشتق من النوع System.Windows.Expression) , سيقوم WPF باختبار ذلك التعبير للحصول على قيمة حقيقية, ال Expressions تستخدم في الغالب من أجل ربط البيانات Data Binding وهو ما سنتحدّث عنه في تكملة هذا المقال.

المرحلة الثالثة: تطبيق التحريكات Apply Animations

إذا كان هناك تحريك Animation يتم تطبيقه حاليًا على العنصر, فإن لهذا التحريك القدرة على تغيير قيمة الخاصيّة لهذا العنصر, لهذا من المُمكن للتحريك أن يُلغي مفعول كافة مُوفرّي القيمة السابقين في الخطوة الأولى ويُلغي كذلك القيمة الناتجة عن الخطوة الثانية.

المرحلة الرابعة: تطبيق القيمة الاضطراريّة Apply Corece Value

بعد الحصول على آخر قيمة "من المرحلة السابقة", يقوم WPF بتمرير تلك القيمة إلى المُفوّض CoerceValueCallback (إن تمّ تسجيله عند تعريف خاصيّة التبعيّة من خلال الإجراء DependencyPropery.Register), هذا المُفوّض مسؤول عن إرجاع قيمة جديدة قد تكون مُغايرة كليًا للقيمة الأخيرة بناءًا على منطق Logic مُحدّد, على سبيل المثال أداة ProgressBar تستخدم هذا المُفوّض لتقييد قيمة التقدّم Progress Value إلى أدنى قيمة لها Minimum إذا كانت الفيمة الأخيرة أقل من Minimum, أو إلى أعلى قيمة Maximum إذا كانت القيمة الأخيرة أكبر من Maximum (يُمكن تشبيه هذه الخطوة بـ Validation).

المرحلة الخامسة: التحقّق من صلاحية القيمة Validation

يقوم WPF بتمرير آخر قيمة تمّ تحصيلها من المرحلة السابقة إلى المُفوّض ValueValidateCallback (إن تمّ تسجيله عند تعريف خاصيّة التبعيّة من خلال الإجراء DependencyPropery.Register), هذا المُفوّض يجب أن يُعيد true إن كانت القيمة صالحة, أو يُعيد false إن لم تكن كذلك, الأمر الذي سيُسبّب إطلاق استثناء Exception.

تعابير الربط Binding Expressions

يستخدم WPF لتحقيق ربط البيانات الفئة System.Windows.Data.Binding والتي تعمل على وصل خاصيّتين مع بعضهما البعض ويبقي هذا الاتصال مغتوحًا بينهما, يُمكنك إنشاء هذا الاتصال مرّة واحدة وسيتولّى WPF إبقاهء على مدار عمر التطبيق, ويحتاج WPF لإنشاء الربط إلى أربع مُكونات أساسية:

* الكائن الهدف Target Object: الكائن الذي يتمّ ربط مصدر البيانات به وإسناد القيمة المطلوبة إلى أحد خصائصه (عادة مايكون هذا الكائن هو أحد عناصر واجهة الاستخدام (DataGrid- ListBox ...)

* الخاصيّة الهدف Target Property: الحاصيّة الموجودة في الكائن الهدف والتي ستحمل القيمة المطلوبة من خلال هذا الربط.

* الكائن المَصدر Data Source: الكائن الذي يمتلك القيمة التي سيُقدّمها للكائن الهدف.

* المسار Path: الذي يُحدّد موضع القيمة في الكائن المَصدر (قد يكون هذا المسار اسم خاصية Property Name, أو مُفهرِس Indexer).

ملاحظة: يجب أن تكون الخاصيّة الهدف عبارة عن Dependency Property, ولحسن الحظ فإن معظم الخصائص في عناصر WPF هي كذلك.

لنفترض على سبيل المثال أنك تريد ربط الخاصية Content لعنصر Label ليقوم بعرض النص الذي يتم إدخاله داخل عنصر TextBox, ولنفترض أن اسم عنصر Label هو lblResult, وأن اسم عنصر TextBox هو txtBox, وعليه ستكون مُكونات الربط الأساسيّة وفق ما سبق كما يلي:

* الكائن (العنصر) الهدف: هو lblResult, وهو الكائن الذي تريد ربط مصدر البيانات به وعرض القيمة المطلوبة فيه.

* الخاصيّة الهدف: هي الخاصيًة Content للكائن lblResult, لأنك تريد عرض القيمة المطلوبة من خلال الخاصيّة Content.

* الكائن المَصدر: وهو txtBox (الكائن الذي يُوفّر القيمة).

* المسار: وهو اسم الخاصيًة Text في الكائن المَصدر txtBox, لأنك تريد عرض القيمة التي تحملها الخاصيًة Text.

أبسط سيناريو يُمكن به إنجاز عملية الربط هو حينما يكون الكائن المَصدر أحد عناصر WPF, وتكون الخاصيّة المَصدر عبارة عن Dependency Property, وهذا لأن خصائص التبعيّة مبنيّة أساسًا على دعم التنبيه بالتغيّرات Change Notification كما ورد سابقًا, ولهذا عندما تُغيّر من قيمة الخاصية المَصدر في الكائن المَصدر فإن الخاصيّة الهدف في الكائن الهدف تتغيّر تلقائيًا, وهذا ماتريده بالضبط, ولا تحتاج لإتمام ذلك أيّة مُتطلبات إضافية!

وحتى تفهم كيف يُمكن ربط عنصر إلى آخر, لاحظ الصورة التالية التي تحتوي على عنصري تحكّم من عناصر WPF, شريط تمرير Silder, وأداة عرض النّص TextBlock, عندما تقوم بتحريك مُؤشِّر الشريط نحو اليمين يتزايد حجم النّص, وإذا قمت بتحريكه نحو الاتجاه المعاكس يتناقص حجم النّص. من الواضح أنه يُمكنك بسهولة إنجاز هذا السلوك من خلال الكود, من خلال مُعالجة الحدث Slider.ValueChanged ونسخ قيمة موضع المُؤشِّر الحالية وتعيين حجم الخط استنادًا إلى تلك القيمة, ولكن استخدام الربط عنصرًا إلى عنصر سيجعل ذلك أسهل!

capture1s.png

قبل البدء, لست بحاجة لإجراء أيّة تعديلات على الكائن المَصدر (شريط التمرير), كلّ ما تحتاجه فقط هو ضبط مجال القِيم كما تفعل في العادة. الربط سيكون بداخل العنصر TextBlock, وبدل أن تقوم بإسناد قيمة نصية للخاصيّة FontSize ستستخدم تعبير الربط Binding Expression كما يلي:

<StackPanel>
        <Slider Name="sliderFontSize"
                Margin="3"
                Minimum="1"
                Maximum="32"
                Value="12"
                TickFrequency="1"
                TickPlacement="TopLeft">
        </Slider>
        <TextBlock Margin="10"
                   Text="Simple Text"
                   Name="lblSampleText"
                   FontSize="{Binding ElementName=sliderFontSize, Path=Value}">
        </TextBlock>
    </StackPanel>

تعابير الربط تستخدم امتدادات التمييز Markup Extensions (ولهذا تم إحاطتُها بـ {}) ,ويبتدأ تعبير الربط بالكلمة Binding لأنك هنا تقوم بإنشاء كائن من الفئة System.Windows.Data.Binding Class, ورغم ذلك يُمكنك تكوين كائن الربط هذا بعدّة طُرق, وفي مثل هذه الحالة نحتاج لإعداد خاصيّتين لهذا الكائن: الخاصيّة ElementName لتحديد اسم العنصر المَصدر, والخاصيّة Path لتحديد الخاصيّة المَصدر. لاحظ أن اسم الخاصيّة Path تم استخدامه هنا بدل الاسم Property لأن هذا المسار قد يكون إما اسم خاصيّة, وإما مُفهرِسًا Indexer, يُمكنك استخدام النقطة "." للفصل بين أسماء خصائص متداخلة مثل: Property1.Property2.Property3.

إنشاء الربط إجرائيًا

[يُفضّل دائمًا عند إجراء عمليات الربط أن تستخدم XAML فهو أسهل وأكثر مرونة, ولكن ليس هناك ما يمنعك من إجراء الربط إجرائيًا من خلال الكود, وفي المثال التالي توضيح لكيفية إنشاء الربط المُستخدم في المثال السابق من خلال الكود:

[

var binding: Binding := new Binding(ElementName := 'sliderFontSize', Path := new PropertyPath('Value'));
    lblSampleText.SetBinding(TextBlock.FontSizeProperty, binding)
end;

يًمكنك كذلك إلغاء هذا الربط ومسحه إجرائيًا من خلال إجراءات الفئة BindingOperations, الإجراء ClearBinding والذي يأخذ وسيطة من النوع DependencyProperty الذي يحمل الربط الذي تحاول مسحه, بينما الإجراء ClearAllBindings يقوم بمسح كافة الارتباطات لعنصر ما.

BindingOperations.ClearAllBindings(lblSampleText);

لا الإجرائين السابقين يستدعيان الإجراء ClearValue الموجود في كل عنصر يرث الفئة DependencyObject بما فيها TextBlock, هذا الإجراء ببساطة يقوم بمسح القيمة المحليّة للخاصيّة (والذي هو في هذه الحالة تعبير الربط).

إن استخدام الربط من خلال XAML أكثر شيوعًا من الطريقة الإجرائية, لأنه أسهل وبتطلّب عملاً أقل, ولكن هناك حالتان خاصّتان قد تحتاج فيهما إلى استخدام الطريقة الإجرائية, الأولى عندما تحتاج إلى تكوين الربط بناءًا على شروط مُعيّنة, على سبيل المثال قد تحتاج إلى إجراء بعض عمليات التحقّق مثلاً من مُتغيّرات النظام قبل إنشاء الربط, وفي هذه الحالة يُمكنك تقليل حجم الكود بتعريف كائنات الربط مبدئيًا والاحتفاظ بها في موارد الإطار Window Resources من خلال XAML, وتكوين تلك الكائنات إجرائيًا بالشكل المطلوب كما سلف. وأما الحالة الثانيّة وهي عندما تريد مسح الربط والنخلّص منه, فلا يكفي ان تقوم بإسناد قيمة جديدة إلى الخاصية التي تريد مسحها بغرض التخلّص من الربط, فهذا لن يُؤثّر في الأمر شيئًا, بل ستحتاج لاستخدام الإجراء ClearBinding أو الإجراء ClearAllBindings كما سلف.

أوضاع الربط والتحديث Data Binding/Updating Modes

كما رأيت حتى الآن فإن مسار تغيّر الخصائص كان دائمًا من العنصر المَصدر إلى العنصر الهدف, ولكن من المُمكن بشكل ما أو بآخر أن تتغيّر قيمة الخاصية الهدف مُباشرةّ, ومن الجيّد في هذه الحالة أن ينعكس هذا التغيّر نحو العنصر المَصدر أيضًا, ففي المثال السابق قد تقوم من خلال الكود بتغيير قيمة الخاصية FontSize , عندها سستوقّع للحظة أن شريط التمرير سيقوم بتحديث نفسه تلقائيًا للقيمة ولكن هيهات! يُمكن حلّ هذه المسألة بساطة باستخدام الخاصية Mode للكائن Binding, حيث يُمكن التحكّم من خلالها في أوضاع مسار تغيّر البيانات, وهي كالتالي:

* الوضع أُحادي الاتجاه OneWay: يتمّ تحديث قيمة الخاصيّة الهدف عندما تتغيّر قيمة الخاصيّة المَصدر.

* الوضع ثنائي الاتجاه TwoWay: يتمّ تحديث قيمة الخاصيّة المَصدر أو الهدف بتغيّر قيمة الخاصيّة الأخرى, هذا الوضع يُستخدم عادة عند التعامل مع عناصر التحكّم التي يُسمح فيها للمستخدم بالتعديل عليها, مثل DataGrid, TextBox, حيث تنتقل التغيّرات من هذه العناصر مباشرةً إلى الكائن المَصدر المرتبط بها.

* الوضع أُحادي الاتجاه نحو المَصدر OneWayToSource: هذا الوضع مُعاكس للوضع الأول OneWay. حيث يتمّ تحديث قيمة الخاصيّة المَصدر كُلّما تمّ تحديث الخاصيّة الهدف. هذا الوضع مُفيد جدًا خصوصًا في الحالة التي تكون فيها الخاصيّة المَصدر ليست خاصيّة تبعيّة .

* الوضع لأول مرّة فقط OneTime: هذا الوضع مُماثل للوضع الأول OneWay باستثناء أن أيّ تغيّر يتمّ لاحقًا على الخاصيّة المَصدر لايتمّ أخذه بالحسبان ولا تتأثّر به الخاصيّة الهدف.

* الوضع الافتراضي Default: هذا الوضع يعتمد على الخاصيّة الهدف, يُمكن أن يكون هذا الوضع قي هذه الحالة إما ثنائي الاتجاه TwoWay (بالنسبة للخصائص الهدف القابلة للكتابة Writable Properties), أو أحادي الاتجاه, جميع الخصائص تعتمد على هذا الأسلوب, إلا إذا قمت باختيار وضع مُغاير بشكل صريح.

الصورة التالية تُلخّص الأوضاع السابقة:

picture1pa.png

عندما تتعامل مع الربط ثنائي الاتجاه TwoWay, أو أحادي الاتجاه نحو المَصدر OneWayToSource, فإنك قد ترغب أحيانًا في التحكّم في الكيفية التي يتمّ بها تحديث الهدف, على سبيل المثال, عتدما يقوم المستخدم بإدخال البيانات في عنصر TextBox, قد ترغب في أن يحدث التغيير فقط عندما يقوم المستخدم بالانتقال إلى عنصر التحكم التالي بواسطة المفتاح Tab, أو كلّما قام المستخدم بكتابة حرف أو تغيير حرف في عنصر TextBox, أو أن تتحكّم بنفسك من خلال الكود في الوقت الذي ترغب فيه بإجراء التحديث. الخاصية UpdateSourceTrigger من نوع الترقيم UpdateSourceTrigger Enum والموجودة في الفئة Binding تُتيح لك التحكم في تلك الكيفية من خلال أوضاع هي كالتالي:

* PropertyChanged: يتمّ فيه تحديث قيمة الخاصيّة المَصدر كُلّما تمّ تحديث الخاصيّة الهدف.

* LostFocus: يتم تحديث قيمة الخاصيّة المَصدر كُلّما تغيّرت قيمة الخاصيَة الهدف, ولكن فقط عندما يفقد عنصر التحكّم التركيز Focus .

* Explicit: يتم تحديث قيمة الخاصيّة المَصدر كُلّما تغيّرت الخاصيّة الهدف, ولكن فقط عندما تقوم أنت باستدعاء الإجراء BindingExpression.UpdateSource بشكل صريح من خلال الكود. يُمكنك الحصول على كائن من النوع BindingExpression من خلال الإجراء BindingOperations.GetBindingExpression أو استدعاء الإجراء GetBindingExpression لأي كائن من النوع FrameworkContentElement, أو النوع Frameworkelement.

كما يوجد هناك وضع افتراضي بالنسبة لعملية الربط فإن هناك أيصًا وضعًا افتراضيًا بالنسبة لعملية التحديث, على سبيل المثال, فالخاصيّة TextBox.Text افتراضيًا يكون وضع التحديث الخاص بها هو LostFocus .

خيارات الربط Data Binding Options

حتى الآن جميع الأمثلة السابقة تُركّز على الربط بين عنصرين من عناصر WPF, ولكن في أغلب الأحيان في التطبيقات التي تعتمد على قواعد البيانات Data-Driven Applications مصادر البيانات ليست عناصر WPF (ليست أدوات تحكّم واجهة الاستخدام), بل تكون كما ورد في مقدمّة هذا المقال – كائنات غير مرئية – وتحتاج لتمثيلها من خلال عناصر WPF المرئية, ويجب أن يكون متاحًا الوصول إلى خصائص تلك الكائنات , بحيث تكون تلك الخصائص عامّة Public. عند إجراء الربط مع كائنات ليست من عناصر WPF يجب عليك استبعاد استخدام الخاصية ElementName للفئة Binding, وإنما عليك استخدام أحد البدائل التالية:

* الخاصيّة Source: تُمثّل هذه الخاصيّة الارتباط Reference الذي يُشير إلى الكائن المَصدر – بمعنى آخر الكائن الذي يمثل مَصدرًا للبيانات-.

* الخاصيّة RelativeSource: هذه الخاصيّة من النوع System.Windows.Data.RelativeSource Class, وهي تستخدم بغرض الإشارة إلى الكائن نفسه الذي يحتوي على الربط – بمعنى آخر أن الربط يكون من خاصيًة في الكائن المَصدر إلى خاصيّة في الكائن المَصدر نفسه, هذه الخاصيّة مُفيدة عند بناء قوالب أدوات التحكّم Control Templates, و وقوالب البيانات DataTemplates.

* الخاصيّة DataContext للكائن الهدف: عند عدم استخدام أيّ من الخاصيّتين السابقتين, فإن WPF سيقوم بالبحث ضمن شجرة العناصر Elements Tree, بدءًا من النعصر الحالي (العنصر الهدف) , ويقوم بمعاينة الخاصيّة DataContext لكلّ عنصر , ويستخدم أول قيمة ليست Null يجدها كمَصدر للبيانات, إن الخاصيّة DataContext مُفيدة للغاية, إذا كنت تريد أن تقوم بربط خصائص عديدة لنفس الكائن المَصدر مع عدّة عناصر تحكّم WPF, لأنه بوسعك أن تربط الخاصيّة DataContext لأعلى عنصر (العنصر الأبوي الحاوي Container لعناصر التحكّم الباقية) بدلاً من ربطها مع كل عنصر ضمن شجرة العناصر الأبناء.

الخاصيّة Source

يُمكنك التعامل مع الخاصيّة Source بشكل مُباشر, كلّ ما تحتاجه فقط هو أن يكون الكائن مَصدر البيانات جاهزًا للارتباط به, أبسط طريقة يُمكنك من خلالها استخدام هذه الخاصيّة هو أن تقوم بإسناد كائن ستاتيكي لها, قد يكون هذا الكائنن من كتابتك, او احد كائنات مكتبة Microsoft.NET, كما يُوضّح المثال التالي:

ext="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}"/>

تعبير الرط في المثال السابق يُعيد كائنًا من النوع FontFamily من خلال الخاصيّة SystemFonts.IconFontFamily (لاحظ أنك تحتاج لاستخدام مُلحق الترمير s:Static), ثم بعد ذلك تمّ نحديد المسار Path نحو الخاصيّة FontFamily.Source (والتي تُمثّل اسم الخط), وستكون النتيجة هي ظهور الاسم Segoe UI في Windows Vista و Windows 7., يُمكن أيضًا استخدام الموارد الثابتة Static Resources , المثال التالي ستقوم بإنشاء مورد ثابت من النوع FontFamily والذي يُمثّل الخط Calibri:

<Window.Resources>
        <FontFamily x:Key="MyFont">Calibri</FontFamily>
    </Window.Resources>

وفيما يلي كيفية جعل TextBlock يستخدم هذا الكائن:

<TextBlock Margin="10"
           Text="Simple Text"
           FontFamily="{Binding Source{StaticResource MyFont}}"
           Name="lblSampleText">

الخاصيّة RelativeSource

هناك طريقة أخرى لتحديد الكائن مَصدر البيانات, عبر الخاصيّة Binding.RelativeSource, والتي تُشير إلى ذلك الكائن حسب علاقته مع الكائن الهدف, هذه الخاصيّة من النوع RelativeSource, والذي هو في الأصل مُلحق ترميز (موروث من النوع MarkupExtension), وفيما يلي بعض الاستخدامات الشائعة لهذه الخاصيّة:

* جعل الكائن الهدف هو نفسه الكائن المَصدر:

{Binding RelativeSource={RelativeSource Self}}

جعل الكائن المَصدر هو الكائن الذي تحمله الخاصيّة TemplatedParent للكائن الهدف (سيتمّ شرح هذه الخاصيّة لاحقًا في موضوع مُنفصل):

{Binding RelativeSource={RelativeSource TemplatedParent}}

جعل الكائن المَصدر هو أقرب عنصر أبوي Parent Element للكائن الهدف, ويكون هذا العنصر الأبوي من نوع مُحدّد:

{Binding RelativeSource={RelativeSource FindAncestor, 
AncestorType={x:Type desiredType}}}

حيث desiredType هو نوع العنصر الأبوي (الكائن المَصدر).

* جعل الكائن المَصدر هو أقرب عنصر أيويذي الرتبة n للكائن الهدف , ويكون هذا العنصر الأبوي من نوع مُحدّد:

{Binding RelativeSource={RelativeSource FindAncestor, 
AncestorLevel=n, AncestorType={x:Type desiredType}}}

حيث desiredType هو نوع العنصر الأبوي (الكائن المَصدر).

استخدام الخاصيّة RelativeSource مُفيد عند بناء قوالب الأدوات Control Templates , كما أن استخدام الخاصية Self مُفيد أيضًا في الكثير من الأحيان عندما تريد ربط خاصيّة لعنصر مع خاصيّة أخرى لنفس العنصر دون الحاجة لذكر اسم ذلك العنصر في تعبير الربط, المثال التالي سيُعطيك فكرة عن كيفية استخدامها, حيث سيتمّ ربط الخاصية ToolTip لعنصر TextBlock مع الخاصية Text لنفس العنصر, وبهذا سيتم عرض قيمة الخاصيّة Text ضمن ToolTip كلّما مررت فوق العنصر:

<TextBlock Text="Some data" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />

لخاصيّة DataContext

في بعض الأحيان, قد يكون لديك عدة عناصر WPF مرتبطة بنفس الكائن المَصدر, على سبيل المثال, افترض أن الكائن المصَدر سيكون كائنًا من النوع Person ويحتوي على الخصائص التالية: FName, LName, Age, وتريد أن تربط كل خاصيّة منها مع TextBox , وعليه سيكون لديك 3 عناصر TextBox ولتكن بداخل عنصر أبوي من نوع StackPanel, كما يُوضّح المثال التالي, في هذه الحالة قد تقوم بكتابة تعابير ربط طويلة نوعًا ما لكل عنصر TextBox لأنك ستقوم بتحديد الخاصية Source في تعابير الربط تلك, بالإضافة إلى المسار طبعًا:

 <StackPanel>
        <TextBlock Text="{Binding Source=myPerson, Path=FName}" />
        <TextBlock Text="{Binding Source=myPerson, Path=LName}" />
        <TextBlock Text="{Binding Source=myPerson, Path=Age}" />
    </StackPanel>

الجيّد في هذه الحالة أن تقوم بربط الخاصيةDataContext للعنصر StackPanel, والذي يحتوي على جميع عناصر TextBox (مع العلم أنه بالإمكان أيضًا ربط نفس الخاضية لعنصر أعلى من العنعصر StackPanel مثل Window نفسه, ولكن يُفضّل أن تختار العنصر الأقرب), ولربط الخاضيّة DataContext تحتاج أيضًا لاستخدام تعبير الربط, كما في المثال التالي:

    <StackPanel DataContext="{Binding Source=myPerson}">

والآن يُمكنك حذف الخاصيّة Source من تعابير الربط لعناصر TextBox الثلاثة, لأنه طالما لم يتم تحديد تلك الخاصيّة, فإن WPF سيقوم بفحص قيمة الخاصيّة DataContext لكل عنصر من عناصر TextBox, فإن كانت تلك القيمة Null سيقوم بفحص نفس الخاصيّة للعنصر الأعلى (StackPanel في هذه الحالة), وطالما أنها ليست Null ستصبج القيمة التي تحملها (myPerson) هي القيمة التي سيستخدمها WPF من أجل تعابير الربط لعناصر TextBox .

 <StackPanel DataContext="{Binding Source=myPerson}">
        <TextBlock Text="{Binding Path=FName}" />
        <TextBlock Text="{Binding Path=LName}" />
        <TextBlock Text="{Binding Path=Age}" />
    </StackPanel>

ملاحظة: إذا قمت باستخدام تعبير ربط مع تحديد الخاصيّة Source له بشكل صريح, فإن WPF سيستخدم قيمة تلك الخاصية حتى لو كانت الخاصيّة DataContext ليست Null .

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 3 أبريل 2011 في 00:28

#8

و الان سنطبق ما تعلمناه لحد الان

1- انشاء مشروع ثاني جديد باسم InfoPerson

2- نضع العناصر التاية في و الواجهة الرئيسه للمشروع

<?xml version='1.0' encoding='utf-8' ?>
<Window x:Class="WPFApplication6.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    >
    <Grid>
        <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

        <Label Grid.Column="0" Grid.Row="0" Content="First Name" />
        <Label Grid.Column="0" Grid.Row="1" Content="Last Name" />
        <Label Grid.Column="0" Grid.Row="2" Content="Age" />

        <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Path=FName}" />
        <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding Path=LName}" />
        <TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Path=Age}" />
    </Grid>
</Window>

3- نضع الكود التالي في الملف Person.pas اي كتابة الصنف

namespace WPFApplication6.ViewModel;

interface

uses
  System.Collections.Generic,
  System.Text;
type
  Person = public class
  public property FName: System.String read 'Abdelali';
  public property LName: System.String read 'ZEKIRI';
  public property Age: System.Int32 read 36;
  end;

implementation

end.

4- في السورس للصنف الريسي Window1 نضيف تعريف الصنف person ة نربطه معها

uses InfoPerson.ViewModel;

constructor Window1;
begin
    InitializeComponent();
    DataContext := new Person();
end;

post-1034-086937900 1301783685_thumb.jpg

post-1034-006881800 1301783757_thumb.jpg

المرفقات
WPF-ex2-1.JPGWPF-ex2-2.JPG
#9

السلام عليكم

MVVM

الان إن شاء الله سنبدا بشرح برمجة WPF وفق النموذج التصميمي MVVM

مقدمة

ساجد ان شاء الله دروس باللغة العربي و ان لم يكن ساترجم ما بكتب هنا

1- http://www.codeproject.com/KB/WPF/MVVMEpisode1.aspx

2- http://japf.developpez.com/tutoriels/dotnet/mvvm-pour-des-applications-wpf-bien-architecturees-et-testables/

في حين ذلك سنبد يمثال بسيط

فبل ان نشرح ما هو MVVM لبد ان نشرح اولا MVC و MVP لانه هو تطير لهذه النماذج

1- MVCتقوم أغلب أطر الويب على فكرة MVC أو Model View Controller أي النموذج-العرض-الحاكم أي تقسيم برنامج الويب إلى عناصر مستقلة لا تتداخل هي

■النموذج Model - حيث نحدد عناصر المدخلات (نوعها وقيمها التلقائية وفهارسها والعلاقات فيما بينها) وهو يكافئ مخطط قاعدة البيانات لكن بصورة مستقلة عن نوع قاعدة البيانات (أي من المفروض أن نتمكن من تغيير قاعدة البيانات من MySQL إلى PostgreSQL بتعديل الإعدادات فقط) غالبا ما يتم ذلك ORM أو Object Relational Mappers أي تثميل كل جدول في قاعدة البيانات بفئة class وتمثيل كل صف في الجدول بكائن من تلكم الفئة.

■العرض View - وهو نظام قوالب لتمثيل المدخلات والبيانات ويفترض أن يكون نظام القوالب مرنا به مزايا مثل الحلقات التكرارية والجمل الشرطية وبل يحتوي قوالب أخرى أو يُحتوى فيها (الوراثة) وفي نفس الوقت يجب أن تخلو القوالب من البرمجة وتكون أقرب للتصميم فالقوالب ليست لغات برمجة.

■الحاكم Controller - وهنا البرمجة حيث نجلب كائنات من النموذج حتى نركبها على قالب ما أو ننشئ كائنات جديدة. وهنا نحتاج ربط عناوين URI بالدوال المطلوب تنفيذها ويكون ذلك بطريقتين

■مطابقة أنماط معينة تسمى routes تقابل بين النمط والدالة

■نشر الكائنات object dispatcher أو object publisher حيث مثلا يتم تنفيذ الدالة التي تطابق جزء من عنوان url وتمرير بقيته إليها كمعاملات.

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 14 أبريل 2011 في 13:43

#10

1- المثال الاول في MVVM

الهدف عرض قاءمة اشخاص في Datagridview

خطوات العمل

ننشا مشروع جديد باسم ListPersonsApp

نضيف فيه 3 مستندات او مجلدات Model + View + ViewModel

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

نقوم الان بتصميم الواجة اي رسم الداتا قريد بواسطة XAML

<?xml version='1.0' encoding='utf-8' ?>
<Window x:Class="ListPersonsApp.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:ViewModel="clr-namespace:ListPersonsApp.ViewModel"
        Height="150" Width="479"
        >

    <ListView ItemsSource="{Binding ListPersons}">
        <ListView.View>
            <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Authors">
                <GridViewColumn Header="Name"       Width="200"    DisplayMemberBinding="{Binding Path=FirstName}" />
                <GridViewColumn Header="Last Name"  Width="200"    DisplayMemberBinding="{Binding Path=LastName}"  />
                <GridViewColumn Header="Age"        Width="52"     DisplayMemberBinding="{Binding Path=Age}"       />
            </GridView>
        </ListView.View>
    </ListView>
</Window>

 xmlns:ViewModel="clr-namespace:ListPersonsApp.ViewModel"

هنا عرفنا النيم سبيس لجميع الاصناف التي سننشاءها لاحقا في المجلد ViewModel

 <ListView ItemsSource="{Binding ListPersons}">

قمنا بربط العنصر ListView مع الخاصية ListPersons و التي تعتبر شيء يضم قاءمة من الاشاء سنشرح لاحقا

DisplayMemberBinding="{Binding Path=FirstName}"

قمنا بريط هذا العمود بالحقل FirstName

نقوم الان بتعريف الصنف الذي توجد به الخاصية ListPersons وذلك بالخول الى السورس MainWindow.xaml.asp

namespace ListPersonsApp.View;

interface

uses
  System.Windows;

type
  MainWindow = public partial class(System.Windows.Window)
  private
  public
    constructor;
  end;

implementation

constructor MainWindow;
begin
  InitializeComponent();
  DataContext := new PersonCollectionViewModel();
end;

end.

نقوم بتغيير مسار الواجهة الرءيسية في ملف APP.XAML

 StartupUri="View/MainWindow.xaml"

post-1034-003487100 1301857175_thumb.jpg

المرفقات
WPF-ex3-1.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 3 أبريل 2011 في 23:01

1
#11

لا يمكننا عمل كومبيل لانه يتوقف عند PersonCollectionViewModel

و من اسم هذا الصنف سنعرف مكانه اي المجلد ViewModel اذن نضيف كلاس جديد بهذا الاسم

namespace ListPersonsApp.ViewModel;

interface

uses
  System.Collections.Generic,
  System.Text;

type
  PersonCollectionViewModel = public class
  end;

implementation

end.

الان يمكن الضغط على F5 ان كان كل شئ على مايرام نواصل علما اننا سنترك كتابت اكواد هده الكلاس الى مرحلة اخرى

نكتب الان الصنف PersonModel الذي يمثل شخص معين اي لابد من الاسم و اللقب و السن كحقول و نحفض هذا الصنف في المجلد model

namespace ListPersonsApp.Model;

interface

type

  PersonModel = public class
  private  _firstname    : String;
  private  _lastname     : String;
  private  _age          :  Int32;

  public
    property FirstName : String read self._firstname write self._firstname;
    property LastName  : String read self._lastname  write self._lastname;
    property Age       : Int32  read self._age       write self._age;
  end;

implementation
end.

و بما اننا نريد عرض قاءمة لا بد من ان ننشاء صنف PersonCollectionModel جديد من نوع قاءمة يضم قاءمة الاشخاص اي قاءمة من الصنف PersonModel

namespace ListPersonsApp.Model;

interface

uses
  System.Collections.Generic;

type
  PersonCollectionModel = public class

    private _listpersons: List<PersonModel>; readonly;
    public property Listpersons: List<PersonModel> read _listpersons;
    public constructor ;

 end;

implementation

constructor PersonCollectionModel;
begin
  self._listpersons := new List<PersonModel>();
  self._listpersons.Add(new PersonModel(FirstName := 'ZEKIRI' ,  LastName := 'Abdelali' ,  Age := 23 ));
  self._listpersons.Add(new PersonModel(FirstName := 'BERKAN' ,  LastName := 'Mess'     ,  Age := 22 ));
  self._listpersons.Add(new PersonModel(FirstName := 'ALLOY'  ,  LastName := 'Ali'      ,  Age := 38 ))
end;

end.

الان سنكتب الصنف PersonViewModel

namespace ListPersonsApp.ViewModel;

interface

uses
  System,
  ListPersonsApp.Model;

type
  PersonViewModel = public class

    private _person: PersonModel; readonly;
    public property Person: PersonModel read self._person;

    property FirstName  : String  read  self._person.FirstName  write   self._person.FirstName;
    property LastName   : String  read  self._person.LastName   write   self._person.LastName;
    property Age        : integer read  self._person.Age        write   self._person.Age;

    public constructor (person: PersonModel);
  end;

implementation

constructor PersonViewModel(person: PersonModel);
begin
    if person = nil then raise new NullReferenceException('person');
    self._person := person

end;

end.

اضن ان الكود واضح .

و اخيرا سنكتب كود الصنف PersonCollectionViewModel

namespace ListPersonsApp.ViewModel;

interface

uses
  ListPersonsApp.Model,
  System.Collections.Generic;

type
  PersonCollectionViewModel = public class
    private _listpersons: List<PersonViewModel>;       readonly;
    public property ListPersons: List<PersonViewModel> read self._listpersons;
    public constructor ;
  end;

implementation

constructor PersonCollectionViewModel;
begin
     var spersonCollectionModel: PersonCollectionModel := new PersonCollectionModel();
     self._listpersons := new List<PersonViewModel>();

     for each person: PersonModel in spersonCollectionModel.Listpersons do 
     begin
         self._listpersons.Add(new PersonViewModel(person));
     end;
end;

end.

هنا قمنا بحلقة تكرارية كي نسترجع المعلمات المخزنة في الكائن spersonCollectionModel.Listpersons و شحنها في الكاءن الجديد self._listpersons

لماذا بكل بساطة لك ينستطيع استعماله هذه القاءمة في الواجة لو تلاحض السطر

<ListView ItemsSource="{Binding ListPersons}">

معناه استرجاع القاءمة من الخاصية ListPersons و التي تمثل الكاءن self._listpersons لاحض

public property ListPersons: List<PersonViewModel> read self._listpersons;

الحمد لله انتهينا من المثال الاول و سنتابع العمل عليه في الامثلة القادمة ان شاء الله

فقط انبه انه في البريسم هناك مكون جاهز و المعتادين عليه في الوين فورم او حتي الويب DtataGrid لا حض الصورة

 <DataGrid ItemsSource="{Binding ListPersons}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="FirstName" Binding="{Binding FirstName}"/>
            <DataGridTextColumn Header="LastName" Binding="{Binding LastName}"/>
            <DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
        </DataGrid.Columns>
    </DataGrid>

post-1034-034222000 1301951467_thumb.jpg

ListPersonsAppC#.rar

ListPersonsAppDelphi.rar

post-1034-092777100 1301956105_thumb.jpg

المرفقات
WPF-ex3-2.JPGWPF-ex3-3.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 5 أبريل 2011 في 01:28

1
#12

- المثال الثاني في MVVM

الهدف بعد (عرض قاءمة اشخاص في Datagridview )اظافة ازرار التالي السابق و الاول و الاخر .

خطوات العمل

1- رسم الواجهة

<?xml version='1.0' encoding='utf-8' ?>
<Window x:Class="ListPersonsApp.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:ViewModel="clr-namespace:ListPersonsApp.ViewModel"
        Height="261" Width="479"
        >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <!-- DataGridVeiw -->
        <ListView Grid.Column="0" ItemsSource="{Binding ListPersons}" IsSynchronizedWithCurrentItem="True">
            <ListView.View>
                <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Authors">
                    <GridViewColumn Header="Name"       Width="200"    DisplayMemberBinding="{Binding Path=FirstName}" />
                    <GridViewColumn Header="Last Name"  Width="200"    DisplayMemberBinding="{Binding Path=LastName}"  />
                    <GridViewColumn Header="Age"        Width="52"     DisplayMemberBinding="{Binding Path=Age}"       />
                </GridView>
            </ListView.View>
        </ListView>
        <!--/DataGridView-->
        <!--BindingNavigator-->
        <Grid Grid.Row="1" Height="20" Width="180"  >
            <Grid.ColumnDefinitions>
                <!-- ColumnFirst               |        ColumnPrevious     |          ColumnRecno          |      ColumnRecordCount        |         ColumnNext            |        ColumnLast         -->
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="50" />
                <ColumnDefinition Width="50" />
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="20" />
            </Grid.ColumnDefinitions>
            <!-- ButtonFirst -->
            <Button Grid.Column="0"  Name="BtnFirst"        Command ="{Binding FirstAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnFirst.PNG" />
                </Button.Background>
            </Button>
            <!-- ButtonPrevious -->
            <Button Grid.Column="1"  Name="BtnPRevious"     Command="{Binding PreviousAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnPrevious.PNG" />
                </Button.Background>
            </Button>
            <!-- ButtonRecno -->
            <TextBox Grid.Column="2" Name="TxRecno"         Text    ="{Binding GetRecno}"/>
            <!-- ButtonRecordCount -->
            <Label   Grid.Column="3" Name="TxRecordCount"   Content ="{Binding GetRecordCount}" />
            <!-- ButtonNext -->
            <Button  Grid.Column="4" Name="BtnNext"         Command ="{Binding NextAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnNext.PNG" />
                </Button.Background>
            </Button>
            <!-- ButtonLast -->
            <Button  Grid.Column="5" Name="BtnLast"         Command ="{Binding LastAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnLast.PNG" />
                </Button.Background>
            </Button>
        </Grid>
        <!--/BindingNavigator-->
    </Grid>
</Window>

لقد اظفة ازرا و صور مطابقة تجدونها في المرفقات

سنكتب كود الصنف

ViewModel -->ActionExecute.cs

using System.Windows.Input;
using System;

namespace ListPersonsApp.ViewModel
{
    public class ActionExecute : ICommand
    {
        private  Action     _execute;
        private  Func<bool> _canExecute;

        public ActionExecute(Action execute, Func<bool> canExecute)
        {
            this._execute     = execute;
            this._canExecute  = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add    { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            this._execute();
        }

        public bool CanExecute(object parameter)
        {
            return this._canExecute();
        }
    }
}

اما الان فنغير في الصنف PersonCollectionViewModel

namespace ListPersonsApp.ViewModel;

interface

uses
  System,
  System.Windows.Data,
  System.ComponentModel,
  System.Collections.Generic,
  System.Windows.Input,
  ListPersonsApp.Model;
type

  PersonCollectionViewModel = public class

    private _listpersons: List<PersonViewModel>; readonly;
    private _collectionView : ICollectionView; readonly;

    private _previousAction : ICommand;
    private _nextAction     : ICommand;
    private _lastAction     : ICommand;
    private _firstAction    : ICommand;

    public  property ListPersons    : List<PersonViewModel> read self._listpersons;

    public  property LastAction     : ICommand read get_LastAction;
    public  property PreviousAction : ICommand read get_PreviousAction;
    public  property NextAction     : ICommand read get_NextAction;
    public  property FirstAction    : ICommand read get_FirstAction;

    public  method get_NextAction: ICommand;
    public  method get_PreviousAction: ICommand;
    public  method get_LastAction: ICommand;
    public  method get_FirstAction: ICommand;

    public  constructor ;
  end;


implementation


method PersonCollectionViewModel.get_PreviousAction: ICommand; 
begin
      if self._previousAction = nil then 
         self._previousAction := new ActionExecute(() ->_collectionView.MoveCurrentToPrevious(), () -> _collectionView.CurrentPosition >= 1);
      exit self._previousAction;
end;

method PersonCollectionViewModel.get_NextAction: ICommand; 
begin
      if self._nextAction = nil then 
         self._nextAction := new ActionExecute(() ->_collectionView.MoveCurrentToNext(), () ->_collectionView.CurrentPosition < _listpersons.Count-1);
      exit self._nextAction;
end;

method PersonCollectionViewModel.get_LastAction: ICommand; 
begin
     if self._lastAction = nil then 
        self._lastAction := new ActionExecute(() ->_collectionView.MoveCurrentToLast(), () ->_collectionView.CurrentPosition < _listpersons.Count-1);
     exit self._lastAction;
end;

method PersonCollectionViewModel.get_FirstAction: ICommand; 
begin
     if self._firstAction = nil then 
        self._firstAction := new ActionExecute(() ->_collectionView.MoveCurrentToFirst(), () ->_collectionView.CurrentPosition >= 1);
     exit self._firstAction;

end;

constructor PersonCollectionViewModel;
begin
    var spersonCollectionModel: PersonCollectionModel := new PersonCollectionModel();
    self._listpersons := new List<PersonViewModel>();

    for each person: PersonModel in spersonCollectionModel.Listpersons do 
    begin
        self._listpersons.Add(new PersonViewModel(person));
    end;

    self._collectionView := CollectionViewSource.GetDefaultView(self._listpersons);
    if (self._collectionView = nil) then
       raise new NullReferenceException('_collectionView');
end;

end.

تمرين تطبيقي

اكمل الكود السايق للحصول على قيم في كل من العنصرين

TxRecno TxRecordCount

Resources.rar

post-1034-085922600 1302179618_thumb.jpg

post-1034-076403600 1302207548_thumb.jpg

المرفقات
Ex_4.JPGWPF-ex3-4.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 9 أبريل 2011 في 09:36

#13

skin

مقدمة

ملف السكين هو ملف xaml يملك تعليماة أو أوسمة خاصة Style

<ResourceDictionaryالكلمة في بداية الملف هي من ستحدد لنا انه ليس ملف واجهة ولاصفة بل هو skin

كيف نضيف ResourceDictionary

لاحضة الصورة 1

انشاء ستيل للخلفية

كماهو واضح أننا اعطينا اسم styleMinGrid لهذا الستيل

الربط بالواجهة

1- باظافة مباشرة في XAML كثاب.

<Window.Resources>
<ResourceDictionary>
  <ResourceDictionary.MergedDictionaries>
       <ResourceDictionary Source="/View/Resources/Skins/MetalGreenSkin.xaml" />
  </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>

2- في السورس

         
            string skinFile = @".\View\Resources\Skins\ExpressiveRedSkin.xaml";
            Uri skinDictUri = new Uri(skinFile, UriKind.Relative);
            Application.Current.Resources = (ResourceDictionary)Application.LoadComponent(skinDictUri);

3-

  XmlReader XmlRead = XmlReader.Create(skinFile);
            Application.Current.Resources = (ResourceDictionary)XamlReader.Load(XmlRead);
            XmlRead.Close();

وبهذه الطريق نستطيع تغير ملف السكي متى شانا

post-1034-037957200 1302251521_thumb.jpg

المرفقات
Ex_4-5.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 8 أبريل 2011 في 22:26

#14

السكين 1

<ResourceDictionary 
                    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
    <!-- ====================  |style Min Grid| ================================= -->
    <Style x:Key="styleMinGrid">
        <Setter Property="Border.Background">
        <Setter.Value>
            <LinearGradientBrush StartPoint="1,0.5" EndPoint="0.5,0">
              <GradientStop Color="#FFD6E2D6" Offset="0.75" />
              <GradientStop Color="WhiteSmoke" Offset="0.3" />
            </LinearGradientBrush>
        </Setter.Value>
        </Setter>
    </Style>
    <!-- ==================  |Header Banner Style| ============================= -->
    <Style x:Key="styleBannerHeader">
        <Setter Property="StackPanel.Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="WhiteSmoke" Offset="0.1"      />
                    <GradientStop Color="#FFA4B4A4" Offset="1"         />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="TextBlock.Foreground" Value="White"         />
        <Setter Property="TextBlock.FontSize"   Value="26"/>
        <Setter Property="TextBlock.FontFamily" Value="Garamond" />
        <Setter Property="TextBlock.FontWeight" Value="Bold" />
    </Style>
    <!-- =====================  |Style Button|  ================================= -->
    <Style x:Key="styleButton">
        <Setter Property="StackPanel.Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="WhiteSmoke" Offset="0.1"      />
                    <GradientStop Color="#FFA4B4A9" Offset="1"         />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="TextBlock.Foreground" Value="#FF7F9DB9"         />
        <Setter Property="TextBlock.FontSize"   Value="12"/>
        <Setter Property="TextBlock.FontFamily" Value="Sans Serif" />
        <Setter Property="TextBlock.FontWeight" Value="Bold" />        
    </Style>
    <!-- ================  |Style ListView Content|  ============================ -->
    <Style x:Key="StyleListViewContent">
        <Setter Property="StackPanel.Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0,1" EndPoint="0.5,0">
                    <GradientStop Color="#FFE5E5E5" Offset="0.1"      />
                    <GradientStop Color="WhiteSmoke" Offset="1"         />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="TextBlock.FontSize"   Value="10"/>
        <Setter Property="TextBlock.FontFamily" Value="Verdana" />
        <Setter Property="Border.BorderThickness" Value="1" />
        <Setter Property="Border.CornerRadius" Value="10" />
        <Setter Property="FrameworkElement.BitmapEffect">
            <Setter.Value>
                <DropShadowBitmapEffect ShadowDepth="8" />
            </Setter.Value>
        </Setter>
    </Style>

السكين 2

</ResourceDictionary>

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!-- ====================  |style Min Grid| ================================= -->
    <Style x:Key="styleMinGrid">
        <Setter Property="Border.Background" Value="#FFA77C7C"/>
    </Style>
    <!-- ==================  |Header Banner Style| ============================= -->
    <Style x:Key="styleBannerHeader">
        <Setter Property="StackPanel.Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="WhiteSmoke" Offset="0.1"      />
                    <GradientStop Color="#FF600000" Offset="1"         />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="TextBlock.Foreground" Value="White"         />
        <Setter Property="TextBlock.FontSize"   Value="26"/>
        <Setter Property="TextBlock.FontFamily" Value="Comic Sans MS" />
        <Setter Property="TextBlock.FontWeight" Value="Bold" />
    </Style>
    <!-- =====================  |Style Button|  ================================= -->
    <Style x:Key="styleButton">
        <Setter Property="StackPanel.Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="WhiteSmoke" Offset="0.1"      />
                    <GradientStop Color="#FFA4B4A9" Offset="1"         />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="TextBlock.Foreground" Value="#FF402424"         />
        <Setter Property="TextBlock.FontSize"   Value="12"/>
        <Setter Property="TextBlock.FontFamily" Value="Comic Sans MS" />
        <Setter Property="TextBlock.FontWeight" Value="Bold" />
    </Style>
    <!-- ================  |Style ListView Content|  ============================ -->
    <Style x:Key="StyleListViewContent">
        <Setter Property="StackPanel.Background" Value="#FF402424"/>
        <Setter Property="TextBlock.FontSize"   Value="10"/>
        <Setter Property="TextBlock.FontFamily" Value="Verdana" />
        <Setter Property="Border.BorderThickness" Value="1" />
        <Setter Property="Border.CornerRadius" Value="10" />
        <Setter Property="TextBlock.Foreground" Value="White"         />
        <Setter Property="FrameworkElement.BitmapEffect">
            <Setter.Value>
                <DropShadowBitmapEffect ShadowDepth="8" />
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

لقد ارفقت السورس كود لمن لديه اي مشكل فاليطرح السؤال .

اضن ان كل الامور التي تناوناها حاليه واضحه

post-1034-051355600 1302294404_thumb.jpg

ListPersonsAppDelphi.rar

ListPersonsAppC.rar

المرفقات
WPF-ex3-8.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 10 أبريل 2011 في 10:44

1
#15

تابع اللمثال السابق

استعمال ملفات الموارد Ressources Files

مقدمة

تعريف 1 من هنا

ما هي Resource ؟

هي نوع من الملفات التابعة لبيئة اللغله المستعملة الدلفي الفيبي او الدون نت وتحمل الصيغة .res او resx

ويمكننا باستخدامها من تظمين وتخزين بياناتنا بداخل ملف EXE للبرنامج

كموارد يحتاجها البرنامج في عملة ويمكننا تخزين أي نوع من البيانات

مثل الصور وملفات الصوت والفديو والنصوص وكذلك اعدادات للبرنامج

فمثلا اعدادات لغة البرنامج .

فلو اردنا عمل برنامج مثلا يوجد بة خيار اختيار اللغوكانت لغة البرنامج

الانجليزية فيمكننا ان نخزن الكلمات المناظرة بالعربية داخل ملف Resource بالبرنامج .

والان ما يهمنا هو كيفية تخزين البيانات داخل ملفات Resource وثم كيفية استخراجها

اولا يجب ان نعرف كيفية انشاء ملفات Resource تابع الصور التالية :

من هنا لمبرمجين الدلفي

يحتاج المبرمج في كثير من الأحيان في برامجه بعض الموارد الإضافية مثل أصوات و صور و أيقونات وعلب حوار أو غير ذلك.

لهذا الغرض يحتاج لربط ملفه مع الموارد التي سيحتاجها برنامجه إما على شكل ملفات يرفقها في مجلد, أو يرفقها داخل الملف التنفيذي.

نعم يمكنك دمج ملفات سيحتاجها البرنامج التنفيذي داخل الكود exe او تجمع داخل ملف Dll.

تقوم بربط ملف الكود البرمجي بملفات resources ذاة إمتداد res , هذه الملفات ذات الإمتداد res.* هي ملفات مصرفة Compiled تضم مجموعة من

الموارد التي سيحتاجها البرنامج الذي أنت بصدد تحريره.

ملف الموارد يمكنه أن يضم أي شيئ يمكنك أن تحتاجه:

- سلاسل محرفية Ressources String.

- قوائم Menu.

- علب حوار شخصية Dialogs Box.

- خطوط Fonts.

- أيقونات.

- معلومات عن إصدارة برنامجك Version Information.

- صور Bmp.

- المؤشرات Cursors.

- ملفات Wav.

- ملفات شخصية RCData ( فورم, Html, Video Files....).

- أو غيرها من الموارد كيفما كان نوعها ,ملفات فيديو, MP3, ملفات bynary, ملفات Dll, ملفات Vxd....

كما قلت ملف res.* هو ملف مصرف وملفه المصدري هو ملف نصي text يحمل عادة إمتداد rc.*. يمكنك تحرير هذا الملف في أي محرر نص ASCII editor

1- المرحة 1: احضار معلومات الواجه (الاسم اللقب السن ) من Resources ولا نكتابتها في XAML

شاهد الصورة 1

<?xml version='1.0' encoding='utf-8' ?>
<Window x:Class="ListPersonsApp.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModel="clr-namespace:ListPersonsApp.ViewModel"
    xmlns:Propertie="clr-namespace:ListPersonsApp.Properties"
    Height="305" Width="479"
    >

    <Grid Style="{DynamicResource styleMinGrid}">
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="200"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <!-- Banner Header -->
        <Grid Grid.Row="0"  Style="{DynamicResource styleBannerHeader}">
            <StackPanel Orientation="Horizontal">
                <Image Margin="5,0,0,0" Source="/ListPersonsApp;component/View/Resources/Pictures/Persons.ico" />
                <TextBlock FontSize="20" Padding="8" Text="{x:Static Propertie:Resources.ListPHeader1 }" VerticalAlignment="Center"/>
            </StackPanel>
            <!--  Skin Menu  -->
            <ComboBox HorizontalAlignment="Right" Name="ComboBoxSkin" Width="50" SelectionChanged="ComboBoxSkin_SelectionChanged">

                <ComboBoxItem Width="40" 
                            Tag="/ListPersonsApp;component/View/Resources/Skins/ExpressiveRedSkin.xaml" IsEnabled="True" IsSelected="True">
                    <StackPanel Orientation="Horizontal">
                        <Image Source="/ListPersonsApp;component/View/Resources/Pictures/ExpressiveRed.PNG" ></Image>
                    </StackPanel>
                </ComboBoxItem>

                <ComboBoxItem  Width="40"
                            Tag="/ListPersonsApp;component/View/Resources/Skins/MetalGreenSkin.xaml" IsEnabled="True" IsSelected="False">
                    <StackPanel Orientation="Horizontal">
                        <Image Source="/ListPersonsApp;component/View/Resources/Pictures/MetalGreen.PNG" ></Image>
                    </StackPanel>
                </ComboBoxItem>
            </ComboBox>

        </Grid>

        <ListView 
                Grid.Row="1" 
                ItemsSource="{Binding ListPersons}"  
                IsSynchronizedWithCurrentItem="True"
                Style="{DynamicResource StyleListViewContent}"
            Margin="5,5,5,5"
        >

            <ListView.View>
                <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Authors" ColumnHeaderContainerStyle="{DynamicResource styleButton}">
                    <GridViewColumn Header="{x:Static Propertie:Resources.ListPFieldName1}"       Width="200"    DisplayMemberBinding="{Binding Path=FirstName}" />
                    <GridViewColumn Header="{x:Static Propertie:Resources.ListPFieldLName2}"  Width="200"    DisplayMemberBinding="{Binding Path=LastName}"  />
                    <GridViewColumn Header="{x:Static Propertie:Resources.ListPFieldAge3 }"        Width="52"     DisplayMemberBinding="{Binding Path=Age}"       />
                </GridView>
            </ListView.View>
        </ListView>
        <!--/DataGridView-->
        <!--BindingNavigator-->
        <Grid Grid.Row="2" Height="20" Width="180"  >
            <Grid.ColumnDefinitions>
                <!-- ColumnFirst               |        ColumnPrevious     |          ColumnRecno          |      ColumnRecordCount        |         ColumnNext            |        ColumnLast         -->
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="50" />
                <ColumnDefinition Width="50" />
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="20" />
            </Grid.ColumnDefinitions>
            <!-- ButtonFirst -->
            <Button Grid.Column="0"  Name="BtnFirst"        Command ="{Binding FirstAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnFirst.PNG" />
                </Button.Background>
            </Button>
            <!-- ButtonPrevious -->
            <Button Grid.Column="1"  Name="BtnPRevious"     Command="{Binding PreviousAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnPrevious.PNG" />
                </Button.Background>
            </Button>
            <!-- ButtonRecno -->
            <TextBox Grid.Column="2" Name="TxRecno"         Text="{Binding Path=Recno}" FontFamily="Garamond" HorizontalAlignment="Left" />
            <!-- ButtonRecordCount -->

            <Label   Grid.Column="3" Content="Off"   FontFamily="Garamond"  HorizontalAlignment="Left"/>
            <Label   Grid.Column="3" Name="TxRecordCount"   Content ="{Binding Path=RecordCount}" FontFamily="Garamond" HorizontalAlignment="Right"/>

            <!-- ButtonNext -->
            <Button  Grid.Column="4" Name="BtnNext"         Command ="{Binding NextAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnNext.PNG" />
                </Button.Background>
            </Button>
            <!-- ButtonLast -->
            <Button  Grid.Column="5" Name="BtnLast"         Command ="{Binding LastAction}">
                <Button.Background>
                    <ImageBrush ImageSource="/ListPersonsApp;component/View/Resources/Pictures/BtnLast.PNG" />
                </Button.Background>
            </Button>
        </Grid>
        <!--/BindingNavigator-->
    </Grid>
</Window>

المحلة 2 انشاء ملف لكل لغة نريدها ان تضهر في الواجهة واحضار كل المعلومات منها .

.....

post-1034-028901900 1302424929_thumb.jpg

المرفقات
WPF-ex4-1.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 10 أبريل 2011 في 15:20

#16

المرحلة 2

كما تلاحضون ان الطريقة سلة جدا اضافه ملف لك لغة و نغير الربط بتحدبد اللغه في الواجهة لا كن هناك مشكلة لم اجد لها حل هي ان الكود تنقسه تعليم لتحديث كل العناصر الموجوده في الواجهة استعملة

            base.Initialized;

و

            Application.LoadComponent;

لكن لا نهيجو ارجوا من لديه الحل ان يكتله شكرا

اكيد ان اتغيير حصل لانه كما تلاحضون في الرسالة التي اعرضها في كل تغيير تتغيير .

كما هناك عدة طرق و في هاذه الرابطة ذريقة اخري

http://vb4arab.com/vb/showthread.php?t=62808

شكار انتضر الرد

سابحث لاحقا على الحل سنكمل مع الدروس المتبقية لاانا هدفنا هو MVVM

post-1034-018598700 1302466702_thumb.jpg

ListPersonsAppDelphi.rar

ListPersonsAppC.rar

المرفقات
WPF-ex4-2.JPG

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 11 أبريل 2011 في 22:17

#17

السلام عليكم

سنكمل إن شاء الله في الدرس التالي و داءما مع اكمال المثال

هنا سنتعلم كيف يتم استرجاع القيم بعدان تغيرة او غيرناها نحن اوضح

1- لقد عرضنا رقم السجل المحدد 1 لكنه لا يتغير مع اننا حددنا سجل اخر و كذلك عدد السجيلات من المرفروض ن اني يتغير ينضس او بزيد حسب عدد السجيلات .

لكي نقوم بهدا يجب علينا

1- انشاء حركية event عند تغيير السطر في القاءمة onselectchange .

constructor PersonCollectionViewModel;
begin
    var spersonCollectionModel: PersonCollectionModel := new PersonCollectionModel();
    self._listpersons := new List<PersonViewModel>();

    for each person: PersonModel in spersonCollectionModel.Listpersons do 
    begin
        self._listpersons.Add(new PersonViewModel(person));
    end;

    self._collectionView := CollectionViewSource.GetDefaultView(self._listpersons);
    if (self._collectionView = nil) then
       raise new NullReferenceException('_collectionView')
    else
    begin
         //self._recno:=1;
         //مع المبتود OnSelctPersonChanged
        // ربطنا الحركة CurrentChanged 
           self._collectionView.CurrentChanged += new EventHandler(OnSelctPersonChanged);
    end;


end;

  public PersonCollectionViewModel()
        {
            PersonCollectionModel spersonCollectionModel = new PersonCollectionModel();
            this._listpersons = new List<PersonViewModel>();

            foreach (PersonModel person in spersonCollectionModel.Listpersons)
            {
                this._listpersons.Add(new PersonViewModel(person));
            }


            this._collectionView = CollectionViewSource.GetDefaultView(this._listpersons);
            if (this._collectionView == null) throw new NullReferenceException("_collectionView");
            else
            {
                // ربطنا الحركة CurrentChanged 
                //مع المبتود OnSelctPersonChanged
                this._collectionView.CurrentChanged += new EventHandler(this.OnSelctPersonChanged);
            }

        }

2- ربط هذه الحركة مع ميتود تقوم باسترجاع رقم السطر .

 // اعادت حساي رقم السطر عند كل تغييير 
method PersonCollectionViewModel.OnSelctPersonChanged(sender: Object; e: EventArgs);
begin
  Recno := self._collectionView.CurrentPosition + 1
end

 // اعادت حساي رقم السطر عند كل تغييير 
        private void OnSelctPersonChanged(object sender, EventArgs e)
        {
            Recno = this._collectionView.CurrentPosition + 1;
        }

3- عرض النتيجة في الواجهة و ذلك بتحدث الخاصية recno

method PersonCollectionViewModel.set_Recno(const value: Integer); 
begin
  self._recno := value;
  OnPropertyChanged('Recno');
end;

 public int Recno
        {
            get { return this._recno; }
            set
            {
                this._recno = value;
                OnPropertyChanged("Recno");
            }
        }

OnPropertyChanged

هي ميتود موجودة في الصنف ViewModelBase ستجدونه في المرفقات لكن لم افهم هذ جيدا هدا الصنف لذلك لا شرحه الان .

اما عن عدد السجيلات سنكتبها بعد عملية الاصافة و الحدق في الدرس القادم ان شاء الله

ListPersonsAppC.rar

ListPersonsAppDelphi.rar

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 14 أبريل 2011 في 13:10

#18

2- عرض واجهة جديدة تعرض تفاصيل السجل المحدد مع امكانية الاضافة و التغيير ...الخ

#19

اول مرة اشوف الموضوع .. لي عودة وقراءة ماسبق

شكراً اخي زكيري

ربما يكمن السبب في عدم متابعة الموضوع وجوده في قسم الدلفي ...

#20

السلام عليكم

انا اسف عن الاطالة المهم

الان يمكننا انشاء الواجهة التالية كي نستطيع تكملة الدرس

تجدون في المرفقات السورس يالسي شارب و للحديث بقية حول الشرح

post-1034-087800000 1308174486_thumb.png

ListPersonsAppC.rar

المرفقات
WPF-7.png

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 16 يونيو 2011 في 00:50

#21

السلام عليكم ورحمة الله وبركاته

تحية لك اخي عبد العالي على الموضوع المميز ها انا قد عدت بعد طول عناء وخالي الوفاض

وانا الان محتاج الى فرمتة لان المعلومات متشابكة بداخل راسي ولم اعد استطيع فرزها

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

وفقنا الله واياكم لما يحبه ويرضاه

اللهم علمنا ما ينفعنا وانفعنا بما علمتنا وانفع الناس بنا واغفر لنا وارحمنا

#22

السلام عليكم

اقتباس
الان محتاج الى فرمتة لان المعلومات متشابكة بداخل راسي ولم اعد استطيع فرزها

Format A,BC,D...Z :lol:

الحمد لله لم اتقدم كثيرا في wpf مما يجعلنا نتعاون من البداية ارجوا مشاركة الشيخ مسعود .

لقد فصلت xaml في درس اخر كي نستطيع فهم االمور من البداية و و ضع امثلة بسيطة لاني هنا عقدت الامور باستخدام MVVM

#23

السلام عليكم

الان سنشرح بصفة عامة ما قمنا به :

نلاحض ان المشروع يتكون من 6 مجلدات و ملف App.xaml الذي يعتبر بوابة المشروع و هو يحتوي على اكواد XAML و ملف السورس اسمه App.xaml.cs

StartupUri="View/MainV.xaml"

هدا السطر معناه ان الواجه الاولى التي ستضهر عند تنفيذ المشروع .

مجلد Properties

وضعنا فيه ملفات اللغة التي سنعرضها في الواجة Resources.EN.resx مثلا به كلمات اللغة الانجبيزية التي ستضهر في الازرار و العناوين ...الخ

مجلد Références

نضع فيها المكتبات الخارجية من DLL و غيرها سنستعمله في مرحلة الربط مع الهيبرنت .

مجلد Common

وضعنا فيه كل االدوات و الكلاسات المشتركة بين مختلف المجلدات .

ViewModelBase.cs كلاس تقوم بالتعرف على الخاصية هل قمنا يتغييرها املا . وتقوم باعادة شحن هذه الخاصية .

RelayCommand.cs كلاس : ICommand ستطبق الواجهة لكي نستطيع استخدام الاحداث في شكل خاصيات لل VM اما ActionExecute في كلاس مساعدة فقط لتصغير الكود .

اما 3 المجلدات المتبقة وهي اللزمة لنقول اننا انشءنا MVVM لنه يتكون من

1- الواجهاة وهي اكواد XAML فقط VIEW

MainV.xaml

الواجة الرءيسية للمشروع

وهي تتكون من قسمين الراسية و التي تحتوي على الكمبوبكس لتحديد اللغة و تحديد لون الواجهة .

الوسط يحتوي تعلمات XAMLيعرض النافذتية الالخيين.

ملاحضة

   xmlns:Propertie="clr-namespace:ListPersonsApp.Properties"
        xmlns:ViewModel="clr-namespace:ListPersonsApp.ViewModel"

هنا قمنا بتعريف -namespace لكن ب XAML كما نعمل في سي using و الدلفى uses

اما الكود

<Window.DataContext>
        <ViewModel:PersonListVM />
    </Window.DataContext>

هنا قمنا لربط الواجهة الرءيسية ب VM PersonListVM عنطريق الخاصية DataContext وبذلك نستطيع التعرف على كل الخاصياة الموجدودة في هذا VM

نستطيع ان نعوضها بكود سي شارب او الدلفي كالتالي

this.DataContext = new PersonListVM();

2- PersonListV.xaml

<ListView Grid.Row="0" ItemsSource="{Binding ListPersons}"

هنا ربطنا ListView مع الخاصية ListPersons التي تعتبر قاءمة من PersonVM

DisplayMemberBinding="{Binding Path=FirstName}"

عنا ربطنا العمود مع الخاصية FirstName

Command ="{Binding FirstAction}">

هنا ربطنا النقر على الزر بالخاصية FirstAction

PersonV.xaml2

هام كود هو

 <Grid Grid.Row="0" DataContext="{Binding Person}">

اي ربط المربع مع الخاصية Person التي سترجع بدورها السجل المحدد في القاءمة

- الابجكة التى تحتوي على حقول مع get set . MODEL2

3- الكلاسات التي تحتوي على الخصاءص ذات قيم مستخرجة من الابجكة والتي ستهر في الواجة و تخزن اخبرا في الابجكة VIEWMODEL

vidio.mpg

ListPersonsAppC.rar

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 18 يونيو 2011 في 23:13

#24

السلام عليكم ورحمة الله وبركاته

يا اخي عبد العالي انا قلت اني مشوش لك تا خذ الامور خطوة بخطوة ليس من اجل ان تزيدني تشويشا

بعقلك ياصاحبي

اللهم علمنا ما ينفعنا وانفعنا بما علمتنا وانفع الناس بنا واغفر لنا وارحمنا

#25

السلام عليكم

اخي على لاريد ان اراك هنا في هذا الموضوع حتى تقرا دروس XAML وبعدها ستتضح لك الامور لانك استعملت برجة 3 طبقات من قبل ستجدMVVM سهل بكن كما قلت ابدابXAML

وللاخوة المتابعين للموضوع

سنكمل إن شاء الله .

الان يجب علينا ان

1- نقوم بعمل نجمة حمراء للحقول الاجبارية و مراقبة السن هل هو رقمي اولا 0-100 ..الخ

درسنا موجود هنا

http://vb4arab.com/vb/showthread.php?t=78806

2- اجراء اتصال مع قاعدة البيانات ساحاول عمل ذلك بهيبرنت

الى درس قادم ان شاء الله

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 21 يونيو 2011 في 00:42

مواضيع مشابهة