Get reference to clicked item in view - c#

I've recently been learning the MVVM pattern in WPF and just started making my first proper, rather big application. So far it's all smooth sailing, and I'm liking what I'm seeing a lot. However I recently met something of a stumbling block.
The application is built with a main TabControl, each TabItem containing a pretty big details view.
TabControl inside main View, ItemsSource bound to MainViewModel.OpenTabs
TabItem with data specific View+ViewModel
TabItem with data specific View+ViewModel
TabItem with data specific View+ViewModel
etc...
The OpenTabs collection is an ObservableCollection<BaseViewModel> on MainViewModel, and the TabControl's SelectedItem is bound to MainViewModel.ActiveTab.
So far so good! However, what I'm not sure I'm getting is how to handle closing of tabs while at the same time following MVVM. If I wasn't trying to be strict with the MVVM (in order to learn it properly), I'd just bind a MouseDown-event on the TabItem-headers and thus get a reference to the clicked item in that event, removing it from the OpenTabs collection in that way. But - unless I'm mistaken - the interaction logic shouldn't need references to actual UI items in order to be effective and proper MVVM.
So, how do I handle this MVVM style? Do I use a command that sends a specific parameter with it to my MainViewModel? It seems like the preferred implementation of ICommand in MVVM doesn't take object parameters (looking at MVVM Light as well as some other tutorials).
Should I just create a CloseTab(int id) public method on my MainViewModel and call that from the view codebehind after catching the Click on my TabItem close button? This seems like MVVM-cheating. :)
Also a final note - this should work even if I click close on a TabItem that isn't the currently active one. Otherwise it wouldn't be hard to setup with OpenTabs.Remove(ActiveTab).
Thanks for any help! I'd also appreciate any links to recommended reading/watching regarding these problems.
Solution: It seems the best way is to use a command that can accept command parameters. I used the RelayCommand from MVVM Light framework:
In MainViewModel:
CloseTabCommand = new RelayCommand<BaseViewModel>((vm) =>
{
OpenTabs.Remove(vm);
});
In XAML:
<Button
Command="{Binding Source={StaticResource MainViewModel}, Path=CloseTabCommand}"
CommandParameter="{Binding}">
Note: Your binding paths may of course vary depending on how your Views and ViewModels are set up.

The best and the right way is to create the command. In different frameworks ICommand usually has two implementation, with the parameter and without one (as often you do not need it).
MVVM light has two ICommand implementation as well: RelayCommand and RelayCommand<T>

I suggest creating your own DelegateCommand implementation, a good example on how to this can be found here or here. Or use the Prism variant, you can download it here.
With a DelegateCommand you can pass arguments down to your ViewModel.

Related

MVVM without commands

I'm learning Silverlight/MVVM. Now I'm facing problem with Commands, that I think are just overcomplicated. I wanted to execute close on child window after command.
According to separation of concers it should be execute from view, as I understand.
As far as I looked for solution I found it and it wasn't trivial for such a trivial task.
To sum it up, I must say that separation of view, viewmodel and model are great ideas.
Also Binding from View to ViewModel is nice and clean.
But what about Commands. As I understand they are just piece of code to execute (like delegates).But they are too complicated and troublesome.
I want to know you opinion. What about idea that VieModel would have properties and normal public methods, that it will be executed from events of views. If I will not pass any view related element to the view model it still will be MVVM, right?
Of course, there will be one drawback, that i will have to bind separatly IsEnabled to properties in ViewModel to mimic CanUpdate functionality of Commands. It's not that you view doesn't know about ViewModel.
Views are not very testable, are they?
It would be very flexible. For example, in event for click i would do some strict view logic, call method from viewmodel object and then maybe call another method and after all that do some more view logic.
So, what do you think?
You can try using Cailburn.Micro. It is an open-source framework that runs over WPF and hides some of it complexities. For example, it replaces command-classes with just plain method calls.
You can implement windows-closing by returning a special result that will do the actual closing of the view. That way your ViewModel will still be fully unit-testable, as you can check that expected result is returned, and it will not be view-aware.
Here is an example on how to implement this: https://stackoverflow.com/a/10175228/258737

GetView() vs property in ViewModel

I'm currently in the need of setting the SelectedIndex property of my TabControl when a certain event (IEventAggregator) takes place and thought about how I'd implement that.
I came up with 2 possibilities:
Use GetView() provided by ViewAware in order to access my TabControl and set the SelectedIndex to my value
Use a property in my associated ViewModel and bind this property to my TabControl's SelectedIndex property via XAML
Both options are working fine but I personally want to get this question answered since this is not the first time I'm wondering where to implement the functionality in such cases.
I know that the first option won't enable the Notify support but besides that: What would be the proper way?
Having a GetView() method to manipulate the view directly from the viewmodel completely breaks MVVM. You might as well just put all your logic in codebehind. The whole point of MVVM is to abstract away the actual view so that it is decoupled from the logic, and the app can be unit tested.
What if you change your mind about the tabs in the future and decide to show your multiple views some other way? You've now got to start editing your viewmodel to edit the new view instead of just tweaking some XAML.
And for unit testing you're going to have no way to mock out your TabControl.

MenuBarItems in MVVM

I am playing with mvvm and wpf. Now, my total solution is MVVM-friendly. The only thing i have put in code behind is the "make new product" & close buttons on the mainview.
Now im adding a menubar, and i was wondering if i can put these "make new product" & close Items in code behind, or is this a no go?
Thanks in advance.
The MVVM way to do it is commands. You can consider them as proxies between your declarative XAML and imperative VM.
Create CreateNewProductCommand, implementing ICommand.
Create a handler for the command performing the actual work as part of ICommand interface implementation (conventionally called On*** - OnCreateNewProductCommand) (you may want to pass paramteres for edit, which is supported by the interface too).
Expose your command as property of your VM.
Bind your menu item command (it'll likely have it, just search for properties containing Command) property to that command using standard binding syntax pointing to the relevant property created at a previous step.
This is not the only way to do it. There're more advanced techniques based on interactions/behaviors etc. Some of them would allow you to bypass command creation and bind your UI element event directly to the executable member of your VM.

MVVM viewmodel reference view

I am required to use the mvvm pattern. I know that the viewmodel should not care about the view from what I been reading. As a result I don't know how to solve this problem:
I have a dll that basically turns a textbox and listview into an autocomplete control:
SomeDll.InitAutocomplete<string>(TextBox1, ListView1, SomeObservableCollection);
anyways I don't know how to call that method from the viewmodel using the mvvm patter. if I reference the controls in the view I will be braking the rules.
I am new to MVVM pattern and my company requires me to follow it. what will be the most appropriate way of solving this problem?
I know I will be able to solve it by passing the entire view to the viewmodel as a constructor parameter but that will totaly break the mvvm pattern just because I need to reference two controls in the view.
What you're doing here is a pure view concern, so I'd recommend doing it in the view (i.e. the code-behind). The view knows about the VM and its observable collection, so why not let the code behind make this call?
(I'd also recommend seeing if you can get a non-code/XAML API for "SomeDll", but I have no idea how much control you might have over that)
There are two things that I'd point out here -
First, this is effectively all View-layer code. As such, using code behind isn't necessarily a violation of MVVM - you're not bridging that View->ViewModel layer by including some code in the code behind, if necessary.
That being said, this is often handled more elegantly in one of two ways -
You could wrap this functionality into a new control - effectively an AutoCompleteTextBox control. This would allow you to include the "textbox" and "listview" visual elements into the control template, and bind to the completion items within Xaml.
You could turn this into an attached property (or Blend behavior), which would allow you to "attach" it to a text box, and add that functionality (all within xaml). The items collection would then become a binding on the attached property (or behavior).

MVVM questions on Josh Smith's Sample Application

I have been working through Josh Smith's article on MVVM at http://msdn.microsoft.com/en-us/magazine/dd419663.aspx. Each section makes sense to me but I am having a hard time putting it all together as a coherent unit mentally. I have 2 questions that would help a ton.
If I were to build the sample ap, what would be the logical order to build it?
For the command structure, what happens, in what order when the ap is run?
I am also wondering if I should split this into 2 questions?
I agree with Yacoder on this one. Start with what you know, or your vision. If your vision is to get a certain UX, start in Expression Blend if you want to. If you know what functionality you want, start with the ViewModels and the Unit tests.
Smith's application starts with App.xaml.cs.
There the MainWindowViewModel and the MainWIndow is created and shown.
MainWindow.xaml is the next thing that happens. It defines the main portion of the UI. The main parts of this is showing two collections; Commands and Workspaces. Those are members of MainWindowViewModel.
Smith seems to like properties to check if their corresponding private fields are null and, if they are, assign them. Thus the "Commands" collection is created in line 51 of MainWindowViewModel which calls CreateCommands() just south of there.
The command classes are abstracted away by RelayCommand, probably because each command doesn't need to know much in the case of "Show All" or "Create". The methods for these two commands are in the MainWindowViewModel, because they are conceptually functions of the main window.
The Commands collection is visualized as a list in the Main Window, so they need some kind of presentable, user friendly text to describe them. Thus they are wrapped in their own CommandViewModels.
The commands are presented through the magic of XAML beginning at line 41 of MainWindow.xaml. The HeaderedContentControl is databound to the Commands collection, and specifies CommandsTemplate of MainWindowResources.xaml (starting at line 93 of that file). The template uses a HyperLink with its Command property bound to the Command property of the CommandViewModel.
When it comes to the Save button on the new customer form. This is bound from CustomerView.xaml, line 117. To the CustomerViewModel SaveCommand property in line 196. It is a RelayCommand pointing to methods in CustomerViewModel. Each customer view has its own instance of CustomerViewModel where the data for that customer goes. The instances of RelayCommand belong to those CustomerViewModels, so each view has it's own SaveCommand also. The action and predicate of the RelayCommand instance knows not only which methods and properties they point to, but also of which instance. The Save method of CustomerViewModel only uses data from that instance.
That's roughly how two views can have the same kind of buttons that do the same for their respective customer data.
Start with the part you know better. UI or model that is. Anyway you will have to do several iterations over the whole MVVM thing to make all parts fit together.
Your second question is not exactly clear for me, I'd say no commands are started just by the fact that application runs, some action, like button click triggers the command to do its action. Also, every command can be enabled/disabled, which is then reflected by the visual state of the corresponding control. It's quite a common pattern even out of the boundaries of the MVVM and WPF.
Josh Smith's article is the best resource on MVVM. But if you're unable to understand it straight away, you might consider reading mine which anyone can easily understand. http://codingtales.com/2010/02/06/creating-a-complete-tabbed-interface-in-wpf-using-mvvm/

Categories

Resources