سؤال

عندما أرغب في ربط عنصر تحكم بممتلكات مثالي، يجب أن أقدم اسم العقار كسلسلة. هذا ليس جيدا جدا لأن:

  1. إذا تمت إزالة العقار أو إعادة تسميته، فلا أحصل على تحذير مترجم.
  2. إذا قمت بإعادة تسمية الممتلكات باستخدام أداة Refactoring، فمن المحتمل عدم تحديث ملزمة البيانات.
  3. لا أحصل على خطأ حتى وقت التشغيل إذا كان نوع العقار خاطئ، على سبيل المثال إرساء عدد صحيح لتقطيع التاريخ.

هل يوجد نمط تصميم يمتد هذا، ولكن لا يزال لديه سهولة استخدام البيانات الملزمة؟

(هذه مشكلة في WinForm و ASP.NET و WPF والأكثر احتمالا الكثير من الأنظمة الأخرى)

لقد وجدت الآن "حلول الحلول ل NameOf () المشغل في C #: قواعد البيانات المحكم"يحتوي أيضا على نقطة انطلاق جيدة للحل.

إذا كنت على استعداد لاستخدام معالج منشور بعد تجميع التعليمات البرمجية الخاصة بك، notifypropertyweaver. تستحق النظر في.


أي شخص يعرف حل جيد ل WPF عند الانتهاء من الارتباطات في XML بدلا من ذلك؟

هل كانت مفيدة؟

المحلول

بفضل Oliver لأنني بدأت لدي الآن حلا يدعم كلاهما إعادة الإنفاق وهو نوع آمن. كما سمح لي بتنفيذ inotifypropertychanged بحيث تتناول الخصائص التي يتم إعادة تسميتها.

يشبه الاستخدام:

checkBoxCanEdit.Bind(c => c.Checked, person, p => p.UserCanEdit);
textBoxName.BindEnabled(person, p => p.UserCanEdit);
checkBoxEmployed.BindEnabled(person, p => p.UserCanEdit);
trackBarAge.BindEnabled(person, p => p.UserCanEdit);

textBoxName.Bind(c => c.Text, person, d => d.Name);
checkBoxEmployed.Bind(c => c.Checked, person, d => d.Employed);
trackBarAge.Bind(c => c.Value, person, d => d.Age);

labelName.BindLabelText(person, p => p.Name);
labelEmployed.BindLabelText(person, p => p.Employed);
labelAge.BindLabelText(person, p => p.Age);

يظهر فئة الشخص كيفية تنفيذ InotifyPropertychanged بطريقة آمنة من النوع (أو انظر هذه الإجابة للحصول على وسيلة أخرى لطيفة إلى حد ما لتنفيذ inotifypropertychanged، ActiveSharp - التلقائي inotifypropertychanged تبدو أيضا جيدة):

public class Person : INotifyPropertyChanged
{
   private bool _employed;
   public bool Employed
   {
      get { return _employed; }
      set
      {
         _employed = value;
         OnPropertyChanged(() => c.Employed);
      }
   }

   // etc

   private void OnPropertyChanged(Expression<Func<object>> property)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, 
             new PropertyChangedEventArgs(BindingHelper.Name(property)));
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;
}

فئة المساعد الرملية WinForms لديها اللحوم في ذلك يجعل كل شيء يعمل:

namespace TypeSafeBinding
{
    public static class BindingHelper
    {
        private static string GetMemberName(Expression expression)
        {
            // The nameof operator was implemented in C# 6.0 with .NET 4.6
            // and VS2015 in July 2015. 
            // The following is still valid for C# < 6.0

            switch (expression.NodeType)
            {
                case ExpressionType.MemberAccess:
                    var memberExpression = (MemberExpression) expression;
                    var supername = GetMemberName(memberExpression.Expression);
                    if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;
                    return String.Concat(supername, '.', memberExpression.Member.Name);
                case ExpressionType.Call:
                    var callExpression = (MethodCallExpression) expression;
                    return callExpression.Method.Name;
                case ExpressionType.Convert:
                    var unaryExpression = (UnaryExpression) expression;
                    return GetMemberName(unaryExpression.Operand);
                case ExpressionType.Parameter:
                case ExpressionType.Constant: //Change
                    return String.Empty;
                default:
                    throw new ArgumentException("The expression is not a member access or method call expression");
            }
        }

        public static string Name<T, T2>(Expression<Func<T, T2>> expression)
        {
            return GetMemberName(expression.Body);
        }

        //NEW
        public static string Name<T>(Expression<Func<T>> expression)
        {
           return GetMemberName(expression.Body);
        }

        public static void Bind<TC, TD, TP>(this TC control, Expression<Func<TC, TP>> controlProperty, TD dataSource, Expression<Func<TD, TP>> dataMember) where TC : Control
        {
            control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));
        }

        public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)
        {
            // as this is way one any type of property is ok
            control.DataBindings.Add("Text", dataObject, Name(dataMember));
        }

        public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)
        {       
           control.Bind(c => c.Enabled, dataObject, dataMember);
        }
    }
}

هذا يجعل استخدام الكثير من الأشياء الجديدة في C # 3.5 ويظهر فقط ما هو ممكن. الآن إذا كان لدينا فقط وحدات الماكرو الصحية قد يتوقف مبرمج LISP الاتصال بالمواطنين من الدرجة الثانية)

نصائح أخرى

تم تنفيذ مشغل NameOf في C # 6.0 مع .NET 4.6 و VS2015 في يوليو 2015. وما يلي ساري المفعول ل C # <6.0

لتجنب السلاسل التي تحتوي على أسماء الممتلكات، كتبت فئة بسيطة باستخدام أشجار التعبير لإرجاع اسم العضو:

using System;
using System.Linq.Expressions;
using System.Reflection;

public static class Member
{
    private static string GetMemberName(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression) expression;
                var supername = GetMemberName(memberExpression.Expression);

                if (String.IsNullOrEmpty(supername))
                    return memberExpression.Member.Name;

                return String.Concat(supername, '.', memberExpression.Member.Name);

            case ExpressionType.Call:
                var callExpression = (MethodCallExpression) expression;
                return callExpression.Method.Name;

            case ExpressionType.Convert:
                var unaryExpression = (UnaryExpression) expression;
                return GetMemberName(unaryExpression.Operand);

            case ExpressionType.Parameter:
                return String.Empty;

            default:
                throw new ArgumentException("The expression is not a member access or method call expression");
        }
    }

    public static string Name<T>(Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

    public static string Name<T>(Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }
}

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

var name = Member.Name<MyClass>(x => x.MyProperty); // name == "MyProperty"

حتى الآن لم أجد أي شيء يحل مشكلة بيانات تصنيف البيانات.

مع أطيب التحيات

الإطار 4.5 يوفر لنا CallerMemberNameAttribute, ، مما يجعل تمرير اسم العقار كسلسلة غير ضرورية:

private string m_myProperty;
public string MyProperty
{
    get { return m_myProperty; }
    set
    {
        m_myProperty = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
    // ... do stuff here ...
}

إذا كنت تعمل على الإطار 4.0 مع KB2468871. تثبيت، يمكنك تثبيت حزمة توافق Microsoft BCL عبر Nuget., والتي توفر هذه السمة أيضا.

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

قد تكون الميكانيكا الفعلية قبيحة بعض الشيء، لكنها ستظل آمنة من النوع، وأداء متساو تقريبا إلى InotifyPropertychanged.

شيء من نوعه مثل هذا:

public class DummyViewModel : ViewModelBase
{
    private class DummyViewModelPropertyInfo
    {
        internal readonly string Dummy;

        internal DummyViewModelPropertyInfo(DummyViewModel model)
        {
            Dummy = BindingHelper.Name(() => model.Dummy);
        }
    }

    private static DummyViewModelPropertyInfo _propertyInfo;
    private DummyViewModelPropertyInfo PropertyInfo
    {
        get { return _propertyInfo ?? (_propertyInfo = new DummyViewModelPropertyInfo(this)); }
    }

    private string _dummyProperty;
    public string Dummy
    {
        get
        {
            return this._dummyProperty;
        }
        set
        {
            this._dummyProperty = value;
            OnPropertyChanged(PropertyInfo.Dummy);
        }
    }
}

طريقة واحدة للحصول على ملاحظات إذا تم كسر الروابط الخاصة بك، هي إنشاء DataTemplate وإعلان نوع البيانات الخاص به لتكون نوع ViewModel الذي يرتبط به على سبيل المثال إذا كان لديك شخص وشخص وشخصية يمكنك القيام بما يلي:

  1. أعلن DataTemplate باستخدام DataType = PersonViewModel ومفتاح (مثل شخصي)

  2. قطع كل شخص XAML والصقه في قالب البيانات (والتي يمكن أن تكون مثالية فقط في الجزء العلوي من الشخص.

3A. قم بإنشاء ContentControl وقم بتعيين ContentTemplate = شخصي وربط محتواها على شخص AppIViewModel.

3b. خيار آخر هو عدم إعطاء مفتاح DataTemplate وليس تعيين Contenttemplate من ContentControl. في هذه الحالة، سيقوم WPF بتحديد DataTemplate لاستخدامه، لأنه يعرف نوع الكائن الذي تربطه إليه. ستقوم بالبحث في الشجرة والعثور على DataTemplate الخاص بك وبالتالي يطابق نوع الربط، وسوف تطبقه تلقائيا ك Contenttemplate.

تنتهي بك الأمر مع نفس الرأي الأساسي كما كان من قبل، ولكن نظرا لأنك قد حددت DataTemplate إلى Datatemplate إلى DataType الأساسي، فإن الأدوات مثل Resharper يمكن أن تعطيك ملاحظات (عبر معرفات الألوان - معرفات ألوان الخيارات RESHREPER) أم لا.

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

ميزة أخرى لهذه المعلومات الإضافية التي تقدمها، هي أنه يمكن استخدامها أيضا في إعادة تسمية إعادة تسمية. بقدر ما أتذكر RIDHRAPER قادرة على إعادة تسمية الروابط تلقائيا على DataTemplates المكتوبة عند تغيير اسم خاصية ViewModel الأساسي والعكس بالعكس.

1. إذا تمت إزالة العقار أو إعادة تسميته، لا أحصل على تحذير مترجم.

2. إذا أعيد تسمية الممتلكات بأداة إعادة الإنفاق، فمن المحتمل عدم تحديث ملزمة البيانات.

3. لا تحصل على خطأ حتى وقت التشغيل إذا كان نوع العقار خاطئ، على سبيل المثال ملزمة عدد صحيح إلى اختيار تاريخ.

نعم، إيان، التي هي بالضبط المشاكل مع ملزمة البيانات التي يحركها اسم الاسم. لقد طلبت نمط تصميم. لقد قمت بتصميم نمط نموذج العرض الآمن (TVM) من النوع (TVM) هو Concretion of the View Model Part من نمط Model-ViewModel (MVVM). يعتمد على ملزمة آمنة من النوع، مشابهة للإجابة الخاصة بك. لقد نشرت للتو حلا ل WPF:

http://www.codeProject.com/articles/450688/ENHAND-MVVM-DESIGN-W-TYPE-SAFE-View-Models-TVM.

X: BIND (تسمى أيضا "ربط البيانات المترجمة") ل Xaml (التطبيق العالمي) في نظام التشغيل Windows 10 و Windows Phone 10 قد يحل هذه المشكلة، وانظر https://channel9.msdn.com/events/build 15/3-635.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top