نظرة عامة على تأليف التحكم

قابلية التوسع لطراز عنصر التحكم Windows Presentation Foundation (WPF) يقلل بشكل كبير الحاجة إلى إنشاء عنصر تحكم جديد. مع ذلك، في بعض الحالات قد لا تزال تحتاج إلى إنشاء عنصر تحكم مخصص. يناقش هذا الموضوع الميزات التي تقلل الحاجة لإنشاء عنصر تحكم مخصص و طرازات المختلفة من تأليف التحكم في Windows Presentation Foundation (WPF). يوضح هذا الموضوع أيضاً كيفية إنشاء عنصر تحكم جديد.

يشتمل هذا الموضوع على الأقسام التالية.

  • بدائل لكتابة تحكم جديد
  • طرازات لتأليف التحكم
  • أساسيات تأليف عنصر التحكم
  • موضوعات ذات صلة

بدائل لكتابة تحكم جديد

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

  • محتوى غني. العديد من عناصر تحكم WPF القياسية تدعم المحتوى الغني. على سبيل المثال، خاصية المحتوى من Button هي من نوع Object، لذا نظرياً يمكن عرض أي شيء على Button. ليكون هناك زر لعرض صورة و نص, يمكنك إضافة صورة و TextBlock إلى StackPanel و تعيين StackPanel إلى خاصية Content. لأن عناصر التحكم يمكنها عرض العناصر المرئية WPF و البيانات الإجبارية، هناك حاجة أقل إلى إنشاء عنصر تحكم جديد أو تعديل عنصر تحكم موجود لدعم المرئيات المعقدة. للحصول على مزيد من المعلومات حول طراز المحتوى ل Button و طرازات المحتوى الأخرى في WPF, راجع طراز محتوى WPF.

  • الأنماط.Style هو مجموعة من القيم التي تمثل خصائص لعنصر التحكم. باستخدام الأنماط يمكنك إنشاء تمثيل قابل لإعادة الاستخدام لمظهر و سلوك عنصر التحكم المطلوب دون كتابة عنصر تحكم جديد. على سبيل المثال، افترض أنك تريد الجميع الخاص بك TextBlockعناصر إلى لديها خط أحمر، الخط Arial بالحجم خط مقداره 14. يمكنك إنشاء النمط كـ مورد ثم قم بتعيين الخصائص المناسبة تبعاً لذلك. ثم كل TextBlock قمت بإضافته إلى التطبيق الخاص بك سيكون له نفس المظهر.

  • قوالب البيانات.DataTemplate يُمكنك من تخصيص كيفية عرض البيانات على عنصر التحكم. على سبيل المثال، DataTemplate يمكن استخدامه لتحديد كيفية عرض البيانات في ListBox. لمثال على ذلك، راجع نظرة عامة حول قولبة البيانات. بالإضافة إلى تخصيص مظهر البيانات, DataTemplate يمكن أن يتضمن عناصر واجهة المستخدم التي تمنحك الكثير من المرونة في UIs المخصصة. على سبيل المثال، باستخدام DataTemplate، يمكنك إنشاء ComboBox الذي يحتوي فيها كل عنصر على خانة الاختيار.

  • قوالب عناصر التحكم. العديد من عناصر التحكم في WPF تستخدم ControlTemplate لتعريف بنية و مظهر عنصر التحكم الذي يفصل مظهر عنصر تحكم عن وظيفة عنصر التحكم. يمكنك تغيير مظهر عنصر التحكم بشكل كبير عن طريق إعادة تعريف ControlTemplate الخاص به. على سبيل المثال، افترض أنك تريد عنصر التحكم أن يبدو مثل stoplight. عنصر التحكم هذا يحتوي على واجهة مستخدم بسيطة و الأداء الوظيفي. عنصر التحكم هو ثلاث دوائر, واحدة فقط منها في كل مرة يمكن أن تُنار. بعد بعض الانعكاس, يمكنك إدراك أن RadioButton يوفر وظيفة أن واحد فقط يتم تحديده في المرة و لكن المظهر الافتراضي لRadioButton لا يشبه الأنوار على stoplight. لأن RadioButton يستخدم قالب عنصر تحكم لتعريف المظهر الخاص به, من السهل إعادة تعريف ControlTemplate ليناسب متطلبات عنصر التحكم، و يستخدم أزرار الاختيار لجعل stoplight الخاص بك.

    ملاحظةملاحظة

    على الرغم من أن RadioButton يمكنه استخدام DataTemplate، DataTemplate هو غير كافي في هذا المثال.DataTemplate يعرف مظهر محتوى عنصر التحكم.في حالة RadioButton، المحتوى هو ما يظهر إلى يمين الدائرة التي تشير إلى ما إذا كان RadioButton محدد.في مثال stoplight, الزر التبادلي يحتاج فقط أن يكون دائرة يمكن أن تُضاء. بسبب أن متطلبات مظهر stoplight تختلف عن المظهر الافتراضي لRadioButton, من الضروري إعادة تعريف ControlTemplate.بشكل عام, يستخدم DataTemplate لتعريف المحتوى (أو البيانات) من عنصر التحكم، و يستخدم ControlTemplate لتعريف كيفية بناء عنصر التحكم.

  • المشغّلات.Trigger يسمح لك أن تغير بشكل حيوي مظهر و سلوك عنصر تحكم بدون إنشاء عنصر التحكم الجديد. على سبيل المثال، افترض أن لديك عدة عناصر تحكم ListBox في التطبيق الخاص بك و تريد العناصر في كل ListBox أن تكون غامقة و حمراء عند تحديدها. قد تكون الغريزة الأولى لك إنشاء فئة ترث من ListBox و تجاوز أسلوب OnSelectionChanged لتغيير مظهر العنصر المحدد و لكن الأسلوب الأفضل هو إضافة مشغل إلى نمط من ListBoxItem الذي يغير من مظهر العنصر المحدد. المشغل يمكنك من تغيير قيم الخصائص أو اتخاذ إجراءات تستند إلى قيمة الخاصية. EventTrigger يمكنك من اتخاذ إجراءات عند حدوث حدث ما.

للحصول على مزيد من المعلومات حول الأنماط، قوالب, و المشغلات، راجع التنسيق و القولبة.

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

طرازات لتأليف التحكم

طرازات المحتوى الغني, الأنماط, القوالب و المشغّلات تقوم بتقليل حاجتك إلى إنشاء عنصر تحكم جديد. مع ذلك، إذا كنت بحاجة إلى إنشاء عنصر تحكم جديد, من المهم أن تفهم طرازات تأليف التحكم المختلفة في WPF.WPF يوفر ثلاثة طرازات عامة لإنشاء عنصر تحكم, توفر كل منها مجموعة مختلفة من الميزات و مستوى من المرونة. الفئات الأساسية للطرازات الثلاثة هم UserControl، Control، و FrameworkElement.

الاشتقاق من UserControl

إن أبسط طريقة لإنشاء عنصر تحكم في WPF هي أن تشتق من UserControl. عند إنشاء عنصر تحكم يرث من UserControl، تقوم بإضافة المكونات الموجودة إلى UserControl، تسمية المكونات، و قم بالإشارة إلى معالجات الأحداث في Extensible Application Markup Language (XAML). يمكنك بعد ذلك الإشارة إلى العناصر المسماة و تعريف معالجات الأحداث في التعليمات البرمجية. طراز تطوير هذا يشبه جداً الطراز المستخدم لتطوير التطبيق في WPF.

إذا تم البناء بشكل صحيح، UserControl يمكنه الاستفادة من فوائد المحتوى الغني, الأنماط، و المشغلات. ومع ذلك، إذا كان عنصر التحكم الخاص بك يرث من UserControl، الأشخاص الذين يستخدمون عنصر التحكم الخاص بك لن يتمكنوا من استخدام DataTemplate أو ControlTemplate لتخصيص المظهر الخاص به. من الضروري أن تشتق من فئة Control أو أحدى الفئات المشتقة منها (غير UserControl) لإنشاء عنصر تحكم مخصص يدعم القوالب.

فوائد الاشتقاق من UserControl

خذ بعين الاعتبار الاشتقاق من UserControl إذا كان كل التالي مطبق:

  • تريد إنشاء عنصر التحكم الخاص بك بشكل مشابه إلى كيفية إنشاء أحد تطبيقات.

  • يحتوي عنصر التحكم الخاص بك فقط على المكونات الموجودة.

  • لا تحتاج لدعم التخصيص المعقد.

الاشتقاق من عنصر تحكم

الاشتقاق من فئة Control هو الطراز المستخدم من قبل معظم عناصر التحكم WPF الموجودة. عند إنشاء عنصر تحكم يرث من فئة Control. تقوم بتعريف مظهره باستخدام القوالب. بالقيام بذلك، فانك تفصل منطق العمل من التمثيل المرئي. يمكنك التأكد أيضاً من فك الارتباط من UI و المنطق باستخدام الأوامر و الروابط بدلاً من الأحداث و تجنب مرجعية العناصر في ControlTemplate كلما أمكن ذلك. إذا كان UI و المنطق من عنصر التحكم الخاص بك تم فك ارتباطهم بشكل صحيح, المستخدم لعنصر التحكم الخاص بك يمكنه إعادة تعريف ControlTemplate الخاص بعنصر التحكم لتخصيص مظهره. على الرغم من أن إنشاء Control مخصص غير بسيط مثل إنشاء UserControl، Control مخصص يوفر أكثر مرونة.

فوائد الاشتقاق من عنصر تحكم

خذ بعين الاعتبار الاشتقاق من Control بدلاً من استخدام فئة UserControl إذا كان أي من التالي مطبق:

  • تريد مظهر عنصر التحكم الخاص بك أن يكون قابل للتخصيص عبر ControlTemplate.

  • تريد عنصر التحكم الخاص بك أن يدعم سمات مختلفة.

الاشتقاق من FrameworkElement

عناصر التحكم التي تشتق من UserControl أو Control تعتمد على إنشاء عناصر موجودة. للعديد من السيناريوهات, يكون هذا حل مقبول, لأن أي كائن يرث من FrameworkElement يمكن أن يكون في ControlTemplate. مع ذلك، توجد أوقات عندما يتطلب مظهر عنصر التحكم أكثر من وظائف تركيب عنصر بسيط. لهذه السيناريوهات, إسناد مكون على FrameworkElement هو الاختيار الصحيح.

توجد طريقتان قياسيتين لإنشاء مكونات مستندة إلى FrameworkElement: تقديم مباشر و تركيب عنصر مخصص. التقديم المباشر يتضمن تجاوز أسلوب OnRender من FrameworkElement و يوفر عمليات DrawingContext التي بوضوح تعرف مرئيات المكون. هذه هي الطريقة التي يستخدمها Image و Border. يتضمن تركيب عنصر مخصص استخدام كائنات من نوع Visual لإنشاء مظهر المكون الخاص بك. على سبيل المثال ، راجع استخدام كائنات DrawingVisual. Trackهو مثالاً لعنصر تحكم فيWPFيستخدم عنصر مخصص التركيب. من الممكن أيضاً مزج التقديم المباشر و تركيب العنصر المخصص في نفس عنصر التحكم.

فوائد الاشتقاق من FrameworkElement

خذ بعين الاعتبار الاشتقاق من FrameworkElement إذا كان أي من التالي مطبق:

  • تريد أن يكون لك تحكم دقيق على مظهر عنصر التحكم الخاص بك بعد ما تم توفيره بواسطة إنشاء عنصر بسيط.

  • تريد أن تعرف مظهر عنصر التحكم الخاص بك بواسطة تعريف منطق التقديم الخاص بك.

  • تحتاج إلى إنشاء عناصر موجودة في طرق novel التي تجاوز ما هو ممكن مع UserControl و Control.

أساسيات تأليف عنصر التحكم

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

استخدم الخصائص التبعية

عند تكون الخاصية خاصية تبعية, من الممكن القيام بما يلي:

  • تعيين الخاصية في نمط.

  • ربط الخاصية إلى مصدر البيانات.

  • استخدم مورد حيوي كقيمة الخاصية.

  • تحريك الخاصية.

إذا كنت تريد خاصية من عنصر التحكم الخاص بك أن تدعم أي من هذه الوظائف, يجب تنفيذها كخاصية تبعية. يحدد المثال التالي خاصية تبعية باسم Value عن طريق القيام بما يلي:

  • تعريف معرّف DependencyProperty باسم ValueProperty مثل حقل public static readonly.

  • تسجيل اسم الخاصية مع النظام الخاصية, عن طريق استدعاء DependencyProperty.Register، لتحديد ما يلي:

    • اسم الخاصية.

    • نوع الخاصية.

    • النوع الذي يملك الخاصية.

    • بيانات تعريف الخاصية. بيانات التعريف تحتوي على القيمة الافتراضية للخاصية, CoerceValueCallback و PropertyChangedCallback.

  • تعريف خاصية برنامج تضمين CLR المسمى Value, و الذي هو نفس الاسم المستخدم لتسجيل الخاصية التبعية، عن طريق تطبيق موصلات get و set التابعين للخاصية. لاحظ أن موصلات get و set تستدعي فقط GetValue و SetValue على التوالي. من المستحسن أن موصلات الخصائص التبعية لا تحتوي على منطق إضافية لأن العملاء و WPF يمكنهم تجاوز الموصلات و استدعاء GetValue و SetValue مباشرة. على سبيل المثال, عند تكون الخاصية موثوقة إلى مصدر بيانات, لا يتم استدعاء موصل set الخاص بالخاصية. بدلاً من إضافة منطق إضافي للحصول على و تعيين الموصلات, استخدم مفوضين ValidateValueCallback ، CoerceValueCallback ، و PropertyChangedCallback للاستجابة إلى أو التحقق من القيمة عند تغييرها. للحصول على مزيد من المعلومات حول عمليات الاسترجاعات, راجع استدعاء خاصية التبعية و التحقق من صحتها.

  • تحديد أسلوب ل CoerceValueCallbackتسمية CoerceValue. CoerceValueويضمنValueهو أكبر أو تساوي إلىMinValueوأقل من أو تساوي إلىMaxValue.

  • تحديد أسلوب PropertyChangedCallback، يسمى OnValueChanged. OnValueChangedبإنشاءRoutedPropertyChangedEventArgs<T>الكائن وتحضير إلى رفعValueChangedتوجيه الأحداث. تتم مناقشة الأحداث الموجهة في المقطع التالي.

        ''' <summary>
        ''' Identifies the Value dependency property.
        ''' </summary>
        Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Decimal), GetType(NumericUpDown), New FrameworkPropertyMetadata(MinValue, New PropertyChangedCallback(AddressOf OnValueChanged), New CoerceValueCallback(AddressOf CoerceValue)))

        ''' <summary>
        ''' Gets or sets the value assigned to the control.
        ''' </summary>
        Public Property Value() As Decimal
            Get
                Return CDec(GetValue(ValueProperty))
            End Get
            Set(ByVal value As Decimal)
                SetValue(ValueProperty, value)
            End Set
        End Property

        Private Shared Overloads Function CoerceValue(ByVal element As DependencyObject, ByVal value As Object) As Object
            Dim newValue As Decimal = CDec(value)
            Dim control As NumericUpDown = CType(element, NumericUpDown)

            newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue))

            Return newValue
        End Function

        Private Shared Sub OnValueChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
            Dim control As NumericUpDown = CType(obj, NumericUpDown)

            Dim e As New RoutedPropertyChangedEventArgs(Of Decimal)(CDec(args.OldValue), CDec(args.NewValue), ValueChangedEvent)
            control.OnValueChanged(e)
        End Sub
/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register(
        "Value", typeof(decimal), typeof(NumericUpDown),
        new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
                                      new CoerceValueCallback(CoerceValue)));

/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{          
    get { return (decimal)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

private static object CoerceValue(DependencyObject element, object value)
{
    decimal newValue = (decimal)value;
    NumericUpDown control = (NumericUpDown)element;

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));

    return newValue;
}

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    NumericUpDown control = (NumericUpDown)obj;         

    RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>(
        (decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
    control.OnValueChanged(e);
}

لمزيد من المعلومات، راجع خصائص التبعية المخصصة.

استخدام الأحداث الموجهة

تماماً مثل الخصائص التبعية تقوم بتوسيع فكرة خصائص CLR مع الوظائف الإضافية، الأحداث الموجهة تقوم بتوسيع الفكرة لأحداث CLR القياسية. عند إنشاء عنصر تحكم WPF جديد، من المفيد أيضاً تنفيذ الحدث كحدث موجه لأن الحدث الموجه يدعم السلوك التالي:

  • يمكن معالجة الأحداث على أصل عناصر تحكم متعددة. إذا كان الحدث حدث فقاعي, يمكن لأصل واحد في شجرة العنصر أن يشترك إلى الحدث. ثم يمكن لكتّاب التطبيق استخدام معالج واحد للاستجابة إلى الحدث من عناصر تحكم متعددة. على سبيل المثال، إذا كان عنصر التحكم الخاص بك جزءًا من كل عنصر في ListBox(لأنه يتم تضمينه في DataTemplate)، يمكن لمطور التطبيق تعريف معالج الحدث لحدث عنصر التحكم الخاص بك على ListBox. كلما يحدث الحدث على أيّ من عناصر التحكم, يتم استدعاء معالج الحدث.

  • يمكن استخدام الأحداث الموجهة في EventSetter، والذي يمكّن مطوري التطبيقات من تحديد معالج حدث ضمن نمط.

  • يمكن استخدام الأحداث الموجهة في EventTrigger، والذي يكون مفيداً لتحريك الخصائص باستخدام XAML. لمزيد من المعلومات، راجع نظرة عامة حول الحركة.

يعرف المثال التالي حدث موجّه عن طريق القيام بما يلي:

  • تعريف معرّف RoutedEvent باسم ValueChangedEvent مثل حقل public static readonly.

  • تسجيل الحدث الموجه عن طريق استدعاء أسلوب EventManager.RegisterRoutedEvent. يحدد المثال المعلومات التالية عندما تستدعي RegisterRoutedEvent:

    • اسم الحدث هو ValueChanged.

    • استراتيجية التوجيه هي Bubble, و هذا يعني أنه يتم استدعاء أولاً معالج الحدث على المصدر (الكائن الذي رفع الحدث), و ثم يتم استدعاء معالجات الأحداث على عناصر أصل المصدر على التوالي، بدءاً بمعالج الحدث على أقرب عنصر أصل.

    • نوع معالج الحدث هو RoutedPropertyChangedEventHandler<T>، المُنشَئ مع نوع Decimal.

    • النوع المالك للحدث هو NumericUpDown.

  • أعلن عن حدث عام مسمى ValueChanged و يتضمن تصريحات موصل الحدث. يستدعي المثال AddHandler في تعريف موصل add و RemoveHandler في تعريف موصل remove لاستخدام خدمات حدث WPF.

  • إنشاء أسلوب محمي، ظاهري باسم OnValueChanged الذي يرفع حدث ValueChanged.

        ''' <summary>
        ''' Identifies the ValueChanged routed event.
        ''' </summary>
        Public Shared ReadOnly ValueChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, GetType(RoutedPropertyChangedEventHandler(Of Decimal)), GetType(NumericUpDown))

        ''' <summary>
        ''' Occurs when the Value property changes.
        ''' </summary>
        Public Custom Event ValueChanged As RoutedPropertyChangedEventHandler(Of Decimal)
            AddHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
                MyBase.AddHandler(ValueChangedEvent, value)
            End AddHandler
            RemoveHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
                MyBase.RemoveHandler(ValueChangedEvent, value)
            End RemoveHandler
            RaiseEvent(ByVal sender As System.Object, ByVal e As RoutedPropertyChangedEventArgs(Of Decimal))
            End RaiseEvent
        End Event

        ''' <summary>
        ''' Raises the ValueChanged event.
        ''' </summary>
        ''' <param name="args">Arguments associated with the ValueChanged event.</param>
        Protected Overridable Sub OnValueChanged(ByVal args As RoutedPropertyChangedEventArgs(Of Decimal))
            MyBase.RaiseEvent(args)
        End Sub
/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
    "ValueChanged", RoutingStrategy.Bubble, 
    typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown));

/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
{
    add { AddHandler(ValueChangedEvent, value); }
    remove { RemoveHandler(ValueChangedEvent, value); }
}

/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
{
    RaiseEvent(args);
}

للمزيد من المعلومات، راجع نظرة عامة حول الأحداث الموجهة وكيفية القيام بما يلي: إنشاء حدث موجه مخصص.

استخدم الربط

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

يقوم المثال التالي بتحديث TextBlock من عنصر تحكم NumericUpDown, تعيين اسم له و الإشارة إلى مربع النص حسب الاسم في التعليمات البرمجية.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
  <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
        Private Sub UpdateTextBlock()
            valueText.Text = Value.ToString()
        End Sub
private void UpdateTextBlock()
{
    valueText.Text = Value.ToString();
}

يستخدم المثال التالي الربط لتحقيق نفس الشيء.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">

    <!--Bind the TextBlock to the Value property-->
    <TextBlock 
        Width="60" TextAlignment="Right" Padding="5"
        Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type local:NumericUpDown}}, 
                       Path=Value}"/>

</Border>

لمزيد من المعلومات حول ربط البيانات، راجع نظرة عامة لربط البيانات.

التصميم للمصممين

لتلقي الدعم لعناصر التحكم WPF المخصصة في مصمم WPF لـ Visual Studio (على سبيل المثال، تحرير خاصية مع إطار خصائص)، اتبع هذه الإرشادات. للحصول على مزيد من المعلومات حول تطوير مصمم WPF, راجع مصمم WPF.

الخصائص التبعية

تأكد من تنفيذ CLR get و set الموصلات كما هو موضح سابقاً، في "استخدام الخصائص التبعية." يستخدم المصممين برنامج التضمين للكشف عن وجود خاصية تبعية، و لكنهم, مثل WPF و عملاء عنصر التحكم, ليس مطلوب منهم استدعاء الموصلات عند الحصول على أو تعيين الخاصية.

الخصائص المرفقة

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

  • أن يكون لديك public static readonly DependencyProperty من نموذج PropertyNameProperty الذي تم إنشاؤه باستخدام أسلوب RegisterAttached. اسم الخاصية الذي تم تمريره إلى RegisterAttached يجب أن يطابق PropertyName.

  • تنفيذ زوج من أساليب CLR public static باسم SetPropertyName و GetPropertyName. يجب على كلا الأسلوبين قبول فئة مشتقة من DependencyProperty كوسيطتهم الأولى. أسلوب Set PropertyName أيضاً يقبل وسيطة لها نوع يطابق نوع البيانات المسجلة من الخاصية. أسلوب GetPropertyName يجب أن يرجع قيمة من نفس النوع. إذا كان أسلوب Set PropertyName مفقود، يتم وضع علامة على الخاصية أنه للقراءة فقط.

  • SetPropertyName و GetPropertyName يجب أن يتوجه مباشرة إلى أساليب GetValue و SetValue على كائن تبعية الهدف، على التوالي. قد يقوم المصممين بوصول للخاصية المرفقة عن طريق الاستدعاء عبر برنامج تضمين الأسلوب أو إجراء استدعاء مباشر إلى كائن تبعية الهدف.

للحصول على مزيد من المعلومات حول الخصائص المرفقة, راجع نظرة عامة حول الخصائص المرفقة.

تعريف و استخدام الموارد المشتركة

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

عندما يبحث التطبيق عن مورد، فإنه يبحث في ثلاثة مستويات بالترتيب التالي:

  1. مستوى العنصر.

    يبدأ النظام بالعنصر الذي يشير إلى المورد و ثم يبحث موارد الأصل المنطقي و هكذا إلى أن يتم الوصول إلى العنصر الجذر.

  2. مستوى التطبيق.

    الموارد المعرّفة بواسطة كائن Application.

  3. مستوى السمة.

    قواميس مستوى السمة تكون مخزنة في مجلد فرعي مسمى سمات. الملفات الموجودة في مجلد السمات صواب تتوافق مع السمات. على سبيل المثال، قد يكون لديك Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml ، و هكذا. يمكنك أيضاً الحصول على ملف مسمى generic.xaml. عندما يبحث النظام عن مورد في مستوى السمات، أولاً يقوم بالبحث عنه في الملف السمة الخاصة و ثم يبحث عنه في generic.xaml.

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

تعريف موارد في مستوى العنصر

يمكنك تعريف الموارد المشتركة على مستوى العنصر بواسطة إنشاء قاموس مورد مخصص و دمجها مع قاموس مورد عنصر التحكم الخاص بك. عند استخدام هذه الطريقة، يمكنك تسمية ملف المورد الخاص بك بأي شيء تريده, و يمكن أن يكون في نفس المجلد كعناصر التحكم الخاصة بك. الموارد عند مستوى العنصر يمكنها أيضاً استخدام سلاسل بسيطة كمفاتيح. يقوم المثال التالي بإنشاء ملف مورد LinearGradientBrush مسمى Dictionary1.xaml.

<ResourceDictionary 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">
  <LinearGradientBrush 
    x:Key="myBrush"  
    StartPoint="0,0" EndPoint="1,1">
    <GradientStop Color="Red" Offset="0.25" />
    <GradientStop Color="Blue" Offset="0.75" />
  </LinearGradientBrush>

</ResourceDictionary>

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

يقوم المثال التالي بدمج قاموس المورد باستخدام XAML.

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

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

يقوم المثال التالي بإنشاء فئة تقوم بإرجاع ResourceDictionary مشترك.

internal static class SharedDictionaryManager
{
    internal static ResourceDictionary SharedDictionary
    {
        get
        {
            if (_sharedDictionary == null)
            {
                System.Uri resourceLocater =
                    new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml", 
                                    System.UriKind.Relative);

                _sharedDictionary = 
                    (ResourceDictionary)Application.LoadComponent(resourceLocater);
            }

            return _sharedDictionary;
        }
    }

    private static ResourceDictionary _sharedDictionary;
}

يقوم المثال التالي بدمج المورد المشترك مع الموارد من عنصر تحكم مخصص في منشئ عنصر التحكم قبل أن تستدعي InitilizeComponent. لأن SharedDictionaryManager.SharedDictionary هي خاصية ثابتة ، يتم إنشاء ResourceDictionary مرة واحدة فقط. لأنه تم دمج قاموس المورد قبل استدعاء InitializeComponent, الموارد متوفرة إلى عنصر تحكم في ملف XAML الخاص به.

public NumericUpDown()
{
    this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
    InitializeComponent();

}

تعريف موارد في مستوى السمة

WPF يمكنك من إنشاء موارد لسمات Windows المختلفة. ككاتب تحكم, يمكنك تعريف مورد لسمة معينة لتغيير مظهر عنصر التحكم اعتماداً على ما هي السمة قيد الاستخدام. على سبيل المثال، مظهر Button في سمة Windows الكلاسيكية (السمة الافتراضية لنظام التشغيل Windows 2000) يختلف عن Button في سمة Luna Windows (السمة الافتراضية لـ Windows XP) لأن Button يستخدم ControlTemplate مختلف لكل سمة.

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

اسم ملف قاموس المورد

سمة Windows

Classic.xaml

مظهر كلاسيكي Windows 9 x / 2000 على Windows XP

Luna.NormalColor.xaml

السمة الزرقاء الافتراضية على Windows XP

Luna.Homestead.xaml

السمة الزيتية على Windows XP

Luna.Metallic.xaml

السمة الفضية على Windows XP

Royale.NormalColor.xaml

السمة الافتراضية على Windows XP Media Center Edition

Aero.NormalColor.xaml

السمة الافتراضية على Windows Vista

لا تحتاج إلى تعريف موارد عن كل سمة. إذا لم يتم تعريف مورد لسمة معينة، إذاً يستخدم عنصر التحكم المورد العام, الذي يكون في ملف قاموس المورد المسمى generic.xaml في نفس المجلد كملفات قاموس المورد محدد السمة. على الرغم من أن generic.xaml لا تقابل سمة Windows معينة, هي لا تزال قاموس مستوى السمة.

نموذج عنصر تحكم مخصص NumericUpDown مع سمة و دعم أتمتة UI يحتوي على اثنين من قواميس المورد لعنصر التحكم NumericUpDown: واحد يكون في generic.xaml و واحد يكون في Luna.NormalColor.xaml. يمكنك تشغيل التطبيق و التبديل بين السمة الفضية في Windows XP و سمة أخرى لمعرفة الاختلاف بين قوالب عنصري التحكم . (إذا كنت تقوم بتشغيل Windows Vista, يمكنك إعادة تسمية Luna.NormalColor.xaml إلى Aero.NormalColor.xaml و التبديل بين اثنين من السمات مثل Windows الكلاسيكي و السمة الافتراضية لنظام التشغيل Windows Vista.)

عندما تضع ControlTemplate في أي من ملفات قاموس مورد سمة خاصة، يجب عليك إنشاء مُنشئ ثابت لعنصر التحكم الخاص بك و استدعاء أسلوب OverrideMetadata(Type, PropertyMetadata) على DefaultStyleKey، كما هو موضح في المثال التالي.

        Shared Sub New()
            DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
        End Sub
static NumericUpDown()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
               new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}

تعريف و الاشارة إلى مفاتيح لموارد السمة

عند تعريف مورد عند مستوى العنصر، يمكنك تعيين سلسلة كمفتاحه ثم الوصول إلى المورد عبر السلسلة. عند تعريف مورد عند مستوى السمة، يجب عليك استخدام ComponentResourceKey كالمفتاح. يعرف المثال التالي مورد في generic.xaml.

<LinearGradientBrush 
     x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter}, 
                                  ResourceId=MyEllipseBrush}"  
                                  StartPoint="0,0" EndPoint="1,0">
    <GradientStop Color="Blue" Offset="0" />
    <GradientStop Color="Red" Offset="0.5" />
    <GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>

المثال التالي يشير إلى المورد عن طريق تحديد ComponentResourceKey كمفتاح.

<RepeatButton 
    Grid.Column="1" Grid.Row="0"
    Background="{StaticResource {ComponentResourceKey 
                        TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                        ResourceId=ButtonBrush}}">
    Up
</RepeatButton>
<RepeatButton 
    Grid.Column="1" Grid.Row="1"
    Background="{StaticResource {ComponentResourceKey 
                    TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                    ResourceId=ButtonBrush}}">
    Down
 </RepeatButton>

تحديد موقع موارد النسق

لإيجاد الموارد لعنصر تحكم, يحتاج التطبيق المضيف إلى معرفة أن التجميع يحتوى على موارد عنصر التحكم خاصة. يمكنك تحقيق ذلك عن طريق إضافة ThemeInfoAttribute إلى التجميع الذي يحتوي على عنصر التحكم. ThemeInfoAttribute يحتوي خاصية GenericDictionaryLocation التي تحدد موقع الموارد العامة و خاصية ThemeDictionaryLocation التي تحدد موقع الموارد الخاصة بالسمة.

يقوم المثال التالي بتعيين خصائص GenericDictionaryLocation و ThemeDictionaryLocation إلى SourceAssembly ، لتحديد أن الموارد العامة و الخاصة بالسمة موجودة في نفس التجميع مع عنصر التحكم.

<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, 
           ResourceDictionaryLocation.SourceAssembly)]

راجع أيضًا:

المبادئ

حزمة ال URIفى WPF

موارد أخرى

مصمم WPF

تخصيص عنصر التحكم