2011-06-11 10 views
9

कुछ गुणों का उपयोग करके मेरी viewmodel:अपडेट किया जा रहा निर्भर गुणों पर MVVM

public ObservableCollection<Task> Tasks { get; set; } 

public int Count 
{ 
    get { return Tasks.Count; } 
} 

public int Completed 
{ 
    get { return Tasks.Count(t => t.IsComplete); } 
} 

इन गुणों जब Tasks परिवर्तन अद्यतन करने के लिए सबसे अच्छा तरीका क्या है?

मेरे वर्तमान विधि:

public TaskViewModel() 
{ 
    Tasks = new ObservableCollection<Task>(repository.LoadTasks()); 
    Tasks.CollectionChanged += (s, e) => 
     { 
      OnPropertyChanged("Count"); 
      OnPropertyChanged("Completed"); 
     }; 
} 

वहाँ यह करने के लिए एक और अधिक सुरुचिपूर्ण रास्ता नहीं है?

उत्तर

9

Count के संबंध में, आपको यह बिल्कुल करने की ज़रूरत नहीं है। बस Tasks.Count से बांधें और आपकी बाइंडिंग ObservableCollection द्वारा परिवर्तन की अधिसूचना प्राप्त की जाएगी।

Completed एक अलग कहानी है, क्योंकि यह ObservableCollection के बाहर है। फिर भी, अमूर्त/इंटरफ़ेस के स्तर से, आप को Tasks संग्रह की संपत्ति होने के लिए वास्तव में चाहते हैं।

इस के लिए, मुझे लगता है कि एक बेहतर दृष्टिकोण अपने Tasks संपत्ति के लिए "उप" व्यू-मॉडल बनाने के लिए होगा:

public class TasksViewModel : ObservableCollection<Task> 
{ 
    public int Completed 
    { 
     get { return this.Count(t => t.IsComplete); } 
    } 

    protected override void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
     base.OnPropertyChanged(e); 
     if(e.PropertyName == "Count") NotifyCompletedChanged(); 
    } 

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     base.OnCollectionChanged(e); 
     NotifyCompletedChanged(); 
    } 

    void NotifyCompletedChanged() 
    { 
     OnPropertyChanged(_completedChangedArgs); 
    } 
    readonly PropertyChangedEventArgs _completedChangedArgs = new PropertyChangedEventArgs("Completed"); 
} 

यह आपको ObservableCollection की सभी सुविधाओं देता है, और प्रभावी ढंग से Completed बनाता है इसका संपत्ति हिस्सा। हमने अभी भी उन मामलों पर कब्जा नहीं किया है जहां पूर्ण वस्तुओं की संख्या वास्तव में बदलती है, लेकिन हमने कुछ हद तक अनावश्यक अधिसूचनाओं की संख्या कम कर दी है।

अब viewmodel सिर्फ संपत्ति है:

public TasksViewModel Tasks { get; set; } 

... और आप Tasks, Tasks.Count, और आसानी से Tasks.Completed करने के लिए बाध्य कर सकते हैं।


एक विकल्प के रूप से, अगर आप "मुख्य" व्यू-मॉडल पर इन अन्य गुण पैदा होता है, यदि आप एक subclassed ObservableCollection<T> की इस धारणा को कुछ पद्धति जहां आप एक Action<string> में पारित कर सकते हैं के साथ एक बनाने के लिए ले जा सकते हैं प्रतिनिधि, जो मुख्य दृश्य-मॉडल पर संपत्ति परिवर्तन अधिसूचना बढ़ाने और संपत्ति के नामों की कुछ सूची का प्रतिनिधित्व करेगा। इस संग्रह में तो प्रभावी ढंग से दृश्य-मॉडल पर संपत्ति परिवर्तन सूचनाएं बढ़ा सकता है:

public class ObservableCollectionWithSubscribers<T> : ObservableCollection<T> 
{ 
    Action<string> _notificationAction = s => { }; // do nothing, by default 
    readonly IList<string> _subscribedProperties = new List<string>(); 

    public void SubscribeToChanges(Action<string> notificationAction, params string[] properties) 
    { 
     _notificationAction = notificationAction; 

     foreach (var property in properties) 
      _subscribedProperties.Add(property); 
    } 


    protected override void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
     base.OnPropertyChanged(e); 
     NotifySubscribers(); 
    } 

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     base.OnCollectionChanged(e); 
     NotifySubscribers(); 
    } 

    void NotifySubscribers() 
    { 
     foreach (var property in _subscribedProperties) 
      _notificationAction(property); 
    } 
} 

तुम भी ObservableCollection<Task> के रूप में संपत्ति प्रकार छोड़ सकते हैं।

public class ViewModel : INotifyPropertyChanged 
{ 
    public ViewModel() 
    { 
     var tasks = new ObservableCollectionWithSubscribers<Task>(); 
     tasks.SubscribeToChanges(Notify, "Completed"); 
     Tasks = tasks; 
    } 

    public ObservableCollection<Task> Tasks { get; private set; } 

    public int Completed 
    { 
     get { return Tasks.Count(t => t.IsComplete); } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    void Notify(string property) 
    { 
     var handler = PropertyChanged; 
     if(handler != null) handler(this, new PropertyChangedEventArgs(property)); 
    } 
} 
+1

यह निश्चित रूप से एक बेहतर डिजाइन है। –

4

मेरे लिए बल्कि सुरुचिपूर्ण लग रहा है। मैं वास्तव में नहीं जानता कि आप इसे और अधिक संक्षिप्त कैसे बनाते हैं।

ठीक है (कैसे अजीब इस तरह एक जवाब लिखने के लिए। किसी को वास्तव में और अधिक सुरुचिपूर्ण कुछ के साथ आता है, मैं इसे हटाना हो सकता है।), मैं एक बात है, मूल से संबंधित नहीं देखा प्रश्न: आपकी Tasks संपत्ति में सार्वजनिक सेटटर है। इसे private set; बनाएं, या आपको set को बैकिंग फ़ील्ड के साथ कार्यान्वित करने की आवश्यकता होगी ताकि आप पिछले उदाहरण पर प्रतिनिधि को हटा सकें, नए स्थान को प्रतिस्थापित कर सकें और तार कर सकें और 0 कार्य"कार्य", "गणना" और " पूरा कर लिया है"। (और कैसे Tasks निर्माता में स्थापित किया जाएगा देखकर, मेरा अनुमान है कि private set; बेहतर विकल्प है।)

Count के बारे में और अधिक सुरुचिपूर्ण Completed को अधिसूचित नहीं कर करता है, लेकिन यह एक बग ठीक करता है।

और कई MVVM चौखटे ताकि OnPropertyChanged("Count") के बजाय, आप OnPropertyChanged(() => Count) इतना है कि यह रिफैक्टरिंग उपकरण की मदद से किया renames का पालन करेंगे लिख सकते हैं, एक लैम्ब्डा से प्रॉपर्टी का नाम मिलता है। मुझे नहीं लगता कि नामकरण अक्सर होता है, हालांकि, लेकिन यह कुछ स्ट्रिंग अक्षर से बचता है।

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