खोज…


टिप्पणियों

मॉडल और दृश्य-मॉडल

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

पृथक्करण देखें

MVVM का इरादा उन तीन अलग-अलग क्षेत्रों - मॉडल, व्यू-मॉडल और व्यू को अलग करना है। यद्यपि यह दृश्य-मॉडल (VM) और (अप्रत्यक्ष रूप से) मॉडल तक पहुँचने के लिए स्वीकार्य है, लेकिन MVVM के साथ सबसे महत्वपूर्ण नियम यह है कि VM के पास व्यू या इसके नियंत्रण की कोई पहुँच नहीं होनी चाहिए। सार्वजनिक संपत्तियों के माध्यम से वीएम को वह सब कुछ उजागर करना चाहिए जो देखने की जरूरत है। VM को UI नियंत्रण जैसे TextBox , Button आदि को सीधे उजागर या हेरफेर नहीं करना चाहिए।

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

RelayCommand

दुर्भाग्य से RelayCommand वर्ग इस उदाहरण में इस्तेमाल WPF ढांचे का हिस्सा नहीं है (यह होना चाहिए था!), लेकिन आप इसे लगभग हर WPF डेवलपर टूल बॉक्स में पाएंगे। एक त्वरित खोज ऑनलाइन बहुत सारे कोड स्निपेट्स को प्रकट करेगी जो आप उठा सकते हैं, अपना खुद का बनाने के लिए।

के लिए एक उपयोगी विकल्प RelayCommand है ActionCommand जो के हिस्से के रूप प्रदान की जाती है Microsoft.Expression.Interactivity.Core जिसमें तुलनात्मक कार्यक्षमता प्रदान करता है।

WPF और C # का उपयोग करके बेसिक MVVM उदाहरण

WPF और C # का उपयोग करके, विंडोज़ डेस्कटॉप एप्लिकेशन में MVVM मॉडल का उपयोग करने के लिए यह एक बेसिक उदाहरण है। उदाहरण कोड एक सरल "उपयोगकर्ता जानकारी" संवाद को लागू करता है।

यहाँ छवि विवरण दर्ज करें

देखें

XAML

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
         <RowDefinition Height="Auto"/>
         <RowDefinition Height="Auto"/>
     </Grid.RowDefinitions>

    <TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="4" Text="{Binding FullName}" HorizontalAlignment="Center" FontWeight="Bold"/>

    <Label Grid.Column="0" Grid.Row="1" Margin="4" Content="First Name:" HorizontalAlignment="Right"/>
    <!-- UpdateSourceTrigger=PropertyChanged makes sure that changes in the TextBoxes are immediately applied to the model. -->
    <TextBox Grid.Column="1" Grid.Row="1" Margin="4" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Width="200"/>

    <Label Grid.Column="0" Grid.Row="2" Margin="4" Content="Last Name:" HorizontalAlignment="Right"/>
    <TextBox Grid.Column="1" Grid.Row="2" Margin="4" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left"  Width="200"/>

    <Label Grid.Column="0" Grid.Row="3" Margin="4" Content="Age:" HorizontalAlignment="Right"/>
    <TextBlock Grid.Column="1" Grid.Row="3" Margin="4" Text="{Binding Age}" HorizontalAlignment="Left"/>

</Grid>

और पीछे कोड

public partial class MainWindow : Window
{
    private readonly MyViewModel _viewModel;

    public MainWindow() {
        InitializeComponent();
        _viewModel = new MyViewModel();
        // The DataContext serves as the starting point of Binding Paths
        DataContext = _viewModel;
    }
}

देखें मॉडल

// INotifyPropertyChanged notifies the View of property changes, so that Bindings are updated.
sealed class MyViewModel : INotifyPropertyChanged
{
    private User user;
    
    public string FirstName { 
        get {return user.FirstName;} 
        set {
            if(user.FirstName != value) {
                user.FirstName = value;
                OnPropertyChange("FirstName");
                // If the first name has changed, the FullName property needs to be udpated as well.
                OnPropertyChange("FullName");
            }
        }
    }

    public string LastName {
        get { return user.LastName; }
        set {
            if (user.LastName != value) {
                user.LastName = value;
                OnPropertyChange("LastName");
                // If the first name has changed, the FullName property needs to be udpated as well.
                OnPropertyChange("FullName");
            }
        }
    }
    
    // This property is an example of how model properties can be presented differently to the View.
    // In this case, we transform the birth date to the user's age, which is read only.
    public int Age { 
        get {
            DateTime today = DateTime.Today;
            int age = today.Year - user.BirthDate.Year;
            if (user.BirthDate > today.AddYears(-age)) age--;
            return age;
        }
    }

    // This property is just for display purposes and is a composition of existing data.
    public string FullName {
        get { return FirstName + " " + LastName; }
    }

    public MyViewModel() {
        user = new User {
            FirstName = "John",
            LastName = "Doe",
            BirthDate = DateTime.Now.AddYears(-30)
        };
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChange(string propertyName) {
        if(PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

आदर्श

sealed class User
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime BirthDate { get; set; }
} 

दृश्य-मॉडल

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

public class CustomerEditViewModel
{
    /// <summary>
    /// The customer to edit.
    /// </summary>
    public Customer CustomerToEdit { get; set; }

    /// <summary>
    /// The "apply changes" command
    /// </summary>
    public ICommand ApplyChangesCommand { get; private set; }

    /// <summary>
    /// Constructor
    /// </summary>
    public CustomerEditViewModel()
    {
        CustomerToEdit = new Customer
                         {
                             Forename = "John",
                             Surname = "Smith"
                         };

        ApplyChangesCommand = new RelayCommand(
            o => ExecuteApplyChangesCommand(), 
            o => CustomerToEdit.IsValid);
    }

    /// <summary>
    /// Executes the "apply changes" command.
    /// </summary>
    private void ExecuteApplyChangesCommand()
    {
        // E.g. save your customer to database
    }
}

कंस्ट्रक्टर एक Customer मॉडल ऑब्जेक्ट बनाता है और उसे CustomerToEdit प्रॉपर्टी में असाइन करता है, ताकि यह दृश्य में दिखाई दे।

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

RelayCommand दो पैरामीटर लेता है - पहला वह प्रतिनिधि होता है जिसे कमांड निष्पादित होने पर बुलाया जाता है (उदाहरण के लिए एक बटन क्लिक के जवाब में)। दूसरा पैरामीटर एक प्रतिनिधि है जो एक बूलियन मान लौटाता है जो यह दर्शाता है कि कमांड निष्पादित कर सकता है; इस उदाहरण में यह ग्राहक की IsValid संपत्ति के लिए IsValid है। जब यह गलत हो जाता है, तो यह बटन या मेनू आइटम को निष्क्रिय कर देता है जो इस कमांड से जुड़ा होता है (अन्य नियंत्रण अलग तरीके से व्यवहार कर सकते हैं)। यह एक सरल लेकिन प्रभावी सुविधा है, जो विभिन्न स्थितियों के आधार पर नियंत्रण को सक्षम या अक्षम करने के लिए कोड लिखने की आवश्यकता से बचती है।

यदि आपको यह उदाहरण मिलता है और चल रहा है, तो TextBox es में से एक को खाली करने का प्रयास करें ( Customer मॉडल को अमान्य स्थिति में रखने के लिए)। जब आप TextBox से दूर जाते हैं, तो आपको यह पता लगाना चाहिए कि "लागू करें" बटन अक्षम हो गया है।

ग्राहक निर्माण पर टिप्पणी

दृश्य-मॉडल INotifyPropertyChanged (INPC) को लागू नहीं करता है। इसका मतलब यह है कि अगर एक अलग Customer वस्तु को CustomerToEdit संपत्ति को सौंपा जाना था, तो नए ऑब्जेक्ट को प्रतिबिंबित करने के लिए दृश्य के नियंत्रण नहीं बदलेंगे - TextBox अभी भी पिछले ग्राहक का नाम और उपनाम होगा।

उदाहरण कोड काम करता है क्योंकि Customer को व्यू-मॉडल के कंस्ट्रक्टर में बनाया जाता है, इससे पहले कि वह व्यू के DataContext को सौंपा जाए (जिस बिंदु पर बाइंडिंग को वायर्ड किया जाता है)। एक वास्तविक दुनिया के अनुप्रयोग में आप एक डेटाबेस से कंस्ट्रक्टर के अलावा अन्य तरीकों से ग्राहकों को पुनः प्राप्त कर सकते हैं। इसका समर्थन करने के लिए, VM को INPC को लागू करना चाहिए, और CustomerToEdit संपत्ति को "विस्तारित" गेट्टर और सेटर पैटर्न का उपयोग करने के लिए बदला जाना चाहिए, जिसे आप उदाहरण मॉडल कोड में देखते हैं, सेट्टर में PropertyChanged घटना को बढ़ाते हैं।

दृश्य-मॉडल के ApplyChangesCommand को INPC को लागू करने की आवश्यकता नहीं है क्योंकि कमांड को बदलने की बहुत संभावना नहीं है। आपको इस पैटर्न को लागू करने की आवश्यकता होगी यदि आप कमांडर को कंस्ट्रक्टर के अलावा कहीं और बना रहे हैं, उदाहरण के लिए किसी तरह का Initialize() तरीका।

सामान्य नियम यह है: यदि संपत्ति किसी भी दृश्य नियंत्रण के लिए बाध्य है तो INPC को लागू करें और संपत्ति का मूल्य निर्माणकर्ता के अलावा कहीं भी बदलने में सक्षम है। यदि संपत्ति का मूल्य केवल कंस्ट्रक्टर में सौंपा गया है (और आप इस प्रक्रिया में कुछ टाइपिंग से खुद को बचा लेंगे) तो आपको INPC को लागू करने की आवश्यकता नहीं है।

आदर्श

मॉडल एम वीवीएम में पहला "एम" है। मॉडल आमतौर पर एक वर्ग होता है जिसमें डेटा होता है जिसे आप किसी प्रकार के उपयोगकर्ता इंटरफ़ेस के माध्यम से उजागर करना चाहते हैं।

यहाँ कुछ गुणों का खुलासा करने वाला एक बहुत ही सरल मॉडल वर्ग है: -

public class Customer : INotifyPropertyChanged
{
    private string _forename;
    private string _surname;
    private bool _isValid;

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Customer forename.
    /// </summary>
    public string Forename
    {
        get
        {
            return _forename;
        }
        set
        {
            if (_forename != value)
            {
                _forename = value;
                OnPropertyChanged();
                SetIsValid();
            }
        }
    }

    /// <summary>
    /// Customer surname.
    /// </summary>
    public string Surname
    {
        get
        {
            return _surname;
        }
        set
        {
            if (_surname != value)
            {
                _surname = value;
                OnPropertyChanged();
                SetIsValid();
            }
        }
    }

    /// <summary>
    /// Indicates whether the model is in a valid state or not.
    /// </summary>
    public bool IsValid
    {
        get
        {
            return _isValid;
        }
        set
        {
            if (_isValid != value)
            {
                _isValid = value;
                OnPropertyChanged();
            }
        }
    }

    /// <summary>
    /// Sets the value of the IsValid property.
    /// </summary>
    private void SetIsValid()
    {
        IsValid = !string.IsNullOrEmpty(Forename) && !string.IsNullOrEmpty(Surname);
    }

    /// <summary>
    /// Raises the PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    private void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

यह वर्ग INotifyPropertyChanged इंटरफ़ेस को लागू करता है जो एक PropertyChanged घटना को उजागर करता है। जब भी कोई संपत्ति मान बदलता है तो इस घटना को उठाया जाना चाहिए - आप इसे उपरोक्त कोड में कार्रवाई में देख सकते हैं। PropertyChanged घटना WPF डेटा-बाइंडिंग तंत्रों में एक महत्वपूर्ण टुकड़ा है, क्योंकि इसके बिना, उपयोगकर्ता इंटरफ़ेस किसी संपत्ति के मूल्य में किए गए परिवर्तनों को प्रतिबिंबित करने में सक्षम नहीं होगा।

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

देखें

M V VM में व्यू "V" है। यह आपका यूजर इंटरफेस है। आप विज़ुअल स्टूडियो ड्रैग-एंड-ड्रॉप डिजाइनर का उपयोग कर सकते हैं, लेकिन अधिकांश डेवलपर्स अंततः कच्चे एक्सएएमएल कोडिंग को समाप्त करते हैं - HTML लिखने के समान अनुभव।

Customer मॉडल के संपादन की अनुमति देने के लिए यहां एक सरल दृश्य का XAML है। एक नया दृश्य बनाने के बजाय, इसे केवल WPF प्रोजेक्ट के MainWindow.xaml फ़ाइल में चिपकाया जा सकता है, बीच में <Window ...> और </Window> टैग: -

<StackPanel Orientation="Vertical"
            VerticalAlignment="Top"
            Margin="20">
    <Label Content="Forename"/>
    <TextBox Text="{Binding CustomerToEdit.Forename}"/>

    <Label Content="Surname"/>
    <TextBox Text="{Binding CustomerToEdit.Surname}"/>

    <Button Content="Apply Changes"
            Command="{Binding ApplyChangesCommand}" />
</StackPanel>

यह कोड एक साधारण डेटा एंट्री फॉर्म बनाता है जिसमें दो TextBox शामिल हैं - एक ग्राहक के नाम के लिए, और एक उपनाम के लिए। प्रत्येक TextBox ऊपर एक Label है, और फ़ॉर्म के नीचे एक "लागू करें" Button होता है।

पहला Text TextBox ढूंढें और इसे Text प्रॉपर्टी देखें:

Text="{Binding CustomerToEdit.Forename}"

TextBox के पाठ को एक निश्चित मान पर सेट करने के बजाय, यह विशेष घुंघराले ब्रेस सिंटैक्स पाठ को "पथ" CustomerToEdit.Forename बजाय बाइंड करने के लिए है। इस पथ के सापेक्ष क्या है? यह दृश्य का "डेटा संदर्भ" है - इस मामले में, हमारा दृश्य-मॉडल। बाध्यकारी पथ, जैसा कि आप समझ सकते हैं, दृश्य-मॉडल का CustomerToEdit गुण है, जो कि प्रकार का Customer है जो बदले में Forename नामक एक संपत्ति को उजागर करता है - इसलिए "बिंदीदार" पथ संकेतन।

इसी तरह, यदि आप Button के XAML को देखते हैं, तो इसमें एक Command है जो व्यू-मॉडल के ApplyChangesCommand संपत्ति के लिए बाध्य है। वीएम की कमांड के लिए एक बटन को वायर करने की जरूरत है।

DataContext

तो आप व्यू-मॉडल को दृश्य के डेटा संदर्भ के रूप में कैसे सेट करते हैं? एक तरीका इसे दृश्य के "कोड-पीछे" में सेट करना है। इस कोड फ़ाइल को देखने के लिए F7 दबाएं, और व्यू-मॉडल का एक उदाहरण बनाने के लिए मौजूदा कंस्ट्रक्टर में एक लाइन जोड़ें और इसे विंडो के DataContext प्रॉपर्टी में असाइन करें। इसे इस तरह देखना चाहिए:

    public MainWindow()
    {
        InitializeComponent();

        // Our new line:-
        DataContext = new CustomerEditViewModel();
    }

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

एमवीवीएम में कमांडिंग

MVVM- पैटर्न का सम्मान करते हुए WPF में Events को संभालने के लिए कमांड का उपयोग किया जाता है।

एक सामान्य EventHandler इस तरह दिखेगा ( Code-Behind में स्थित):

public MainWindow()
{
    _dataGrid.CollectionChanged += DataGrid_CollectionChanged;
}

private void DataGrid_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
        //Do what ever
}

कोई MVVM में भी ऐसा ही करने का उपयोग हम Commands :

 <Button Command="{Binding Path=CmdStartExecution}" Content="Start" />

मैं आपके आदेश गुणों के लिए किसी प्रकार के उपसर्ग ( Cmd ) का उपयोग करने की सलाह देता हूं, क्योंकि आपको मुख्य रूप से xaml में उनकी आवश्यकता होगी - इस तरह से उन्हें पहचानना आसान है।

चूंकि यह MVVM है, आप अपने ViewModel में उस Command (For Button "eq" Button_Click ) को संभालना चाहते हैं।

इसके लिए हमें मूल रूप से दो चीजों की जरूरत है:

  1. System.Windows.Input.ICommand
  2. RelayCommand (उदाहरण के लिए यहाँ से लिया गया है

एक साधारण उदाहरण इस तरह दिख सकता है:

private RelayCommand _commandStart;
public ICommand CmdStartExecution
{
   get
   {
      if(_commandStart == null)
      {
          _commandStart = new RelayCommand(param => Start(), param => CanStart());
      }
      return _commandStart;
   }
}

public void Start()
{
   //Do what ever
}

public bool CanStart()
{
    return (DateTime.Now.DayOfWeek == DayOfWeek.Monday); //Can only click that button on mondays.
}

तो यह विस्तार से क्या कर रहा है:

ICommand है जो xaml में Control लिए बाध्य है। RelayCommand आपकी कमांड को एक Action (यानी कॉल ए Method ) में रूट करेगा। नल-चेक केवल यह सुनिश्चित करता है कि प्रत्येक Command केवल एक बार (प्रदर्शन मुद्दों के कारण) आरंभ हो जाएगा। यदि आपने ऊपर दिए गए RelayCommand के लिंक को पढ़ा है तो आपने देखा होगा कि RelayCommand पास इसके निर्माण के लिए दो अधिभार हैं। (Action<object> execute) और (Action<object> execute, Predicate<object> canExecute)

इसका मतलब है कि आप (वस्तुतः) एक दूसरी Method जोड़कर एक bool लौटा सकते हैं, यह बताने के लिए कि "घटना" को Control कर सकते हैं कि आग लग सकती है या नहीं।

कि के लिए एक अच्छी बात यह है कि Button रों उदाहरण के लिए किया जाएगा Enabled="false" यदि Method वापस आ जाएगी false

CommandParameters

<DataGrid x:Name="TicketsDataGrid">
    <DataGrid.InputBindings>
        <MouseBinding Gesture="LeftDoubleClick" 
                      Command="{Binding CmdTicketClick}" 
                      CommandParameter="{Binding ElementName=TicketsDataGrid, 
                                                 Path=SelectedItem}" />
    </DataGrid.InputBindings>
<DataGrid />

इस उदाहरण में मैं अपने ViewModel में Click_Command के लिए DataGrid.SelectedItem पास करना चाहता हूं।

आपकी विधि इस तरह दिखनी चाहिए जबकि ICommand कार्यान्वयन स्वयं ऊपर के रूप में रहता है।

private RelayCommand _commandTicketClick;

public ICommand CmdTicketClick
{
   get
   {
       if(_commandTicketClick == null)
       {
           _commandTicketClick = new RelayCommand(param => HandleUserClick(param));
       }
       return _commandTicketClick;
   }
}

private void HandleUserClick(object item)
{
    MyModelClass selectedItem = item as MyModelClass;
    if (selectedItem != null)
    {
        //Do sth. with that item
    }
}


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow