0
votes

So I hear that MVVM is the way to code in WPF. I have some kind of ViewModel in place. However if you look at the code snippet I currently have, it is tightly coupled with UI and this is the part I want to separate out.

My first challenge would be to bind all controls via the ViewModel instead of setting it directly in the code behind.

But that begs a question. I want to know how much detail should we get with MVVM style. I am assuming that User interaction can be best handled in the UI code behind whether to show up a field or not. But if that makes it tightly coupled still, then MVVM approach can be very involved process. In the code snippet below, I have added comments to give an idea of what UI is doing. UI shows a window with radio button containing two choices. Once the User picks one, then 2 comboboxes shows up, hiding/showing some other controls. Selecting an item in the first combo will populate the second combobox. Selecting an item from second combo will produce a code to show up. Some controls are hidden or shown depending on radio button choice selected.

I am curious on how to go about implementing MVVM and how much is too much...

public partial class TaskCodeWin : Window
{
    // Object to bind the combobox selections to.
    private SACodeGeneratorViewModel.ViewModelComboBox _viewModelComboBox;
    private SACodeGeneratorViewModel.ViewModelLitComboBox _viewModelLitComboBox;

    public TaskCodeWin()
    {
      // Display the window, Users need to pick one practice choice
        InitializeComponent();
    }

    private string[] _practicearea = new string[] { "", "" };
    private void lblPracticeArea_Click(object sender, RoutedEventArgs e)
    {
        //Set the practice area and then ...
        if (!lblPracticeArea.IsChecked.Value)
        {
            _practicearea[0] = "STD1";
            _practicearea[1] = "STD2";
        }
        else
        {
            _practicearea[0] = "MA2";
            _practicearea[1] = "";
        }

        //...set the data on appropriate comboBoxes
        SetInitializationByPractice();
    }

    private void SetInitializationByPractice()
    {
        if (_practicearea[0].Equals("MA2"))
        {
            SourceMAContext();  //Use the correct VIEWMODEL context
            InitializeMAComboBoxes();  //Populate ComboBoxes using the above VIEWMODEL
            ShowHideActivityCode(Visibility.Hidden);  // Show or Hide controls as appropriate
        }
    }

    private void PopulateFirstDigitCombo()
    {
        _viewModelComboBox.LoadFirstDigit();
    }

    private string _firstDigit = String.Empty;
    private void cmbFirstDigit_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        // Users picks FirstDigit, then ...
        if (cmbFirstDigit.SelectedIndex >= 0)
        {
            _firstDigit = SetSecondDigitByPractice(); // ...populate the Second ComboBoxes using the appropriate VIEWMODEL

            displayTaskCode(); //Show the 3 digit combination code
        }

        cmbSecondDigit.SelectedIndex = -1;  

    }

    private string SetSecondDigitByPractice()
    {
        // When Users picks FirstDigit, then populate the Second ComboBoxes using the appropriate VIEWMODEL and selected FirstDigit
        if (_practicearea[0].Equals("MA2"))
        {
            return _viewModelComboBox.LoadSecondDigit(cmbFirstDigit.SelectedItem as object);
        }
        else
        {
            return _viewModelLitComboBox.LoadSecondDigit(cmbFirstDigit.SelectedItem as object);
        }
    }

    private string[] _codes;
    private string[] displayTaskCode()
    {
        _codes = new string[] {_firstDigit,_secondDigit };

        if (_firstDigit.Equals(String.Empty) || _firstDigit.Equals("-"))
        {
            _codes[0] = "-";
        }
        if (_secondDigit.Equals(String.Empty) || _secondDigit.Equals("-"))
        {
            _codes[1] = "-";
        }
        else
        {
            _codes[0] = "";
        }

        return _codes;
    }


    #region Properties

    private string[] practiceArea
    {
        get
        {
            return _practicearea;
        }
    }

    private string[] displayCode
    {
        get
        {
            return _codes;
        }
    }

    #endregion

}
1
There does not seem to be any MVVM code in your example? What do you mean by too much? It's pretty simple... only code relating to your view should be in your view. The main goal of MVVM is to make your view models unit testable... this tends to be forgotten.BenjaminPaul
Great. Would that mean the only code behind should be : public TaskCodeWin() { // Display the window, Users need to pick one practice choice InitializeComponent(); }Ikram M.
There are whole books on MVVM. This question is too much.paparazzo
There are pure MVVM, where you have 0 code (auto-generated constructor with InitializeComponents() is not counted =P). And then there is non-strict MVVM (which I like the most), which says don't do bullshit if you can solve problem by few lines in code-behind. Both agree what ViewModel should not know about View. Opposite (View knows about ViewModel) is debatable. Sometimes it is easier to call ViewModel directly (as it's in DataContext anyway) then to make complications. You are free to join any mvvm party.Sinatr
Totally agree with @Sinatr. Viewmodel => View == bad. View => ViewModel == OK if needed. Think of ViewModels as something you will want to put in a PCL which does not know anything about System.Windows, except maybe System.Windows.Input.ICommand.Federico Berasategui

1 Answers

3
votes

The golden rule with MVVM is that the ViewModels must not deal with UI related-stuff.

So here are two advices to start using the MVVM pattern:

  • separate your UI controls (views, usercontrols...etc.) and your ViewModels into two different assemblies (the one that contains UI stuff references the one that contains ViewModels). This creates a strong separation that prevents any unwanted reference of an UI element in your ViewModels.
  • write some code behind if necessary, to deal with pure UI stuff, such as showing a popup, managing focus, drag & drop...etc.

So with your code:

  • Basically remove everything but the constructor. Add something to instantiate your ViewModel (most probably this.DataContext = new YourViewModel(); in the constructor).
  • Instead of the event handler, use Commanding, and execute the associated logic in the ViewModel
  • To conditionally show/hide UI elements, bind the Visibility property of your elements to a public bool property of your ViewModel, using a binding converter (something like http://www.codeproject.com/Tips/285358/All-purpose-Boolean-to-Visibility-Converter).
  • Expose ObservableCollections from the ViewModel and use bindings to populate your comboboxes
  • Etc.