WPF में MVVM - मॉडल में परिवर्तनों के ViewModel को कैसे अलर्ट किया जाए ... या मुझे करना चाहिए?


112

मैं कुछ MVVM लेखों से गुजर रहा हूं, मुख्य रूप से यह और यह

मेरा विशिष्ट प्रश्न यह है: मैं मॉडल से ViewModel में मॉडल परिवर्तन कैसे संवाद करूं?

जोश के लेख में, मैं नहीं देखता कि वह ऐसा करता है। ViewModel हमेशा गुणों के लिए मॉडल पूछता है। राहेल के उदाहरण में, वह मॉडल लागू करती है INotifyPropertyChanged, और मॉडल से घटनाओं को उठाती है, लेकिन वे स्वयं दृश्य द्वारा उपभोग के लिए हैं (वह ऐसा क्यों करता है, इस बारे में अधिक विवरण के लिए उसका लेख / कोड देखें)।

कहीं भी मुझे ऐसे उदाहरण नहीं दिखाई देते हैं, जहाँ मॉडल मॉडल गुणों के परिवर्तनों के ViewModel को सचेत करता है। इसने मुझे चिंतित कर दिया है कि शायद यह किसी कारण से नहीं हुआ है। क्या मॉडल में परिवर्तनों के ViewModel को सचेत करने के लिए एक पैटर्न है? ऐसा लगता है कि आवश्यक है (1) बोधगम्य रूप से प्रत्येक मॉडल के लिए 1 से अधिक ViewModel हैं, और (2) भले ही सिर्फ एक ViewModel है, मॉडल पर कुछ कार्रवाई के परिणामस्वरूप अन्य गुण बदले जा सकते हैं।

मुझे संदेह है कि फॉर्म के उत्तर / टिप्पणियां हो सकती हैं "आप ऐसा क्यों करना चाहेंगे?" टिप्पणियाँ, तो यहाँ मेरे कार्यक्रम का वर्णन है। मैं MVVM के लिए नया हूँ इसलिए शायद मेरा पूरा डिज़ाइन दोषपूर्ण है। मैं संक्षेप में इसका वर्णन करूँगा।

मैं कुछ ऐसा प्रोग्रामिंग कर रहा हूं जो "ग्राहक" या "उत्पाद" कक्षाओं की तुलना में अधिक दिलचस्प है (कम से कम, मेरे लिए!)। मैं ब्लैकजैक प्रोग्रामिंग कर रहा हूं।

मेरे पास एक ऐसा दृश्य है जिसमें कोई कोड पीछे नहीं है और बस ViewModel में संपत्तियों और आदेशों पर निर्भर करता है (जोश स्मिथ के लेख देखें)।

बेहतर या बदतर के लिए, मैं दृष्टिकोण है कि मॉडल जैसे ही नहीं, वर्गों को शामिल करना चाहिए ले लिया PlayingCard, Deck, लेकिन यह भी BlackJackGameवर्ग है कि पूरे खेल की स्थिति रहती है और जानता है कि जब खिलाड़ी चला बस्ट है, डीलर कार्ड आकर्षित करने के लिए है, और खिलाड़ी और डीलर वर्तमान स्कोर क्या है (21, 21, बस्ट, आदि से कम)।

से BlackJackGameमैं "drawcard" की तरह के तरीकों का पर्दाफाश और यह मेरे लिए हुआ है कि एक कार्ड तैयार हो जाती है, जैसे गुण CardScore, और IsBustअद्यतन किया जाना चाहिए और इन नए मूल्यों ViewModel को नहीं भेजी। शायद यह दोषपूर्ण सोच है?

एक दृष्टिकोण ले सकता है कि ViewModel ने DrawCard()विधि को बुलाया ताकि उसे अपडेट किए गए स्कोर के लिए पूछना चाहिए और पता लगाना चाहिए कि वह बस्ट है या नहीं। राय?

मेरे ViewModel में, मेरे पास प्लेइंग कार्ड (सूट, रैंक) के आधार पर वास्तविक छवि को हथियाने और इसे दृश्य के लिए उपलब्ध कराने का तर्क है। मॉडल को इससे चिंतित नहीं होना चाहिए (शायद अन्य ViewModel कार्ड छवियों को खेलने के बजाय केवल संख्याओं का उपयोग करेगा)। बेशक, शायद कुछ मुझे बताएंगे कि मॉडल को ब्लैकजैक गेम की अवधारणा भी नहीं होनी चाहिए और इसे व्यूमॉडल में संभाला जाना चाहिए?


3
आपके द्वारा किसी मानक ईवेंट तंत्र की तरह ध्वनियों का वर्णन करने वाली सहभागिता वह सब है जिसकी आपको आवश्यकता है। मॉडल नामक एक घटना को उजागर कर सकता है OnBust, और वीएम इसे सदस्यता ले सकता है। मुझे लगता है कि आप भी IEA दृष्टिकोण का उपयोग कर सकते हैं।
कोड

मैं ईमानदार रहूंगा, अगर मैं एक वास्तविक लाठी 'ऐप' बनाऊं तो मेरा डेटा सेवाओं / प्रॉक्सी की कुछ परतों के पीछे छिपा होगा और यूनिट-टेस्ट के एक पांडित्य स्तर A + B = C. के समान होगा। यह प्रॉक्सी होगा / सेवा जो परिवर्तनों की सूचना देती है।
मीरियन ह्यूजेस

1
सभी को धन्यवाद! दुर्भाग्य से, मैं केवल एक ही उत्तर चुन सकता हूं। मैं रैचेल को अतिरिक्त वास्तुकला सलाह के कारण चुन रहा हूं और मूल प्रश्न को साफ कर रहा हूं। लेकिन बहुत सारे शानदार जवाब थे और मैं उनकी सराहना करता हूं। -देवा
डेव


2
एफडब्ल्यूआईडब्ल्यू: वीएम और एम दोनों प्रति डोमेन अवधारणा को बनाए रखने की जटिलताओं के साथ कई वर्षों तक संघर्ष करने के बाद, अब मैं मानता हूं कि दोनों डीआरवाई में विफल रहते हैं; एक "डोमेन इंटरफ़ेस" और एक "ViewModel इंटरफ़ेस" - एक ही वस्तु पर दो INTERFACES होने से चिंताओं की आवश्यक जुदाई को और अधिक आसानी से किया जा सकता है। इस ऑब्जेक्ट को व्यापार तर्क और व्यू लॉजिक दोनों के लिए पारित किया जा सकता है, बिना भ्रम या सिंक्रनाइज़ेशन की कमी के। वह वस्तु एक "पहचान वस्तु" है - यह विशिष्ट रूप से इकाई का प्रतिनिधित्व करती है। डोमेन कोड बनाम व्यू कोड को अलग रखना एक वर्ग के अंदर ऐसा करने के लिए बेहतर उपकरणों की जरूरत है।
टूलमेकरसूट जूल

जवाबों:


61

यदि आप चाहते हैं कि आपके मॉडल परिवर्तनों के ViewModels को सचेत करें, तो उन्हें INotifyPropertyChanged को लागू करना चाहिए , और ViewModels को PropertyChange सूचनाएं प्राप्त करने के लिए सदस्यता लेनी चाहिए।

आपका कोड कुछ इस तरह दिख सकता है:

// Attach EventHandler
PlayerModel.PropertyChanged += PlayerModel_PropertyChanged;

...

// When property gets changed in the Model, raise the PropertyChanged 
// event of the ViewModel copy of the property
PlayerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SomeProperty")
        RaisePropertyChanged("ViewModelCopyOfSomeProperty");
}

लेकिन आमतौर पर यह केवल तभी आवश्यक होता है जब एक से अधिक ऑब्जेक्ट मॉडल के डेटा में बदलाव कर रहे हों, जो कि आमतौर पर ऐसा नहीं होता है।

यदि आपके पास कभी ऐसा मामला होता है, जहां आपके पास वास्तव में संपत्ति मॉडल को इसमें संलग्न संपत्ति संलग्न करने के लिए अपनी मॉडल संपत्ति का संदर्भ नहीं है, तो आप प्रिज्म EventAggregatorया एमवीवीएम लाइट जैसे मैसेजिंग सिस्टम का उपयोग कर सकते हैं Messenger

मेरे पास एक मेरे ब्लॉग पर मैसेजिंग सिस्टम का संक्षिप्त अवलोकन है , हालांकि इसे संक्षेप में प्रस्तुत करने के लिए, कोई भी वस्तु संदेश प्रसारित कर सकती है, और कोई भी वस्तु विशिष्ट संदेशों को सुनने के लिए सदस्यता ले सकती है। तो आप PlayerScoreHasChangedMessageएक वस्तु से प्रसारण कर सकते हैं , और दूसरी वस्तु उन प्रकार के संदेशों को सुनने के लिए सदस्यता ले सकती है और PlayerScoreजब वह सुनती है तो यह संपत्ति का अद्यतन करती है ।

लेकिन मुझे नहीं लगता कि यह आपके द्वारा वर्णित प्रणाली के लिए आवश्यक है।

एक आदर्श MVVM दुनिया में, आपका एप्लिकेशन आपके ViewModels से युक्त होता है, और आपके मॉडल आपके आवेदन को बनाने के लिए उपयोग किए जाने वाले ब्लॉक हैं। उनके पास आमतौर पर केवल डेटा होता है, इसलिए इस तरह के तरीके नहीं होंगे DrawCard()(जो कि एक ViewModel में होंगे)

तो आपके पास शायद इन जैसे सादे मॉडल डेटा ऑब्जेक्ट होंगे:

class CardModel
{
    int Score;
    SuitEnum Suit;
    CardEnum CardValue;
}

class PlayerModel 
{
    ObservableCollection<Card> FaceUpCards;
    ObservableCollection<Card> FaceDownCards;
    int CurrentScore;

    bool IsBust
    {
        get
        {
            return Score > 21;
        }
    }
}

और आपके पास एक ViewModel ऑब्जेक्ट होगा

public class GameViewModel
{
    ObservableCollection<CardModel> Deck;
    PlayerModel Dealer;
    PlayerModel Player;

    ICommand DrawCardCommand;

    void DrawCard(Player currentPlayer)
    {
        var nextCard = Deck.First();
        currentPlayer.FaceUpCards.Add(nextCard);

        if (currentPlayer.IsBust)
            // Process next player turn

        Deck.Remove(nextCard);
    }
}

(उपरोक्त वस्तुओं को सभी को लागू करना चाहिए INotifyPropertyChanged, लेकिन मैंने इसे सरलता के लिए छोड़ दिया)


3
अधिक सामान्यतः, क्या सभी व्यावसायिक तर्क / नियम मॉडल में जाते हैं? सभी तर्क कहां कहते हैं कि आप 21 तक कार्ड ले सकते हैं (लेकिन डीलर 17 पर रखा गया है), कि आप कार्ड को विभाजित कर सकते हैं, आदि। मैंने माना कि यह सब मॉडल वर्ग में था और इस कारण से मुझे लगा कि मुझे इसकी आवश्यकता है। मॉडल में एक BlacJackGame नियंत्रक वर्ग। मैं अभी भी इसे समझने की कोशिश कर रहा हूं और उदाहरण / संदर्भों की सराहना करूंगा। एक उदाहरण के लिए लाठी के विचार को आईओएस प्रोग्रामिंग पर एक आईट्यून्स वर्ग से हटा दिया गया था जहां व्यावसायिक तर्क / नियम निश्चित रूप से एक एमवीसी पैटर्न के मॉडल वर्ग में हैं।
डेव

3
@Dave हाँ, DrawCard()विधि आपके अन्य गेम लॉजिक के साथ, ViewModel में होगी। एक आदर्श एमवीवीएम एप्लीकेशन में, आपको यूआई के बिना पूरी तरह से अपने आवेदन को चलाने में सक्षम होना चाहिए, बस ViewModels बनाकर और उनके तरीकों को चलाकर, जैसे कि टेस्ट स्क्रिप्ट या कमांड प्रॉम्प्ट विंडो के माध्यम से। मॉडल आम तौर पर कच्चे डेटा और बुनियादी डेटा सत्यापन वाले डेटा मॉडल होते हैं।
राहेल १

6
सभी की मदद के लिए धन्यवाद राहेल। मुझे इस पर कुछ और शोध करना होगा या दूसरा प्रश्न लिखना होगा; मैं अभी भी खेल तर्क के स्थान पर भ्रमित हूं। आप (और अन्य) इसे ViewModel में डालने की वकालत करते हैं, अन्य लोग "व्यावसायिक तर्क" कहते हैं, जो मेरे मामले में मैं मानता हूं कि खेल के नियम हैं और खेल की स्थिति मॉडल में हैं (उदाहरण के लिए देखें: msdn.microsoft.com/en-us /library/gg405484%28v=pandp.40%29.aspx ) और stackoverflow.com/questions/10964003/… )। मुझे लगता है कि इस साधारण खेल में, यह शायद ज्यादा मायने नहीं रखता। लेकिन जानकर अच्छा लगेगा। Thxs!
डेव

1
@ क्या मैं "व्यापार तर्क" शब्द का गलत तरीके से उपयोग कर सकता हूं और इसे एप्लिकेशन तर्क के साथ मिला सकता हूं। आपके द्वारा लिंक किए गए MSDN लेख को उद्धृत करने के लिए करने के लिए "पुन: उपयोग के अवसरों को अधिकतम करने के लिए, मॉडल में कोई उपयोग मामला-विशिष्ट या उपयोगकर्ता कार्य-विशिष्ट व्यवहार या अनुप्रयोग तर्क नहीं होना चाहिए" और आमतौर पर, दृश्य मॉडल उन आदेशों या क्रियाओं को परिभाषित करेगा, जिनका प्रतिनिधित्व किया जा सकता है यूआई में और कि उपयोगकर्ता आह्वान कर सकते हैं " । इसलिए जैसी चीजें DrawCardCommand()व्यूमॉडल में होंगी, लेकिन मुझे लगता है कि आपके पास एक ऐसी BlackjackGameModelवस्तु हो सकती है , जिसमें एक DrawCard()विधि हो, जिसे आप चाहें तो कमांड बुला सकते हैं
राहेल

2
मेमोरी लीक से बचें। एक कमजोर पैटर्न का उपयोग करें। joshsmithonwpf.wordpress.com/2009/07/11/…
JJS

24

संक्षिप्त उत्तर: यह बारीकियों पर निर्भर करता है।

आपके उदाहरण में मॉडल को "अपने दम पर" अपडेट किया जा रहा है और पाठ्यक्रम के इन परिवर्तनों को किसी तरह से विचारों को प्रचारित करने की आवश्यकता है। चूँकि दृश्य केवल दृश्यमॉडल तक सीधे पहुँच सकते हैं, इसका मतलब है कि मॉडल को इन परिवर्तनों को संबंधित दृश्यमॉडल से संवाद करना चाहिए। ऐसा करने के लिए स्थापित तंत्र निश्चित रूप से है INotifyPropertyChanged, जिसका अर्थ है कि आपको इस तरह एक वर्कफ़्लो मिलेगा:

  1. Viewmodel मॉडल बनाया गया है और लपेटता है
  2. Viewmodel मॉडल की PropertyChangedघटना के लिए सदस्यता लेता है
  3. Viewmodel दृश्य के रूप में सेट है DataContext , गुण आदि बाध्य हैं
  4. दृश्यमॉडल पर ट्रिगर कार्रवाई देखें
  5. मॉडल पर Viewmodel कॉल विधि
  6. मॉडल खुद को अपडेट करता है
  7. Viewmodel मॉडल का संचालन करता है PropertyChangedऔर अपना खुद का उठाता हैPropertyChanged प्रतिक्रिया में
  8. फीडबैक लूप को बंद करते हुए व्यू इसकी बाइंडिंग में बदलाव को दर्शाता है

दूसरी ओर यदि आपके मॉडल में थोड़ा (या नहीं) व्यावसायिक तर्क निहित है, या यदि किसी अन्य कारण से (जैसे कि लेन-देन क्षमता प्राप्त करना) तो आपने प्रत्येक दृश्यमॉडल को अपने लिपटे मॉडल को "खुद" करने का फैसला किया, फिर मॉडल में सभी संशोधन से गुजरना होगा इस तरह की व्यवस्था आवश्यक नहीं होगी।

मैं यहाँ एक अन्य MVVM प्रश्न में इस तरह के डिज़ाइन का वर्णन करता हूँ ।


नमस्कार, आपने जो सूची बनाई है, वह शानदार है। हालांकि मुझे 7. और 8. के ​​साथ एक समस्या है। विशेष रूप से: मेरे पास एक ViewModel है, जो INotifyPropertyhanhanged को लागू नहीं करता है। इसमें बच्चों की एक सूची होती है, जिसमें बच्चों की एक सूची होती है (इसका उपयोग WPF ट्रीव्यू नियंत्रण के लिए ViewModel के रूप में किया जाता है)। मैं किसी भी बच्चे (TreeviewItems) में संपत्ति-परिवर्तन के लिए UserControl DataContext ViewModel को "कैसे" सुनूं? मैं वास्तव में सभी बाल-तत्वों की सदस्यता कैसे लेता हूं, जो INotifyPropertyChanged को लागू करते हैं? या मुझे एक अलग सवाल करना चाहिए?
इगोर

4

तुम्हारी पसंद:

  • INotifyPropertyChanged लागू करें
  • आयोजन
  • प्रॉक्सी मैनिप्युलेटर के साथ POCO

जैसा कि मैं इसे देखता हूं, INotifyPropertyChanged.Net का एक मूलभूत हिस्सा है। यानी इसमेंSystem.dll । अपने "मॉडल" में इसे लागू करना एक घटना संरचना को लागू करने के लिए समान है।

यदि आप शुद्ध पीओसीओ चाहते हैं, तो आपको प्रभावी ढंग से प्रॉक्सी / सेवाओं के माध्यम से अपनी वस्तुओं में हेरफेर करना होगा और फिर प्रॉक्सी को सुनकर आपके ViewModel को परिवर्तनों की सूचना दी जाएगी।

व्यक्तिगत रूप से मैं अभी-अभी INotifyPropertyChanged लागू करता हूं और फिर मेरे लिए गंदा काम करने के लिए FODY का उपयोग करता हूं। यह POCO को देखता है और महसूस करता है।

एक उदाहरण (FODY to IL Weave the PropertyChanged raisers):

public class NearlyPOCO: INotifyPropertyChanged
{
     public string ValueA {get;set;}
     public string ValueB {get;set;}

     public event PropertyChangedEventHandler PropertyChanged;
}

फिर आप अपने ViewModel को किसी भी बदलाव के लिए PropertyChanged सुन सकते हैं; या संपत्ति विशिष्ट परिवर्तन।

INotifyPropertyChanged मार्ग की सुंदरता, क्या आप इसे विस्तारित ऑब्जर्वेबल काबिलेक्शन के साथ जोड़ रहे हैं । इसलिए आप अपने पास की पोको ऑब्जेक्ट्स को एक संग्रह में डंप करें, और संग्रह को सुनें ... अगर कुछ भी बदलता है, कहीं भी, आप इसके बारे में सीखते हैं।

मैं ईमानदार रहूंगा, यह "संकलक द्वारा संकलित ऑटोटेक्स्ट चैंज क्यों नहीं किया गया था" में शामिल हो सकता है, जो चर्चा करता है, जो इसके लिए भटकता है: c # में प्रत्येक वस्तु को यह सूचित करने की सुविधा होनी चाहिए कि क्या इसका कोई हिस्सा बदला गया था; यानी डिफ़ॉल्ट रूप से INotifyPropertyChanged लागू करें। लेकिन यह नहीं है और सबसे अच्छा मार्ग है, जिसे कम से कम प्रयास की आवश्यकता है, आईएल वीविंग (विशेष रूप से FODY ) का उपयोग करना है।


4

बहुत पुराना धागा लेकिन बहुत खोज के बाद मैं अपने स्वयं के समाधान के साथ आया: ए प्रॉपर्टीचेंजप्रॉक्सी

इस वर्ग के साथ, आप आसानी से किसी और के NotifyPropertyChanged में पंजीकरण कर सकते हैं और पंजीकृत संपत्ति के लिए निकाल दिए जाने पर उचित कार्रवाई कर सकते हैं।

यहां एक नमूना है कि यह कैसे दिख सकता है जब आपके पास एक मॉडल संपत्ति "स्थिति" होती है जो उस पर बदल सकती है और फिर स्वचालित रूप से यह देखने के लिए ViewModel को सूचित करना चाहिए कि यह स्वयं की संपत्ति है, यह "स्थिति" संपत्ति पर है ताकि दृश्य भी अधिसूचित हो: )

public class MyModel : INotifyPropertyChanged
{
    private string _status;
    public string Status
    {
        get { return _status; }
        set { _status = value; OnPropertyChanged(); }
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MyViewModel : INotifyPropertyChanged
{
    public string Status
    {
        get { return _model.Status; }
    }

    private PropertyChangedProxy<MyModel, string> _statusPropertyChangedProxy;
    private MyModel _model;
    public MyViewModel(MyModel model)
    {
        _model = model;
        _statusPropertyChangedProxy = new PropertyChangedProxy<MyModel, string>(
            _model, myModel => myModel.Status, s => OnPropertyChanged("Status")
        );
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

और यहाँ वर्ग ही है:

/// <summary>
/// Proxy class to easily take actions when a specific property in the "source" changed
/// </summary>
/// Last updated: 20.01.2015
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TPropType">Type of the property</typeparam>
public class PropertyChangedProxy<TSource, TPropType> where TSource : INotifyPropertyChanged
{
    private readonly Func<TSource, TPropType> _getValueFunc;
    private readonly TSource _source;
    private readonly Action<TPropType> _onPropertyChanged;
    private readonly string _modelPropertyname;

    /// <summary>
    /// Constructor for a property changed proxy
    /// </summary>
    /// <param name="source">The source object to listen for property changes</param>
    /// <param name="selectorExpression">Expression to the property of the source</param>
    /// <param name="onPropertyChanged">Action to take when a property changed was fired</param>
    public PropertyChangedProxy(TSource source, Expression<Func<TSource, TPropType>> selectorExpression, Action<TPropType> onPropertyChanged)
    {
        _source = source;
        _onPropertyChanged = onPropertyChanged;
        // Property "getter" to get the value
        _getValueFunc = selectorExpression.Compile();
        // Name of the property
        var body = (MemberExpression)selectorExpression.Body;
        _modelPropertyname = body.Member.Name;
        // Changed event
        _source.PropertyChanged += SourcePropertyChanged;
    }

    private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == _modelPropertyname)
        {
            _onPropertyChanged(_getValueFunc(_source));
        }
    }
}

1
मेमोरी लीक से बचें। एक कमजोर पैटर्न का उपयोग करें। joshsmithonwpf.wordpress.com/2009/07/11/…
JJS

1
@JJS - OTOH, कमजोर इवेंट पैटर्न को खतरनाक मानते हैं । अगर मैं अपंजीकृत ( -= my_event_handler) को भूल जाता हूं, तो व्यक्तिगत रूप से मैं मेमोरी लीक होने का जोखिम उठाऊंगा , क्योंकि यह एक दुर्लभ + अप्रत्याशित ज़ोंबी समस्या से ट्रैक करना आसान है जो कभी भी हो सकता है।
टूलमेकरसिटेव

@ तुल्मेकर ने संतुलित तर्क जोड़ने के लिए धन्यवाद दिया। मेरा सुझाव है कि डेवलपर्स अपनी स्थिति में उनके लिए सबसे अच्छा काम करते हैं। इंटरनेट से स्रोत कोड को आँख बंद करके न अपनाएं। ईवेंटअग्रेगेटर / ईवेंटबस जैसे अन्य पैटर्न आमतौर पर उपयोग किए जाने वाले क्रॉस-कंपोनेंट मैसेजिंग हैं (जो कि अपने स्वयं के खतरों से भी प्रभावित होते हैं)
JJS

2

मुझे यह लेख सहायक लगा: http://social.msdn.microsoft.com/Forums/vstudio/en-US/3eb70678-c216-414f-a4a5-e1e3e557bb95/mvvm-businesslogic-is-part-of-the-?forum = WPF

मेरा सारांश:

MVVM संगठन के पीछे का विचार विचारों और मॉडलों के आसान पुन: उपयोग की अनुमति देना है और साथ ही डिकॉयड परीक्षण की अनुमति देना है। आपका दृश्य-मॉडल एक मॉडल है जो दृश्य संस्थाओं का प्रतिनिधित्व करता है, आपका मॉडल व्यावसायिक संस्थाओं का प्रतिनिधित्व करता है।

यदि आप बाद में पोकर गेम बनाना चाहते हैं तो क्या होगा? UI का अधिकांश पुन: प्रयोज्य होना चाहिए। यदि आपका गेम लॉजिक आपके व्यू-मॉडल में बंधा हुआ है, तो उन तत्वों को फिर से उपयोग करना बहुत मुश्किल होगा, बिना व्यू-मॉडल को रीप्रोग्राम किए। यदि आप अपना यूजर इंटरफेस बदलना चाहते हैं तो क्या होगा? यदि आपका गेम लॉजिक आपके व्यू-मॉडल लॉजिक से जुड़ा हुआ है, तो आपको अपने गेम को फिर से काम करने की आवश्यकता होगी। यदि आप एक डेस्कटॉप और एक वेब ऐप बनाना चाहते हैं तो क्या होगा? यदि आपके व्यू-मॉडल में गेम लॉजिक है, तो यह इन दोनों एप्लिकेशनों को साइड-बाय-साइड बनाए रखने के लिए जटिल हो जाएगा क्योंकि एप्लिकेशन लॉजिक अनिवार्य रूप से व्यू-मॉडल में बिजनेस लॉजिक के साथ बाध्य होगा।

डेटा परिवर्तन सूचनाएं और डेटा सत्यापन हर परत में होता है (दृश्य, दृश्य-मॉडल और मॉडल)।

मॉडल में आपका डेटा प्रतिनिधित्व (निकाय) और व्यावसायिक तर्क उन संस्थाओं के लिए विशिष्ट होते हैं। कार्ड का एक डेक निहित गुणों के साथ एक तार्किक 'बात' है। एक अच्छे डेक में डुप्लिकेट कार्ड नहीं हो सकते। इसे शीर्ष कार्ड (ओं) को प्राप्त करने के तरीके को उजागर करने की आवश्यकता है। यह जानने की जरूरत है कि जितने कार्ड बचे हैं, उससे ज्यादा कार्ड न दें। इस तरह के डेक व्यवहार मॉडल का हिस्सा होते हैं क्योंकि वे कार्ड के एक डेक में निहित होते हैं। डीलर मॉडल, प्लेयर मॉडल, हैंड मॉडल आदि भी होंगे। ये मॉडल बातचीत कर सकते हैं।

दृश्य-मॉडल में प्रस्तुति और अनुप्रयोग तर्क शामिल होंगे। खेल को प्रदर्शित करने से जुड़े सभी कार्य खेल के तर्क से अलग हैं। इसमें चित्र के रूप में हाथ प्रदर्शित करना, डीलर मॉडल के लिए कार्ड के लिए अनुरोध, उपयोगकर्ता प्रदर्शन सेटिंग्स आदि शामिल हो सकते हैं।

लेख की हिम्मत:

मूल रूप से, जिस तरह से मुझे यह समझाना पसंद है वह यह है कि आपके व्यावसायिक तर्क और संस्थाओं में मॉडल शामिल है। यह वही है जो आपका विशिष्ट अनुप्रयोग उपयोग कर रहा है, लेकिन कई अनुप्रयोगों में साझा किया जा सकता है।

दृश्य प्रस्तुति परत है - कुछ भी जो वास्तव में उपयोगकर्ता के साथ सीधे संबंधित है।

ViewModel मूल रूप से "गोंद" है जो आपके एप्लिकेशन के लिए विशिष्ट है जो दोनों को एक साथ जोड़ता है।

मेरा यहाँ एक अच्छा चित्र है जो दिखाता है कि वे कैसे इंटरफ़ेस करते हैं:

http://reedcopsey.com/2010/01/06/better-user-and-developer-experiences-from-windows-forms-to-wpf-with-mvvm-part-7-mvvm/

आपके मामले में - कुछ बारीकियों से निपटने की सुविधा देता है ...

सत्यापन: यह आमतौर पर 2 रूपों में आता है। उपयोगकर्ता इनपुट से संबंधित मान्यता ViewModel (मुख्य रूप से) और दृश्य में होगी (यानी: "न्यूमेरिक" टेक्स्टबॉक्स को पाठ को प्रवेश करने से रोकने से दृश्य, आदि में आपके लिए नियंत्रित किया जाएगा)। जैसे, उपयोगकर्ता से इनपुट का सत्यापन आमतौर पर एक वीएम चिंता का विषय है। कहा जा रहा है, अक्सर सत्यापन की एक दूसरी "परत" होती है - यह मान्यता है कि उपयोग किया जा रहा डेटा व्यावसायिक नियमों से मेल खाता है। यह अक्सर मॉडल का हिस्सा होता है - जब आप अपने मॉडल पर डेटा पुश करते हैं, तो यह सत्यापन त्रुटियों का कारण हो सकता है। फिर वीएम को इस जानकारी को व्यू में वापस भेजना होगा।

संचालन "बिना किसी दृश्य के पर्दे के पीछे, जैसे डीबी को लिखना, ईमेल भेजना, आदि": यह वास्तव में मेरे आरेख में "डोमेन विशिष्ट संचालन" का हिस्सा है, और वास्तव में मॉडल का विशुद्ध रूप से हिस्सा है। यह वह है जिसे आप एप्लिकेशन के माध्यम से उजागर करने का प्रयास कर रहे हैं। ViewModel इस जानकारी को उजागर करने के लिए एक पुल के रूप में कार्य करता है, लेकिन संचालन शुद्ध-मॉडल हैं।

ViewModel के लिए संचालन: ViewModel को केवल INPC से अधिक की आवश्यकता है - इसे किसी भी ऑपरेशन की आवश्यकता है जो आपके एप्लिकेशन (आपके व्यावसायिक तर्क नहीं) के लिए विशिष्ट है, जैसे कि वरीयताओं और उपयोगकर्ता की स्थिति, आदि। यह ऐप अलग-अलग होने जा रहा है। एप्लिकेशन द्वारा।, यहां तक ​​कि जब एक ही "मॉडल" को रोकना।

इसके बारे में सोचने का एक अच्छा तरीका - यह कहें कि आप अपने ऑर्डरिंग सिस्टम के 2 संस्करण बनाना चाहते हैं। पहला WPF में है, और दूसरा एक वेब इंटरफेस है।

साझा तर्क जो खुद आदेशों के साथ काम करता है (ईमेल भेजना, डीबी में प्रवेश करना, आदि) मॉडल है। आपका एप्लिकेशन उपयोगकर्ता के लिए इन ऑपरेशनों और डेटा को उजागर कर रहा है, लेकिन इसे 2 तरीकों से कर रहा है।

WPF एप्लिकेशन में, उपयोगकर्ता इंटरफ़ेस (दर्शक क्या बातचीत करता है) "दृश्य" है - वेब एप्लिकेशन में, यह मूल रूप से कोड है जो (कम से कम अंततः) क्लाइंट पर जावास्क्रिप्ट + एचटीएमएल + सीएसएस में बदल जाता है।

ViewModel बाकी "गोंद" है जो आपके द्वारा उपयोग किए जा रहे विशिष्ट दृश्य तकनीक / परत के साथ काम करने के लिए आपके मॉडल (ऑर्डर करने से संबंधित इन कार्यों) को अनुकूलित करने के लिए आवश्यक है।


शायद एक सरल उदाहरण एक संगीत खिलाड़ी है। आपके मॉडल में लाइब्रेरी और सक्रिय साउंड फ़ाइल और कोडेक्स और प्लेयर लॉजिक और डिजिटल सिग्नल प्रोसेसिंग कोड होंगे। दृश्य-मॉडल में आपके नियंत्रण और विज़ुअलाइज़ेशन और लाइब्रेरी ब्राउज़र शामिल होंगे। उस जानकारी को प्रदर्शित करने के लिए बहुत सारे यूआई तर्क की आवश्यकता होती है और एक प्रोग्रामर को यूआई सहज और मजेदार बनाने पर ध्यान केंद्रित करने की अनुमति देने के लिए एक प्रोग्रामर को संगीत नाटक पर ध्यान केंद्रित करने की अनुमति देना अच्छा होगा। व्यू-मॉडल और मॉडल को उन दो प्रोग्रामरों को इंटरफेस के एक सेट पर सहमत होने और अलग से काम करने की अनुमति देनी चाहिए।
वोट कॉफ़ी

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

2

के आधार पर अधिसूचना INotifyPropertyChanged और INotifyCollectionChanged वास्तव में आप क्या जरूरत है। संपत्ति परिवर्तनों की सदस्यता के साथ अपने जीवन को सरल बनाने के लिए, संपत्ति के नाम का संकलन-समय सत्यापन, मेमोरी लीक से बचने के लिए, मैं आपको जोश स्मिथ के एमवीवीएम फाउंडेशन से प्रॉपर्टी ओवरस्वर का उपयोग करने की सलाह दूंगा । जैसा कि यह परियोजना खुला स्रोत है, आप स्रोतों से अपनी परियोजना में सिर्फ उस वर्ग को जोड़ सकते हैं।

समझने के लिए, कैसे उपयोग करें PropertyObserver इस लेख को पढ़ें ।

इसके अलावा, रिएक्टिव एक्सटेंशन्स (Rx) पर एक नज़र डालें । आप अपने मॉडल से IObserver <T> को उजागर कर सकते हैं और दृश्य मॉडल में इसकी सदस्यता ले सकते हैं।


जोश स्मिथ के उत्कृष्ट लेख को संदर्भित करने और कमजोर घटनाओं को कवर करने के लिए बहुत बहुत धन्यवाद!
JJS

1

लोगों ने इसका जवाब देने के लिए एक अद्भुत काम किया, लेकिन इस तरह की स्थितियों में मुझे वास्तव में लगता है कि एमवीवीएम पैटर्न एक दर्द है इसलिए मैं जाऊंगा और एक पर्यवेक्षी नियंत्रक या एक निष्क्रिय दृश्य दृष्टिकोण का उपयोग करूंगा और कम से कम मॉडल ऑब्जेक्ट्स के लिए बाध्यकारी प्रणाली को जाने दूंगा। अपने आप परिवर्तन उत्पन्न कर रहे हैं।


1

मैं दिशात्मक मॉडल की वकालत कर रहा हूं -> मॉडल देखें -> लंबे समय से परिवर्तनों के प्रवाह को देखें, जैसा कि आप 2008 से मेरे MVVM लेख के परिवर्तन अनुभाग में देख सकते हैं । इसे लागू करने की आवश्यकता हैINotifyPropertyChanged लिए मॉडल पर । जहाँ तक मैं बता सकता हूँ, यह तब से आम चलन है।

क्योंकि आपने जोश स्मिथ का उल्लेख किया है, उसके प्रॉपर्टीज क्लास को देखें । यह मॉडल के INotifyPropertyChanged.PropertyChangedकार्यक्रम की सदस्यता के लिए एक सहायक वर्ग है ।

आप वास्तव में इस दृष्टिकोण को और आगे ले जा सकते हैं, जैसा कि मैंने हाल ही में अपने PropertiesUpdater वर्ग का निर्माण करके किया है । दृश्य-मॉडल पर गुणों की गणना जटिल अभिव्यक्तियों के रूप में की जाती है जिसमें मॉडल पर एक या अधिक गुण शामिल होते हैं।


1

INotifyPropertyChanged को लागू करने के लिए कुछ भी गलत नहीं हैमॉडल के अंदर और ViewModel के अंदर इसे सुनने के लिए । वास्तव में आप XAML में मॉडल की संपत्ति में भी डॉट कर सकते हैं: {बाइंडिंग मॉडल। ModelProperty}

जहां तक ​​आश्रित / परिकलित पठन-योग्य गुणों की बात है, अब तक मैंने इससे बेहतर और सरल कुछ नहीं देखा: https://github.com/StephenCleary/CalculatedProperties । यह बहुत सरल है, लेकिन अविश्वसनीय रूप से उपयोगी है, यह वास्तव में "एमवीवीएम के लिए एक्सेल सूत्र" है - बस उसी तरह से काम करता है जैसे एक्सेल आपके पक्ष से अतिरिक्त प्रयास के बिना फार्मूला कोशिकाओं में परिवर्तन का प्रसार करता है।


0

आप मॉडल से ईवेंट उठा सकते हैं, जिसे देखने के लिए सदस्यता की आवश्यकता होगी।

उदाहरण के लिए, मैंने हाल ही में एक परियोजना पर काम किया, जिसके लिए मुझे एक ट्रीव्यू (स्वाभाविक रूप से, मॉडल को एक पदानुक्रमित प्रकृति थी) उत्पन्न करना था। मॉडल में मेरे पास एक ऑब्जर्वेबलेक्लेशन था जिसे कहा जाता है ChildElements

व्यूमोडेल में, मैंने मॉडल में ऑब्जेक्ट का एक संदर्भ संग्रहीत किया था, और अवलोकनयोग्य CollectionChangedघटना की तरह सदस्यता लिया , जैसे: ModelObject.ChildElements.CollectionChanged += new CollectionChangedEventHandler(insert function reference here)...

तब मॉडल में परिवर्तन होते ही आपका व्यूमोडल स्वचालित रूप से अधिसूचित हो जाता है। आप उसी अवधारणा का उपयोग कर सकते हैं PropertyChanged, लेकिन आपको उस कार्य के लिए अपने मॉडल से संपत्ति परिवर्तन की घटनाओं को स्पष्ट रूप से उठाना होगा।


अगर श्रेणीबद्ध डेटा के साथ काम कर, आप को देखने के लिए चाहता हूँ डेमो 2 की मेरी MVVM लेख
HappyNomad

0

यह मुझे एक महत्वपूर्ण प्रश्न लगता है - जब ऐसा करने का कोई दबाव नहीं है। मैं एक परीक्षण परियोजना पर काम कर रहा हूं, जिसमें एक ट्री व्यू शामिल है। मेनू आइटम हैं और ऐसे हैं जो कमांड से मैप किए जाते हैं, उदाहरण के लिए हटाएं। वर्तमान में, मैं मॉडल मॉडल और व्यू मॉडल दोनों को अपडेट कर रहा हूं।

उदाहरण के लिए,

public void DeleteItemExecute ()
{
    DesignObjectViewModel node = this.SelectedNode;    // Action is on selected item
    DocStructureManagement.DeleteNode(node.DesignObject); // Remove from application
    node.Remove();                                // Remove from view model
    Controller.UpdateDocument();                  // Signal document has changed
}

यह सरल है, लेकिन लगता है कि एक बहुत ही बुनियादी दोष है। एक विशिष्ट इकाई परीक्षण कमांड निष्पादित करेगा, फिर दृश्य मॉडल में परिणाम की जांच करेगा। लेकिन यह परीक्षण नहीं करता है कि मॉडल अपडेट सही था, क्योंकि दोनों एक साथ अपडेट किए जाते हैं।

इसलिए शायद मॉडल अपडेट को व्यू मॉडल अपडेट ट्रिगर करने के लिए प्रॉपर्टी ऑबजर्वर जैसी तकनीकों का उपयोग करना बेहतर है। एक ही इकाई परीक्षण अब केवल तभी काम करेगा जब दोनों क्रियाएं सफल रहीं।

यह एक संभावित जवाब नहीं है, मुझे एहसास है, लेकिन यह वहाँ लगाने के लायक है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.