문제

내가하고자 할 때에 바인딩하 제어하는 시설 내 개체가 제공하는 속성의 이름으로 문자열입니다.이것은 아주 좋지 않기 때문에:

  1. 는 경우 호텔 제거하거나 이름이 바뀌지 않아요 컴파일러 경고입니다.
  2. 는 경우 이름을 바꾸 호텔 와 refactoring 도구 가능성 데이터 바인딩이 될 수 없 업데이트됩니다.
  3. 나지 않아 오류를 얻을 때까지 runtime 경우에는 유형의 시설 잘못된 것입,예를 들어,정수를 바인딩 날짜 선택.

거기 디자인 패턴을 얻는 둥근이지만,아직도 사용 편의성의 데이터 바인딩?

(이 문제에 WinForm,Asp.net 고 WPF 그리고 대부분의 많은 다른 시스템)

나는 이제 발견"에 대한 해결 방법 이름()연산자는 C#:형식이 안전한 데이터 바인딩"그는 또한 좋은 출발점에 대한 솔루션입니다.

고자 하는 경우 사용하는 포스트 프로세서 컴파일한 후에 코드 notifypropertyweaver 잘 가치가 있습니다.


누구나 알고있는 좋은 해결책에 대한 WPF 경우 바인딩 할 수 있는 XML 오히려 그 C#?

도움이 되었습니까?

해결책

올리버 덕분에 나를 시작해 주셔서 감사합니다. 이제 리팩토링을 지원하고 안전한 유형 인 솔루션이 있습니다. 또한 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);

Person Class는 유형의 안전한 방법으로 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 프로그래머는 우리를 2 등석 시민이라고 부를 수 있습니다)

다른 팁

이름 운영에서 구현되었 C#6.0 니다.NET4.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),그것은 확실히 도움이 되(at least for me),하지만 당신의 코드가 여전히지 않는 형식 안정적입니다.을 확장할 수 있는 메서드 이름을 가진 두번째 형식 인수를 정의하는 반환 값이 기능하는 것을 제한 유형의 시설을 제공합니다.

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

지금까지 나는 아무것도 발견하지 않았를 해결하는 데이터 바인딩 typesafety 문제입니다.

Best Regards

프레임 워크 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 ...
}

Framework 4.0에서 작업하는 경우 KB2468871 설치하면 설치할 수 있습니다 Microsoft BCL 호환성 팩 ~을 통해 너겟, 이 속성도 제공합니다.

이 블로그 기사는이 접근법의 성능에 대한 몇 가지 좋은 질문을 제기합니다.. 정적 초기화의 일부로 표현식을 문자열로 변환하여 이러한 단점을 향상시킬 수 있습니다.

실제 역학은 약간보기 흉한 일이지만 여전히 유형-안전하고 RAW 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을 작성하고 데이터 타입을 PersonView와 PersonViewModel이 있으면 다음을 수행 할 수있는 뷰 모델의 유형으로 데이터 타입을 선언하는 것입니다.

  1. DataType = PersonViewModel 및 키 (예 : PersonTemplate)로 DataTemplate를 선언합니다.

  2. 모든 PersonView XAML을 자르고 데이터 템플릿에 붙여 넣기 (이상적으로는 개인 뷰의 맨 위에있을 수 있습니다.

3A. ContentControl을 작성하고 ContentTemPlate = PersonTemPlate을 설정하고 해당 내용을 PersonViewModel에 바인딩합니다.

3B. 또 다른 옵션은 DataTemplate의 키를 제공하지 않고 ContentScontrol의 ContentTemplate을 설정하지 않는 것입니다. 이 경우 WPF는 어떤 유형의 객체를 바인딩하는지 알고 있으므로 어떤 데이터 템플을 사용할 것인지 파악합니다. 트리를 검색하고 DataTemplate을 찾아 바인딩 유형과 일치하므로 자동으로 ContentTemplate로 적용됩니다.

당신은 이전과 동일한보기로 끝나지만 데이터 emplate를 기본 데이터 유형에 매핑 했으므로 Resharper와 같은 도구는 바인딩이 고장날 수 있도록 피드백 (색상 식별자를 통해 피드백)을 제공 할 수 있습니다. 아니면 아니에요.

여전히 컴파일러 경고를 얻지 못하지만 바인딩이 깨진 바인딩을 시각적으로 확인할 수 있습니다. 이는 시야와 뷰 모델 사이를 오가는 것보다 낫습니다.

이 추가 정보의 또 다른 장점은 리팩토링을 이름 바꾸는 데 사용될 수 있다는 것입니다. 내가 기억하는 한, Resharper는 기본 뷰 모델의 속성 이름이 변경 될 때 타이핑 된 데이터 emplates에서 바인딩을 자동으로 바꿀 수 있습니다.

1. 속성이 제거되거나 이름이 바뀌면 컴파일러 경고가 없습니다.

2. 리팩토링 도구로 속성의 이름을 바꾸는 경우 데이터 바인딩이 업데이트되지 않을 수 있습니다.

3. 속성 유형이 잘못된 경우 런타임까지 오류가 발생하지 않습니다. 예를 들어 정수를 날짜 선택기에 바인딩합니다.

그렇습니다. Ian, 그것은 이름-스트링 중심 데이터 바인딩의 문제입니다. 당신은 디자인 패턴을 요청했습니다. MVM (Model-View-ViewModel) 패턴의 뷰 모델 부분의 구체화 인 TVM (Type-Safe View Model) 패턴을 설계했습니다. 그것은 자신의 대답과 유사한 유형-안전 바인딩을 기반으로합니다. 방금 WPF에 대한 솔루션을 게시했습니다.

http://www.codeproject.com/articles/450688/enhanced-mvm-design-w-type-safe-view-models-tvm

X : Windows 10의 XAML (Universal App)의 Bind ( "Compiled Data Bindings"라고도 함)와 Windows Phone 10 은이 문제를 해결할 수 있습니다. https://channel9.msdn.com/events/build/2015/3-635

나는 온라인 문서를 찾을 수 없지만 한동안 사용하지 않을 것이기 때문에 많은 노력을 기울이지 않았습니다. 그러나이 대답은 다른 사람들에게 유용한 포인터 여야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top