दुर्भाग्य से कोई भी महान एमवीवीएम उदाहरण ऐप नहीं है जो सब कुछ करता है, और चीजों को करने के लिए बहुत सारे दृष्टिकोण हैं। सबसे पहले, आप वहां से एक ऐप फ्रेमवर्क से परिचित होना चाहते हैं (प्रिज्म एक सभ्य विकल्प है), क्योंकि वे आपको सुविधाजनक उपकरण प्रदान करते हैं जैसे कि निर्भरता इंजेक्शन, कमांडिंग, इवेंट एग्रीगेशन इत्यादि। ।
प्रिज्म रिलीज़:
http://www.codeplex.com/CompositeWPF
इसमें बहुत छोटे उदाहरणों के साथ एक सुंदर सभ्य उदाहरण ऐप (स्टॉक ट्रेडर) शामिल है और कैसे। बहुत कम से कम यह कई सामान्य उप-पैटर्न का अच्छा प्रदर्शन है जिसका उपयोग लोग वास्तव में काम करने वाले MVVM बनाने के लिए करते हैं। मेरे पास सीआरयूडी और संवाद दोनों के लिए उदाहरण हैं, मेरा मानना है।
प्रिज्म हर प्रोजेक्ट के लिए जरूरी नहीं है, लेकिन इससे परिचित होना अच्छी बात है।
CRUD:
यह हिस्सा बहुत आसान है, WPF दो तरह से बाँधने से अधिकांश डेटा को संपादित करना वास्तव में आसान हो जाता है। असली चाल एक मॉडल प्रदान करना है जो यूआई स्थापित करना आसान बनाता है। बहुत कम से कम आप यह सुनिश्चित करना चाहते हैं कि आपका ViewModel (या व्यावसायिक ऑब्जेक्ट) INotifyPropertyChanged
बाइंडिंग का समर्थन करने के लिए लागू होता है और आप संपत्तियों को सीधे UI नियंत्रणों से बाँध सकते हैं, लेकिन आप IDataErrorInfo
सत्यापन के लिए भी लागू करना चाह सकते हैं । आमतौर पर, यदि आप किसी प्रकार के ORM समाधान का उपयोग करते हैं तो CRUD एक सेटिंग है।
यह आलेख सरल क्रूड संचालन प्रदर्शित करता है:
http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx
यह LinqToSql पर बनाया गया है, लेकिन यह उदाहरण के लिए अप्रासंगिक है - यह सब महत्वपूर्ण है कि आपकी व्यावसायिक वस्तुएं लागू होती हैं INotifyPropertyChanged
(जो कि LinqToSql द्वारा उत्पन्न कक्षाएं होती हैं)। MVVM उस उदाहरण का बिंदु नहीं है, लेकिन मुझे नहीं लगता कि इस मामले में यह मायने रखता है।
यह आलेख डेटा सत्यापन http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx प्रदर्शित करता है
फिर से, अधिकांश ORM समाधान उन कक्षाओं को उत्पन्न करते हैं जो पहले से ही लागू होते हैं IDataErrorInfo
और आमतौर पर कस्टम सत्यापन नियमों को जोड़ना आसान बनाने के लिए एक तंत्र प्रदान करते हैं।
अधिकांश समय आप किसी ORM द्वारा बनाई गई कोई वस्तु (मॉडल) ले सकते हैं और इसे ViewModel में लपेट सकते हैं जो इसे सहेजता / हटाता है - और आप UI को मॉडल के गुणों से सीधे बाँधने के लिए तैयार हैं।
दृश्य कुछ इस तरह दिखाई देगा (ViewModel के पास Item
मॉडल रखने वाली एक संपत्ति है, जैसे ORM में बनाया गया वर्ग):
<StackPanel>
<StackPanel DataContext=Item>
<TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
</StackPanel>
<Button Command="{Binding SaveCommand}" />
<Button Command="{Binding CancelCommand}" />
</StackPanel>
संवाद:
संवाद और MVVM थोड़ा मुश्किल हैं। मैं संवादों के साथ मध्यस्थ दृष्टिकोण का एक स्वाद का उपयोग करना पसंद करता हूं, आप इस StackOverflow प्रश्न में इसके बारे में थोड़ा और पढ़ सकते हैं:
WPF MVVM संवाद उदाहरण
मेरा सामान्य दृष्टिकोण, जो काफी क्लासिक MVVM नहीं है, को निम्नानुसार संक्षेपित किया जा सकता है:
एक डायलॉग ViewModel के लिए एक आधार वर्ग जो प्रतिबद्ध और कार्यों को रद्द करने के लिए आदेशों को उजागर करता है, एक घटना से यह पता चलता है कि एक संवाद बंद होने के लिए तैयार है, और आपको अपने सभी संवादों में और कुछ भी करने की आवश्यकता होगी।
आपके संवाद के लिए एक सामान्य दृश्य - यह एक खिड़की, या एक कस्टम "मोडल" ओवरले प्रकार नियंत्रण हो सकता है। इसके दिल में यह एक सामग्री प्रस्तुतकर्ता है जिसे हम व्यूमोडल में डंप करते हैं, और यह विंडो बंद करने के लिए वायरिंग को संभालता है - उदाहरण के लिए डेटा संदर्भ परिवर्तन पर आप जांच सकते हैं कि क्या नया व्यूमॉडल आपके बेस क्लास से विरासत में मिला है, और यदि है, तो प्रासंगिक करीबी घटना की सदस्यता लें (हैंडलर संवाद परिणाम प्रदान करेगा)। यदि आप वैकल्पिक सार्वभौमिक निकट कार्यक्षमता (X बटन, उदाहरण के लिए) प्रदान करते हैं, तो आपको ViewModel पर संबंधित करीबी कमांड को भी चलाना सुनिश्चित करना चाहिए।
कहीं न कहीं आपको अपने ViewModels के लिए डेटा टेम्प्लेट प्रदान करने की आवश्यकता होती है, वे विशेष रूप से बहुत सरल हो सकते हैं क्योंकि आपके पास संभवतः एक अलग नियंत्रण में संलग्न प्रत्येक संवाद के लिए एक दृश्य है। एक ViewModel के लिए डिफ़ॉल्ट डेटा टेम्प्लेट तब कुछ इस तरह दिखाई देगा:
<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
<views:AddressEditView DataContext="{Binding}" />
</DataTemplate>
संवाद दृश्य को इन तक पहुंच की आवश्यकता होती है, क्योंकि अन्यथा यह पता नहीं चलेगा कि ViewModel को कैसे दिखाया जाए, साझा किए गए संवाद UI से अलग इसकी सामग्री मूल रूप से यह है:
<ContentControl Content="{Binding}" />
अंतर्निहित डेटा टेम्पलेट दृश्य को मॉडल में मैप करेगा, लेकिन इसे कौन लॉन्च करता है?
यह नहीं तो mvvm हिस्सा है। इसका एक तरीका वैश्विक घटना का उपयोग करना है। मुझे लगता है कि एक बेहतर बात यह है कि एक घटना एग्रीगेटर टाइप सेटअप का उपयोग करना है, जो निर्भरता इंजेक्शन के माध्यम से प्रदान किया जाता है - इस तरह यह घटना एक कंटेनर के लिए वैश्विक है, न कि पूरे ऐप। प्रिज्म कंटेनर शब्दार्थ और निर्भरता इंजेक्शन के लिए एकता ढांचे का उपयोग करता है, और कुल मिलाकर मुझे एकता बहुत पसंद है।
आमतौर पर, यह इस घटना की सदस्यता के लिए रूट विंडो के लिए समझ में आता है - यह डायलॉग खोल सकता है और अपने डेटा संदर्भ को ViewModel पर सेट कर सकता है जो एक उठाए गए इवेंट के साथ पास हो जाता है।
इसे इस तरह सेट करने से ViewModels एप्लिकेशन को डायलॉग खोलने के लिए कह सकता है और यूआई के बारे में कुछ भी जाने बिना उपयोगकर्ता क्रियाओं का जवाब देता है ताकि अधिकांश भाग के लिए MVVM-ness पूरा बना रहे।
हालांकि, कई बार यूआई को संवाद बढ़ाने पड़ते हैं, जो चीजों को थोड़ा पेचीदा बना सकते हैं। उदाहरण के लिए विचार करें, यदि संवाद स्थिति बटन के स्थान पर निर्भर करती है जो इसे खोलता है। इस मामले में आपको कुछ यूआई विशिष्ट जानकारी की आवश्यकता होती है जब आप एक डायलॉग ओपन करते हैं। मैं आम तौर पर एक अलग वर्ग बनाता हूं जो एक ViewModel और कुछ प्रासंगिक UI जानकारी रखता है। दुर्भाग्य से वहाँ कुछ युग्मन अपरिहार्य लगता है।
एक बटन हैंडलर का छद्म कोड जो एक संवाद बढ़ाता है जिसे तत्व स्थिति डेटा की आवश्यकता होती है:
ButtonClickHandler(sender, args){
var vm = DataContext as ISomeDialogProvider; // check for null
var ui_vm = new ViewModelContainer();
// assign margin, width, or anything else that your custom dialog might require
...
ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
// raise the dialog show event
}
संवाद दृश्य डेटा को स्थिति में बाँध देगा, और निहित ViewModel को आंतरिक में पास करेगा ContentControl
। ViewModel अभी भी UI के बारे में कुछ नहीं जानता है।
सामान्य तौर पर मैं विधि के DialogResult
रिटर्न प्रॉपर्टी का उपयोग नहीं करता ShowDialog()
या डायलॉग बंद होने तक थ्रेड को ब्लॉक करने की अपेक्षा करता हूं । एक गैर-मानक मोडल संवाद हमेशा उस तरह काम नहीं करता है, और एक समग्र वातावरण में आप अक्सर एक इवेंट हैंडलर को किसी भी तरह से ब्लॉक नहीं करना चाहते हैं। मैं ViewModels को इससे निपटने देना पसंद करता हूं - एक ViewModel का निर्माता इसके प्रासंगिक घटनाओं, सेट कमिट / रद्द करने के तरीकों आदि की सदस्यता ले सकता है, इसलिए इस UI तंत्र पर भरोसा करने की कोई आवश्यकता नहीं है।
इसलिए इस प्रवाह के बजाय:
// in code behind
var result = somedialog.ShowDialog();
if (result == ...
मैं उपयोग करता हूं:
// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container
मैं इसे इस तरह से पसंद करता हूं क्योंकि मेरे अधिकांश संवाद गैर-अवरुद्ध छद्म-मोडल नियंत्रण हैं और इसे इस तरह से करना इसके चारों ओर काम करने की तुलना में अधिक सीधा लगता है। यूनिट टेस्ट के लिए भी आसान।