6

モデルクラスのスペースを消費し、反復するRaisePropertyChanged-Propertiesを削除したいと思います。モデルクラスが欲しい...

public class ProductWorkItem : NotificationObject
{
    private string name;
    public string Name
    {
        get { return name; }
        set { 
            if (value == name) return; 
            name = value; RaisePropertyChanged(() => Name); 
        }
    }
    private string description;
    public string Description
    {
        get { return description; }
        set { 
            if (value == description) return; 
            description = value; RaisePropertyChanged(() => Description); 
        }
    }
    private string brand;
    public string Brand
    {
        get { return brand; }
        set { 
            if (value == brand) return; 
            brand = value; RaisePropertyChanged(() => Brand); 
        }
    }
}

...これと同じくらい単純に見えるように:(ただし、プロパティが変更されたときにビューに通知します)

public class ProductWorkItem
{
    public string Name{ get; set; }
    public string Description{ get; set; }
    public string Brand{ get; set; }
}

これは、ある種のプロキシクラスで実現できますか?

モデルクラスごとにプロキシを作成することは避けたいと思います。

4

6 に答える 6

6

「バニラ」C#でこれに対する単純で保守可能なアプローチはありませんが、アスペクトを使用してこれを実現できます。私はこれにPostSharpを使用しました。これは、有料のサードパーティ製品であるという欠点がありますが、無料バージョンもあり、これも実行できます。PostSharpは、ターゲットの指定、継承などの属性の利点を活用し、それらをアスペクトに拡張します。

次に、デリゲートを呼び出すためのメソッドをLocationInterceptionAspectオーバーライドするを定義できます。次に、アスペクト属性で装飾された自動生成されたプロパティを使用できます。OnSetValueRaisePropertyChanged

有料版のPostSharpでは、これをクラスレベルで実行できるため、必要な属性は1つだけです(基本クラスを装飾して属性を継承可能として定義する場合は、属性は必要ありません)。これは、PostSharpサイトでのユースケースとして説明されていますInstanceLevelAspect

于 2012-12-01T23:06:58.503 に答える
3

NotifyPropertyWeaver拡張機能を使用して、それ以来定期的に使用しています。これはVisualStudioの拡張機能であり、コードがコンパイルされる前に、常に同じINPCのものを実装します。あなたはそのことに気づいていません。

拡張機能をインストールする必要があります。モデルは次のようになります。

public class ProductWorkItem : INotifyPropertyChanged
{
    public string Name{ get; set; }
    public string Description{ get; set; }
    public string Brand{ get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
}

拡張機能は、残りすべてを追加します。このアプローチで私が気に入っているのは、クラスがまだ「公式に」INPCインターフェイスを実装しており、非WPFコンテキストでも使用できることです(INPCは単なるWPFのものではないため)が、まだ持っていません。クラスにすべてのものを散らかすために。プロパティに依存する読み取り専用プロパティの通知を生成します。

もちろん、それは書き込みを自動化するだけで、基本的な概念については何も変更しないため、少し偽物です。しかし多分それは妥協です...

詳細はこちら:リンク

于 2012-12-02T10:56:01.607 に答える
2

WPFの各プロパティセッターでRaisePropertyChangedを記述する繰り返しコードを回避できます。

Postsharpの無料バージョンを使用してください。

次のコードを使用することで、仮想プロパティのみを表示にバインドできます。

namespace Test
{
[Serializable]
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)]
public sealed class RaisePropertyChangedAttribute : MethodInterceptionAspect
{
    private string propertyName;

    /// <summary>
    /// Compiles the time validate.
    /// </summary>
    /// <param name="method">The method.</param>
    public override bool CompileTimeValidate(MethodBase method)
    {
        return IsPropertySetter(method) && !method.IsAbstract && IsVirtualProperty(method);
    }

    /// <summary>
    /// Method invoked at build time to initialize the instance fields of the current aspect. This method is invoked
    /// before any other build-time method.
    /// </summary>
    /// <param name="method">Method to which the current aspect is applied</param>
    /// <param name="aspectInfo">Reserved for future usage.</param>
    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
    {
        base.CompileTimeInitialize(method, aspectInfo);
        propertyName = GetPropertyName(method);
    }

    /// <summary>
    /// Determines whether [is virtual property] [the specified method].
    /// </summary>
    /// <param name="method">The method.</param>
    /// <returns>
    ///   <c>true</c> if [is virtual property] [the specified method]; otherwise, <c>false</c>.
    /// </returns>
    private static bool IsVirtualProperty(MethodBase method)
    {
        if (method.IsVirtual)
        {
            return true;
        }

        var getMethodName = method.Name.Replace("set_", "get_");
        var getMethod = method.DeclaringType.GetMethod(getMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        return getMethod != null && getMethod.IsVirtual;
    }

    private static string GetPropertyName(MethodBase method)
    {
        return method.Name.Replace("set_", string.Empty);
    }

    /// <summary>
    /// Determines whether [is property setter] [the specified method].
    /// </summary>
    /// <param name="method">The method.</param>
    /// <returns>
    /// <c>true</c> if [is property setter] [the specified method]; otherwise, <c>false</c>.
    /// </returns>
    private static bool IsPropertySetter(MethodBase method)
    {
        return method.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// Method invoked <i>instead</i> of the method to which the aspect has been applied.
    /// </summary>
    /// <param name="args">Advice arguments.</param>
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        var arg = args as MethodInterceptionArgsImpl;

        if ((arg != null) && (arg.TypedBinding == null))
        {
            return;
        }

        // Note ViewModelBase is base class for ViewModel
        var target = args.Instance as ViewModelBase;

        args.Proceed();

        if (target != null)
        {
            target.OnPropertyChanged(propertyName);                    
        }
    }
}
}
于 2013-07-27T20:19:54.100 に答える
0

このクラスは名前空間で見つかりました...これにより、バインディングターゲットで、バインディングソースで行われSystem.Dynamicた実際のDataBinding呼び出しをインターセプトできます。DependencyObjectProperty

http://i.msdn.microsoft.com/en-us/library/system.windows.data.binding.DataBindingMostBasic(v=vs.110).png?appId=Dev11IDEF1&l=EN-US&k=k(System.Windows .Data.Binding)%3bk(VS.XamlEditor)%3bk(TargetFrameworkMoniker-.NETFramework

したがって、今できることは、とメソッドの両方から派生し、それらをオーバーライドするクラス(と呼びましょうDynamicNpcProxy)を実装することです。INotifyPropertyChangedDynamicObjectTryGetMemberTrySetMember

public class DynamicNpcProxy : DynamicObject, INotifyPropertyChanged
{
    public DynamicNpcProxy(object proxiedObject)
    {
        ProxiedObject = proxiedObject;
    }

    //...

    public object ProxiedObject { get; set; }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        SetMember(binder.Name, value);
        return true;
    }

    protected virtual void SetMember(string propertyName, object value)
    {
        GetPropertyInfo(propertyName).SetValue(ProxiedObject, value, null);
        if (PropertyChanged != null) 
            PropertyChanged(ProxiedObject, new PropertyChangedEventArgs(propertyName));
    }

    protected PropertyInfo GetPropertyInfo(string propertyName)
    {
        return ProxiedObject.GetType().GetProperty(propertyName);
    }

    // override bool TryGetMember(...)
}

それを機能させるには、プロキシを現在のバインディングソースにラップし、それらを置き換えてDynamicObject、残りを実行します。

ViewModel.csの場合:

IList<ProductWorkItem> items;
//... assign items
var proxies = items.Select(p => new DynamicNpcProxy(p)).ToList();
ICollectionView Products = CollectionViewSource.GetDefaultView(proxies);

View.xamlの場合:

<TextBox Text="{Binding Products.CurrentItem.Name}" /> 
<TextBox Text="{Binding Products.CurrentItem.Description}" /> 

最終的には次のようになります。

さらに詳しい情報を提供しているこの記事もチェックしてcode projectください...

于 2012-12-02T02:42:51.397 に答える
0

反対側から来ると(そ​​してあなたが派手な拡張機能を持っていない場合)、ここで私の答えに概説されている拡張方法で変更されたプロパティを「自動指定」できます: 「FieldSpecified」プロパティを設定していないWCFサービスプロキシ

具体的には、「非反射メソッド」を使用して、クラスごとに特定の「OnPropertyChanged」処理を構成し、指定されたフィールドを設定する以外のことを行うことができます。

public static class PropertySpecifiedExtensions2
{
    /// <summary>
    /// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically call each class's <see cref="IAutoNotifyPropertyChanged.Autonotify"/> method on the property name.
    /// </summary>
    /// <param name="entity">the entity to bind the autospecify event to</param>
    public static void Autonotify(this IAutoNotifyPropertyChanged entity)
    {
        entity.PropertyChanged += (me, e) => ((IAutoNotifyPropertyChanged)me).WhenPropertyChanges(e.PropertyName);
    }

    /// <summary>
    /// Create a new entity and <see cref="Autonotify"/> it's properties when changed
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static T Create<T>() where T : IAutoNotifyPropertyChanged, new()
    {
        var ret = new T();
        ret.Autonotify();
        return ret;
    }
}

/// <summary>
/// Used by <see cref="PropertySpecifiedExtensions.Autonotify"/> to standardize implementation behavior
/// </summary>
public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged
{
    void WhenPropertyChanges(string propertyName);
}

そして、各クラス自体が動作を定義します。

public partial class MyRandomClass: IAutoNotifyPropertyChanged
{

    /// <summary>
    /// Create a new empty instance and <see cref="PropertySpecifiedExtensions.Autospecify"/> its properties when changed
    /// </summary>
    /// <returns></returns>
    public static MyRandomClass Create()
    {
        return PropertySpecifiedExtensions2.Create<MyRandomClass>();
    }

    public void WhenPropertyChanges(string propertyName)
    {
        switch (propertyName)
        {
            case "field1": this.field1Specified = true; return;
            // etc
        }

        // etc
        if(propertyName.StartsWith(...)) { /* do other stuff */ }
    }
}

これの欠点は、もちろん、プロパティ名のマジックストリングがリファクタリングを困難にすることです。これは、Expression解析で回避できますか?

于 2014-01-15T18:40:12.057 に答える
0

これはすでに古いものであり、誰も言及していません:

https://marketplace.visualstudio.com/items?itemName=AlexeyLavnikov.KindOfMagic

クラスの1つの属性を使用して、ViewModelのすべてのプロパティの自動通知をオンにできます。

于 2017-01-31T14:00:24.140 に答える