2011-12-20 10 views
12

मैं अपने आवेदन के लिए पर्यवेक्षक पैटर्न को कार्यान्वित कर रहा हूं - वर्तमान में आरएक्स फ्रेमवर्क के साथ खेल रहा हूं।बेहतर संपत्ति चेंज और प्रॉपर्टी चेंजिंग इवेंट हैंडलिंग

Observable.FromEventPattern<PropertyChangedEventArgs>(Instance.Address, "PropertyChanged") 
    .Where(e => e.EventArgs.PropertyName == "City") 
    .ObserveOn(Scheduler.ThreadPool) 
    .Subscribe(search => OnNewSearch(search.EventArgs)); 

(मैं "PropertyChanging" के लिए एक समान एक है)

EventArgs मुझे ज्यादा नहीं देते:

मैं वर्तमान में एक उदाहरण है कि इस तरह दिखता है। मैं जो चाहता हूं वह EventArgs का एक विस्तार है जो मुझे पिछले और नए मानों को देखने की क्षमता प्रदान करेगा, साथ ही 'बदलते' श्रोता में ईवेंट को चिह्नित करने की क्षमता, जैसे परिवर्तन वास्तव में जारी नहीं रहेगा। यह कैसे किया जा सकता है? धन्यवाद।

+0

देखें [यह] (https: //github.com/dotnet/corefx/issues/19627) – Shimmy

उत्तर

22

मुझे लगता है कि यह INOTifyPropertyChanging और INotifyPropertyChanged इंटरफेस को कार्यान्वित करने के तरीके के बारे में आता है।

संपत्तिChangingEventArgs और PropertyChangedEventArgs वर्ग दुर्भाग्यवश संपत्ति के पहले और बाद के मूल्य या परिवर्तन को रद्द करने की क्षमता प्रदान नहीं करते हैं, लेकिन आप अपनी खुद की घटना तर्क कक्षाओं को प्राप्त कर सकते हैं जो कार्यक्षमता प्रदान करते हैं।

सबसे पहले, निम्नलिखित घटनाओं को परिभाषित करें कक्षाओं का तर्क है। ध्यान दें कि ये PropertyChangingEventArgs क्लास और PropertyChangedEventArgs क्लास से प्राप्त होते हैं। यह हमें इन वस्तुओं को PropertyChangingEventHandler और PropertyChangedEventHandler प्रतिनिधियों के तर्क के रूप में पारित करने की अनुमति देता है।

class PropertyChangingCancelEventArgs : PropertyChangingEventArgs 
{ 
    public bool Cancel { get; set; } 

    public PropertyChangingCancelEventArgs(string propertyName) 
     : base(propertyName) 
    { 
    } 
} 

class PropertyChangingCancelEventArgs<T> : PropertyChangingCancelEventArgs 
{ 
    public T OriginalValue { get; private set; } 

    public T NewValue { get; private set; } 

    public PropertyChangingCancelEventArgs(string propertyName, T originalValue, T newValue) 
     : base(propertyName) 
    { 
     this.OriginalValue = originalValue; 
     this.NewValue = newValue; 
    } 
} 

class PropertyChangedEventArgs<T> : PropertyChangedEventArgs 
{ 
    public T PreviousValue { get; private set; } 

    public T CurrentValue { get; private set; } 

    public PropertyChangedEventArgs(string propertyName, T previousValue, T currentValue) 
     : base(propertyName) 
    { 
     this.PreviousValue = previousValue; 
     this.CurrentValue = currentValue; 
    } 
} 

इसके बाद, आप INotifyPropertyChanging और INotifyPropertyChanged इंटरफेस के अपने कार्यान्वयन में इन कक्षाओं में उपयोग करने के लिए की आवश्यकता होगी। , PropertyChanging के लिए अपने ईवेंट हैंडलर्स

class Example : INotifyPropertyChanging, INotifyPropertyChanged 
{ 
    public event PropertyChangingEventHandler PropertyChanging; 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected bool OnPropertyChanging<T>(string propertyName, T originalValue, T newValue) 
    { 
     var handler = this.PropertyChanging; 
     if (handler != null) 
     { 
      var args = new PropertyChangingCancelEventArgs<T>(propertyName, originalValue, newValue); 
      handler(this, args); 
      return !args.Cancel; 
     } 
     return true; 
    } 

    protected void OnPropertyChanged<T>(string propertyName, T previousValue, T currentValue) 
    { 
     var handler = this.PropertyChanged; 
     if (handler != null) 
      handler(this, new PropertyChangedEventArgs<T>(propertyName, previousValue, currentValue)); 
    } 

    int _ExampleValue; 

    public int ExampleValue 
    { 
     get { return _ExampleValue; } 
     set 
     { 
      if (_ExampleValue != value) 
      { 
       if (this.OnPropertyChanging("ExampleValue", _ExampleValue, value)) 
       { 
        var previousValue = _ExampleValue; 
        _ExampleValue = value; 
        this.OnPropertyChanged("ExampleValue", previousValue, value); 
       } 
      } 
     } 
    } 
} 

नोट और PropertyChanged घटनाओं अभी भी नहीं बल्कि एक अधिक विशिष्ट संस्करण की तुलना में, मूल PropertyChangingEventArgs वर्ग और पैरामीटर के रूप में PropertyChangedEventArgs वर्ग लेने के लिए की आवश्यकता होगी: एक कार्यान्वयन का एक उदाहरण निम्नलिखित है। हालांकि, आप नए गुणों तक पहुंचने के लिए ईवेंट को अपने विशिष्ट प्रकारों पर ऑब्जेक्ट्स का तर्क देने में सक्षम होंगे।

class Program 
{ 
    static void Main(string[] args) 
    { 
     var exampleObject = new Example(); 

     exampleObject.PropertyChanging += new PropertyChangingEventHandler(exampleObject_PropertyChanging); 
     exampleObject.PropertyChanged += new PropertyChangedEventHandler(exampleObject_PropertyChanged); 

     exampleObject.ExampleValue = 123; 
     exampleObject.ExampleValue = 100; 
    } 

    static void exampleObject_PropertyChanging(object sender, PropertyChangingEventArgs e) 
    { 
     if (e.PropertyName == "ExampleValue") 
     { 
      int originalValue = ((PropertyChangingCancelEventArgs<int>)e).OriginalValue; 
      int newValue = ((PropertyChangingCancelEventArgs<int>)e).NewValue; 

      // do not allow the property to be changed if the new value is less than the original value 
      if(newValue < originalValue) 
       ((PropertyChangingCancelEventArgs)e).Cancel = true; 
     } 

    } 

    static void exampleObject_PropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (e.PropertyName == "ExampleValue") 
     { 
      int previousValue = ((PropertyChangedEventArgs<int>)e).PreviousValue; 
      int currentValue = ((PropertyChangedEventArgs<int>)e).CurrentValue; 
     } 
    } 
} 
+0

यह बेहद सहायक है, बहुत बहुत धन्यवाद! – user981225

+0

सेटर विधियों में से सभी कोड से बचने के लिए, डायनामिक प्रॉक्सी का उपयोग करके इसे कार्यान्वित करना संभव होगा? – user981225

+0

@ user981225 - मैं डायनामिकप्रॉक्सी से परिचित नहीं हूं (जैसे कि [कैसल] (http://www.castleproject.org/dynamicproxy/index.html)?), लेकिन मुझे लगता है कि आप एओपी या पॉलिसी इंजेक्शन दृष्टिकोण लेना चाहते हैं । मैं आपकी सोच की रेखा से सहमत हूं और मैं निश्चित रूप से उस प्रॉपर्टी सेटर कोड को समेकित करने के लिए कुछ तंत्र (एओपी, नीति इंजेक्शन, या अन्यथा) की तलाश करता हूं ताकि आपके पास बहुत सारे अनावश्यक कोड न हों। हालांकि, मेरे पास ऑफहैंड की सिफारिश करने के लिए कोई विशेष तंत्र नहीं है। –

2

स्वीकार किए जाते हैं प्रतिक्रिया वास्तव में बुरा है, आप ऐसा कर सकते बस बफर के साथ():

नीचे इन घटनाओं के लिए घटना संचालकों का एक उदाहरण है।

Observable.FromEventPattern<PropertyChangedEventArgs>(Instance.Address, "PropertyChanged") 
    .Where(e => e.EventArgs.PropertyName == "City") 
    .Buffer(2,1) //Take 2 events at a time, every 1 event 
    .ObserveOn(Scheduler.ThreadPool) 
    .Subscribe(search => ...); //search[0] is old value, search[1] is new value 
+2

एमएमएम ... कुछ समस्याएं: 1) अंतर्निहित संपत्तिChanged/PropertyChanging घटनाएं संपत्ति के नए या मूल मूल्य प्रदान नहीं करती हैं, जो काम के लिए इस "बफर" दृष्टिकोण के लिए आवश्यक होगी। 2) इस दृष्टिकोण के लिए घटना को एक से अधिक बार आग लगाना आवश्यक है ताकि आप संपत्ति मूल्य की तुलना कर सकें क्योंकि यह दो घटनाओं के बीच बदलता है। ओपी केवल एक ही घटना के लिए संपत्ति के पिछले और वर्तमान मूल्यों का निरीक्षण करने में सक्षम होना चाहता है। 3) ओपी संपत्ति मूल्य में परिवर्तन को रद्द करने में सक्षम होना चाहता है। अंतर्निहित PropertyChanging घटना रद्द करने की व्यवस्था प्रदान नहीं करती है। –

0

किसी को भी करता है दोनों RX का सबसे अच्छा करना चाहते हैं और यहाँ रद्द करने में सक्षम होने के लिए है कि इन विचारों के दोनों के एक संकर है

ViewModel आधार वर्ग सामान

public abstract class INPCBase : INotifyPropertyChanged, INotifyPropertyChanging 
{ 
    public event PropertyChangingEventHandler PropertyChanging; 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected bool OnPropertyChanging<T>(string propertyName, T originalValue, T newValue) 
    { 
     var handler = this.PropertyChanging; 
     if (handler != null) 
     { 
      var args = new PropertyChangingCancelEventArgs<T>(propertyName, originalValue, newValue); 
      handler(this, args); 
      return !args.Cancel; 
     } 
     return true; 
    } 

    protected void OnPropertyChanged<T>(string propertyName, T previousValue, T currentValue) 
    { 
     var handler = this.PropertyChanged; 
     if (handler != null) 
      handler(this, new PropertyChangedEventArgs<T>(propertyName, previousValue, currentValue)); 
    } 
} 


public class PropertyChangingCancelEventArgs : PropertyChangingEventArgs 
{ 
    public bool Cancel { get; set; } 

    public PropertyChangingCancelEventArgs(string propertyName) 
     : base(propertyName) 
    { 
    } 
} 

public class PropertyChangingCancelEventArgs<T> : PropertyChangingCancelEventArgs 
{ 
    public T OriginalValue { get; private set; } 

    public T NewValue { get; private set; } 

    public PropertyChangingCancelEventArgs(string propertyName, T originalValue, T newValue) 
     : base(propertyName) 
    { 
     this.OriginalValue = originalValue; 
     this.NewValue = newValue; 
    } 
} 

public class PropertyChangedEventArgs<T> : PropertyChangedEventArgs 
{ 
    public T PreviousValue { get; private set; } 

    public T CurrentValue { get; private set; } 

    public PropertyChangedEventArgs(string propertyName, T previousValue, T currentValue) 
     : base(propertyName) 
    { 
     this.PreviousValue = previousValue; 
     this.CurrentValue = currentValue; 
    } 
} 
तो मेरे पास है

इन जोड़े एक्सटेंशन।

एक संपत्ति

public static class ExpressionExtensions 
{ 

    public static string GetPropertyName<TProperty>(this Expression<Func<TProperty>> expression) 
    { 
     var memberExpression = expression.Body as MemberExpression; 
     if (memberExpression == null) 
     { 
      var unaryExpression = expression.Body as UnaryExpression; 
      if (unaryExpression != null) 
      { 
       if (unaryExpression.NodeType == ExpressionType.ArrayLength) 
        return "Length"; 
       memberExpression = unaryExpression.Operand as MemberExpression; 

       if (memberExpression == null) 
       { 
        var methodCallExpression = unaryExpression.Operand as MethodCallExpression; 
        if (methodCallExpression == null) 
         throw new NotImplementedException(); 

        var arg = (ConstantExpression)methodCallExpression.Arguments[2]; 
        return ((MethodInfo)arg.Value).Name; 
       } 
      } 
      else 
       throw new NotImplementedException(); 

     } 

     var propertyName = memberExpression.Member.Name; 
     return propertyName; 

    } 

    public static string GetPropertyName<T, TProperty>(this Expression<Func<T, TProperty>> expression) 
    { 
     var memberExpression = expression.Body as MemberExpression; 

     if (memberExpression == null) 
     { 
      var unaryExpression = expression.Body as UnaryExpression; 

      if (unaryExpression != null) 
      { 
       if (unaryExpression.NodeType == ExpressionType.ArrayLength) 
        return "Length"; 
       memberExpression = unaryExpression.Operand as MemberExpression; 

       if (memberExpression == null) 
       { 
        var methodCallExpression = unaryExpression.Operand as MethodCallExpression; 
        if (methodCallExpression == null) 
         throw new NotImplementedException(); 

        var arg = (ConstantExpression)methodCallExpression.Arguments[2]; 
        return ((MethodInfo)arg.Value).Name; 
       } 
      } 
      else 
       throw new NotImplementedException(); 
     } 
     var propertyName = memberExpression.Member.Name; 
     return propertyName; 

    } 

    public static String PropertyToString<R>(this Expression<Func<R>> action) 
    { 
     MemberExpression ex = (MemberExpression)action.Body; 
     return ex.Member.Name; 
    } 

    public static void CheckIsNotNull<R>(this Expression<Func<R>> action, string message) 
    { 
     MemberExpression ex = (MemberExpression)action.Body; 
     string memberName = ex.Member.Name; 
     if (action.Compile()() == null) 
     { 
      throw new ArgumentNullException(memberName, message); 
     } 
    } 

} 

public static class ObservableExtensions 
{ 

    public static IObservable<ItemPropertyChangingEvent<TItem, TProperty>> ObserveSpecificPropertyChanging<TItem, TProperty>(
     this TItem target, Expression<Func<TItem, TProperty>> propertyName) where TItem : INotifyPropertyChanging 
    { 
     var property = propertyName.GetPropertyName(); 

     return ObserveSpecificPropertyChanging(target, property) 
       .Select(i => new ItemPropertyChangingEvent<TItem, TProperty>() 
       { 
        OriginalEventArgs = (PropertyChangingCancelEventArgs<TProperty>)i.OriginalEventArgs, 
        Property = i.Property, 
        Sender = i.Sender 
       }); 
    } 

    public static IObservable<ItemPropertyChangingEvent<TItem>> ObserveSpecificPropertyChanging<TItem>(
     this TItem target, string propertyName = null) where TItem : INotifyPropertyChanging 
    { 

     return Observable.Create<ItemPropertyChangingEvent<TItem>>(obs => 
     { 
      Dictionary<string, PropertyInfo> properties = new Dictionary<string, PropertyInfo>(); 
      PropertyChangingEventHandler handler = null; 

      handler = (s, a) => 
      { 
       if (propertyName == null || propertyName == a.PropertyName) 
       { 
        PropertyInfo prop; 
        if (!properties.TryGetValue(a.PropertyName, out prop)) 
        { 
         prop = target.GetType().GetProperty(a.PropertyName); 
         properties.Add(a.PropertyName, prop); 
        } 
        var change = new ItemPropertyChangingEvent<TItem>() 
        { 
         Sender = target, 
         Property = prop, 
         OriginalEventArgs = a, 
        }; 

        obs.OnNext(change); 
       } 
      }; 

      target.PropertyChanging += handler; 

      return() => 
      { 
       target.PropertyChanging -= handler; 
      }; 
     }); 
    } 



    public class ItemPropertyChangingEvent<TSender> 
    { 
     public TSender Sender { get; set; } 
     public PropertyInfo Property { get; set; } 
     public PropertyChangingEventArgs OriginalEventArgs { get; set; } 

     public override string ToString() 
     { 
      return string.Format("Sender: {0}, Property: {1}", Sender, Property); 
     } 
    } 


    public class ItemPropertyChangingEvent<TSender, TProperty> 
    { 
     public TSender Sender { get; set; } 
     public PropertyInfo Property { get; set; } 
     public PropertyChangingCancelEventArgs<TProperty> OriginalEventArgs { get; set; } 
    } 

} 

तो उदाहरण उपयोग इस

public class MainWindowViewModel : INPCBase 
{ 
    private string field1; 
    private string field2; 


    public MainWindowViewModel() 
    { 
     field1 = "Hello"; 
     field2 = "World"; 

     this.ObserveSpecificPropertyChanging(x => x.Field2) 
      .Subscribe(x => 
      { 
       if (x.OriginalEventArgs.NewValue == "DOG") 
       { 
        x.OriginalEventArgs.Cancel = true; 
       } 
      }); 

    } 

    public string Field1 
    { 
     get 
     { 
      return field1; 
     } 
     set 
     { 
      if (field1 != value) 
      { 
       if (this.OnPropertyChanging("Field1", field1, value)) 
       { 
        var previousValue = field1; 
        field1 = value; 
        this.OnPropertyChanged("Field1", previousValue, value); 
       } 
      } 
     } 
    } 


    public string Field2 
    { 
     get 
     { 
      return field2; 
     } 
     set 
     { 
      if (field2 != value) 
      { 
       if (this.OnPropertyChanging("Field2", field2, value)) 
       { 
        var previousValue = field2; 
        field2 = value; 
        this.OnPropertyChanged("Field2", previousValue, value); 
       } 
      } 
     } 
    } 
} 

तरह होगा अभिव्यक्ति पेड़ से नाम और फिर आरएक्स हिस्सा पाने के लिए एक इलाज काम करता है

संबंधित मुद्दे