1

I want to clear some issues about MVVM violation.Becuase of this i've created a solution with some projects to demonstrate the cases. Here's the definition (Projects) of the solution :

  1. View (its a WPF Class Libraray and obviously it has the views)
  2. ViewModel (its a Class Libraray and obviously it has the viewmodels )
  3. Model (its a Class Libraray and obviously it has the models)
  4. Domain (its a Class Libraray and it has the application dataModels )
  5. Core (its a Class Libraray and it has the core of wpf like RelayCommnd or EventToCommand)
  6. Application ( its a wpf application and the startup project)
  7. ExternalCustomControl (its a wpf custom control library created by an imaginary third party company)

I Offer you to download the whole solution to understand better from Here

First Issue : I've an EventToCommand in the MainWindow.xaml for Closing Event of the window and attached it to MainWindowClosingCommand with the PassEventArgsToCommand set to True,then,in the MainViewModel there's a handler for the command named OnMainWindowClosing

private void OnMainWindowClosing(object parameter)
{
  var arg = parameter as CancelEventArgs;

  // What is the best way to show message dialog to user?
  // Do i have to send message to the View to show the messageBox dialog and then the window send me the answer back to continue?
  // What about IMessageBoxService? Doesn't it violates MVVM?

  // Doesn't following code violates the MVVM? 
  // Cancel the Closing of a Window isnt a UI side duty?
  arg.Cancel = true;

}

and totally whenever you want to set e.Handled or e.Cancel you face this issue.So do you know any other way that doesn't need to cast parameter as CancelEventArgs ?

Second Issue : I've an EventToCommand in the MainWindow.xaml for PreviewMouseDown Event of the Grid and attached it to MouseClickCommand with the PassEventArgsToCommand set to True,then,in the MainViewModel there's a handler for the command named OnMouseClick:

 private void OnMouseClick(object parameter)
{
  //      var arg = parameter as MouseButtonEventArgs; 

  // This is the violation of MVVM : To cast the parameter to MouseButtonEventArgs i have to add a refrence  
  //                                 to PresentationCore.dll in the ViewModel Project  

  // The next and worse step is that in most cases we need to know the Original Source of the event 
  //    (maybe its a StackPanel or a Label or etc) and this again vioaltes the MVVM

  // So Whats the WorkAround?

}

Third Issue : I used the ThirdParty Control(Imagine Infragistics or DevExpress or any other third party control but here as an example i created the imaginary control in my solution as the ExternalCustomControl Project) in my MainWindow Like this :

    <thirdParty:ThirdPartyCustomControl Grid.Row="1"
                                    ItemsSource="{Binding MyItemsSource,Converter={StaticResource converterKey}}" />

and ThirdPartyCustomControl has a property of type IEnumarabe<CustomControlDataModel> (CustomControlDataModel is a type that exists in the ExternalCustomControl assembly) But as you know if you want to create a property in MainViewModel for the control with the type CustomControlDataModel you have to add a refrence to ExternalCustomControl.dll in ViewModel Project and this violates MVVM so i created a type named MyDataModel and bound the ItemsSource of the control to MyItemsSource property in MainViewModel :

    // If i define MyItemsSource as List<CustomControlDataModel> i have to add a refrence to ExternalCustomControl.dll
// and i think its again violate the MVVM (because ExternalCustomControl.dll is a UI Side Controls Assembly) 
public List<MyDataModel> MyItemsSource { get; set; }

so i bound a property of type CustomControlDataModel to a property of type MyDataModel and of course i need a Converter :

public object Convert(object value, Type targetType, object parameter, c     System.Globalization.CultureInfo culture)
{
  // Imagine when the source data (MyDataModel) is huge (for example 1 milion) it (this dummy Conversion)
  // affects the performance

  if (value is List<MyDataModel>)
  {
    var result = new List<CustomControlDataModel>();

    (value as List<MyDataModel>).ForEach(myVal =>
      {
        var custDataModel = new CustomControlDataModel();
        custDataModel.ID = myVal.ID;
        custDataModel.Name = myVal.Name;
        custDataModel.Age = myVal.Age;

        result.Add(custDataModel);
      });

    return result;
  }
  return value;
}

and the question is do you know any better way than this dummy conversion or you normally add your third party assemblies to your view and viewmodel both?

These are the issues that i've faced and i'll be appreciated if you add more if you know the other issues and share your expertise to everyone.

Upadte:

For the MessageBox Part of first issue i suggest this link MesageBox

Thanks.

Community
  • 1
  • 1
HaMEd
  • 43
  • 1
  • 8
  • [UserControls should NOT have view models.](http://stackoverflow.com/a/25796096/1228) This is a *code smell*. You can tell because you're having problems doing it. You're also shoving UI concerns into the view model. UI concerns go in the *codebehind*. MVVM != no codebehind. –  Oct 09 '14 at 13:09
  • @Will this issue is not that case – HaMEd Oct 12 '14 at 20:02

3 Answers3

2

Excellent questions!

1) I personally believe you are correct, the use of a service violates MVVM. I wrote a very lengthy article on this exact topic a few weeks ago titled Implementing Dialog Boxes in MVVM. In that article I make the case for a "pure" solution to the overall problem of MVVM dialog boxes but it took 11 pages to explain how I arrived at that design. Fortunately the actual implementation is very straightforward, is similar to data templating, supports the multiple-project design you've specified and it works with 3rd party libraries. Have a read, I always appreciate objective feedback.

2) If you're using MVVM Lite then EventToCommand allows you to specify an argument converter. Here's an example where I used it to convert the window mouse move message argument to an equivalent representation in my view model:

<i:EventTrigger EventName="MouseMove">
    <cmd:EventToCommand Command="{Binding ElementName=_this, Mode=OneWay, Path=MouseMoveCommand}" PassEventArgsToCommand="True" EventArgsConverter="{StaticResource MouseEventArgsConverter}" />
</i:EventTrigger>

3) If I understand your question correctly I add a reference to both the view and view model projects, at least when that is my project structure. To be perfectly honest though I usually place my view and view models in the same project e.g. MyProject.UI, with everything sorted by category folders. I saw this done during a contract I was working on for a major international firm and in practice it works really well because you typically edit a view and it's corresponding view model at the same time; having them side-by-side in the solution window really does make the whole development process easier. Obviously some purists don't like it much, but personally I don't believe that simply having them in the same project breaks MVVM provided you still adhere strictly to that architecture. I've also never had it create any problems with unit testing etc where you need to create view models only.

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Having the view and viewmodel in the same project definitely doesn't violate any MVVM concepts. Some people like to have a project full of views, and another one for the viewmodels, but personally my projects are structured at the module level rather than at the view/viewmodel level. – Mashton Oct 13 '14 at 19:56
0

my closing code looks like this, i dont think that is violating mvvm

xaml

<Window>
 <i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <cmd:EventToCommand Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
    </i:EventTrigger>
  </i:Interaction.Triggers>

mainviewmodel.cs

    public ICommand ClosingCommand
    {
        get
        {
            return this._closingCommand ?? (this._closingCommand = new DelegateCommand<CancelEventArgs>((args) =>
                {
                    //i set a property in app.xaml.cs when i shut down the app there with
                    //Application.Current.Shutdown();
                    if (App.IsShutDown) return;


                if (this.HasChanges)
                {
                    var result = _msgService.Show("blup blup", "blup", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);

                    if (result == MessageBoxResult.No)
                    {
                        args.Cancel = true;
                    }
                }
                else
                {                      
                    var result = MessageBox.Show("Blup blup", "blup", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);

                    if (result == MessageBoxResult.No)
                    {
                        args.Cancel = true;
                    } 
                }                    
            }));
        }
    }

third issue (ThirdParty Control): i dont get your problem if the control need a type of collection then expose these colelction through your VM.

second issue: well is hard to say. i use something like this, and i would say is mvvm like ;)

 <DataGrid x:Name="myGrd">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="MouseDoubleClick">
         <Commanding:EventToCommand  Command="{Binding Path=OpenCommand}" 
                                     CommandParameter="{Binding ElementName=myGrd, Path=SelectedItem}"/>
      </i:EventTrigger>
    </i:Interaction.Triggers>

and at the end i do mvvm but always in mind to do things simple

blindmeis
  • 22,175
  • 7
  • 55
  • 74
0

For your second issue I think you just need to rethink what it is you are trying to achieve and why.

If you are wiring up a handler on a grid, and then making decisions based on what specific UI element of that grid was clicked in code you are probably doing things wrong. A Command called OnMouseClick is a bit of a code smell too, because that command says nothing. A command would be something like UserSelectedCommand, or GrapefuitSliceRequestedCommand ... i.e. much more specific than just a general 'something was clicked'.

You want to try and break that 'something' down into the logic for issue a clear and definitive command at that point rather than trying to work with MouseClickEventArgs and deciding in code what that means - your UI should be deciding what that means, and issuing commands to your VM.

So your individual UI elements should have the commands and binding, rather than trying to set commands at the layout UI level. If an Image is clicked that means something specific, if a row of a DataGrid is clicked that means something specific, and if a Slider is dragged that means something specific. Create your XAML so that it triggers those specific commands and don't push that responsibility up to a vague 'my whole ui was clicked and now I will use code to find out what exactly' way of thinking.

Mashton
  • 6,037
  • 2
  • 25
  • 35
  • Fisrt Example : imagine i need a DragDrop functionality and based on that i want to do some operations in viewmodel and finnaly insert something in database – HaMEd Oct 12 '14 at 20:07
  • Second Example : imagine i have an editable datagrid with two events RecordUpdating and RecordUpdated,as the user change somethimg in a record the RecordUpdating will fire and the whole operation in the event handler of this event is totally business type operation so its in the viewmodel BUT you need to pass the event arg to viewmodel to Cancel Or Proceed the update operation.Thank you so much – HaMEd Oct 12 '14 at 20:14
  • First Example: Drag-and-drop is definitely a UI operation, so is something I'd handle in code-behind. Once I'd jigged an element around (or ascertained what was moving to where) I'd then fire an event (from the code-behind) to alter the underlying collection that was bound as the ItemSource to whatever it was that was having elements dragged. – Mashton Oct 12 '14 at 20:42
  • Second example: What causes the RecordUpdating event to fire, is that something you are controlling? Is it from detecting an update to a record in the datagrid? Are you meaning that this is an update to a db? I'm guessing so, because updates to the individual rows from the VM property is just a matter of databinding. So assuming you're talking about updating a db, what is it that prevents you from handling the cancel operation without having to know specifics contained within a view event args? How are you intending to cancel a db operation anyway, once you've initiated it? – Mashton Oct 12 '14 at 20:48