1

I'm just trying to understand WPF and MVVM and I am totally confused. Actually I need help with the following task:

I have a MainWindow, that carries a menu. From this menu, I want to load some data into a treeview, that is nested inside a usercontrol.

As I learned so far, I should use commands.
Where do I define them?

Assuming I have the menu, I would use 'inside the MainWindow.xaml.<br> I also would (of course) implement theCommand`-property inside the menu.

The code for Execution should be nested inside a seperate class or inside the codebehind of the UserControl, that needs the data.

This is my Usercontrol:

namespace PlcGenerator.Views { public partial class ProjectView : UserControl { public static RoutedCommand cmdLoadEcad = new RoutedCommand(); public ProjectView() { InitializeComponent(); } private void CanExecuteCmdLoadEcad(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } private void ExecutedCmdLoadEcad(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Command executed."); } } } 

Now this is a (reduced) part of the mainWindow:

<ribbon:RibbonWindow x:Class="PlcGenerator.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ribbon="clr-namespace:System.Windows.Controls.Ribbon;assembly=System.Windows.Controls.Ribbon" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:PlcGenerator" xmlns:viewmodels="clr-namespace:PlcGenerator.ViewModels" xmlns:views="clr-namespace:PlcGenerator.Views" mc:Ignorable="d" Loaded="Window_Loaded" Closing="ClosingApp" Title="Plc Generator" Height="600" Width="1200"> <Window.CommandBindings> <CommandBinding Command="{x:Static views:ProjectView.cmdLoadEcad}" Executed="ProjectView.ExecutedCmdLoadEcad" CanExecute="ProjectView.CanExecuteCmdLoadEcad"/> </Window.CommandBindings> <Window.Resources> <DataTemplate x:Name="settingsViewTemplate" DataType="{x:Type viewmodels:SettingsViewModel}"> <views:SettingsView DataContext="{Binding}"/> </DataTemplate> <DataTemplate x:Name="projectsViewTemplate" DataType="{x:Type viewmodels:ProjectViewModel}"> <views:ProjectView DataContext="{Binding ProjectVM, Source={StaticResource Locator}}"/> </DataTemplate> </Window.Resources> <DockPanel LastChildFill="True"> <ribbon:Ribbon DockPanel.Dock="Top"> <Ribbon.ApplicationMenu> <RibbonApplicationMenu SmallImageSource="Icons/ApplicationMenu.png"> <RibbonApplicationMenuItem Header="Neues Projekt" ImageSource="Icons/NewEntry.png" Command="{x:Static views:ProjectView.cmdLoadEcad}"/> </RibbonApplicationMenu> </Ribbon.ApplicationMenu> </ribbon:Ribbon> <!-- Fensterinhalt--> <ContentControl Margin="5" Content="{Binding}"/> </DockPanel> 

Here I get the error, that the command-element is not found, I think because it's not located in the codebehind MainWindow?

So how can I get that command work from MainWindow-Menu inside a separate class or usercontrol?

I'm really off!

UPDATE (as mentioned in first comment, I tried to understand the linked answer):

I created a ViewModelLocator:

using PlcGenerator.ViewModels; namespace PlcGenerator { public class ViewModelLocator { public ViewModelLocator() { this.ProjectVM = new ProjectViewModel(); this.SettingsVM = new SettingsViewModel(); } public ProjectViewModel ProjectVM{ get; set; } public SettingsViewModel SettingsVM { get; set; } } } 

I wanted to make it available for all views and controls, so I inserted it into App.xaml:

<Application x:Class="PlcGenerator.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:PlcGenerator" StartupUri="MainWindow.xaml"> <Application.Resources> <local:ViewModelLocator x:Key="Locator" /> </Application.Resources> </Application> 

First problem, I wasn't able to get it inserted while it was located in ViewModel-namespace. How does this work?

Now, before doing anything with commands, I'd like to fill the VMs while MainWindow is shown without UserControls, invoking a menu-event. So I wrote the following in codebehind of MainWindow:

public MainWindow() { InitializeComponent(); this.DataContext = new ViewModelLocator(); } public MnuOpenProject(object sender, RoutedEventArgs e) { //How can I now access the ProjectViewModel (ProjectVM) of the Datacontext? } 

In the MnuOpenProject-method, I didn't manage to have any access to DataContext.ProjectVM. How do I create this object inside the VM-locator?

After I solve that, I will go on with commands. Will I then put all the logic, like opening a project inside the Viewmodels?

NEXT TRY

also edited <DataTemplate... in the <Window.Resources...-section, see above

Is that the way it has to be?:

public partial class MainWindow : RibbonWindow { #region Data public ViewModelLocator viewModelLocator; #endregion public MainWindow() { InitializeComponent(); this.DataContext = new ViewModelLocator(); viewModelLocator = (ViewModelLocator)this.DataContext; } public MnuOpenProject(object sender, RoutedEventArgs e) { viewModelLocator.ProjectVM.Name = "ProjectName"; } } 

And also in all other views?

5
  • The commands go in the view model and you bind to them from the view. Please narrow down your question a bit. Commented May 19, 2020 at 14:28
  • I think I have total chaos meanwhile... My Goal is a MainWindow with a menu. There a Menuitem fires an event to open a fileopen-dialog and load some project-stuff. I was told to better do that, using commands so I tried to implement them but got confused of scope, where to implement and how to trigger the command and get the result (ViewModel) that is needed by a UserControl, that is nested inside the MainWindow. Meanwhile, I am a bit desperated because the more I search the more different approaches I find. Commented May 19, 2020 at 14:37
  • Now I am rewriting everything again and again and again. So I need assistance step by step. First, the ViewmodelLocator from the maintained answer looks good as it seems to store my data, available for every view and usercontrol. Commented May 19, 2020 at 14:41
  • Ok, disregarding the commands... is that the rigth way, that I wrote under NEXT TRY, to get access to that "global" Viewmodel? Commented May 19, 2020 at 14:54
  • Not exactly. See my answer. Commented May 19, 2020 at 14:59

2 Answers 2

1

You are creating an instance of the ViewModelLocator class using the following resource in the in App.xaml:

<local:ViewModelLocator x:Key="Locator" /> 

You could then bind the DataContext of the window to a property of the ViewModelLocator directly in the XAM markup like this:

<Window ... DataContext="{Binding ProjectViewModel, Source={StaticResource Locator}}" /> 

Using this approach, there is no need to set the DataContext programmatically in the code-behind.

Once you have set the DataContext to an instance of a view model returned from the ViewModelLocator, you can bind to any public property of the view model in the view, e.g.:

<TextBlock Text="{Binding SomePropertyInProjectViewModel" /> 
Sign up to request clarification or add additional context in comments.

7 Comments

That instance is created on app-startup, right? So how can I change the properties, and show them in a bound control? As I wrote, I open a projectfile and load e.g. a ProjectName. I'd like to insert this name into the property ViewModelLocator.ProjectVM.ProjectName. How do I solve that? In WindowResource I'm binding the Control like this: <views:ProjectView DataContext="{Binding ProjectVM, Source={StaticResource Locator}}"/> and I call the UserControl in codebehind: DataContext = viewModelLocator.ProjectVM;
Yes, it's created on startup. You should just set this.ProjectName in the view model. The question is where exactly you want to do this. MVVM is all about moving the logic to the view models and models. There should no code in your code-behind except for view-related logic.
But when I open the fileopen-dialog, this happens in codebhind. right? So now I have to parallel worlds. The viewmodel and the codebehind. I think I'm too stupid to understand or to explain?
@Telefisch: Yes, exactly. Please refer to this blog post.
Yes, I‘m always trying to only ask one question but sometimes the problem is a bit diffuse (to a beginner). Thank you so much for your help!
|
0

You need to have a ViewModel where you define the command if you are trying to use the MVVM pattern. You need to set the DataContext in the View to the ViewModel - if you need help doing this, see this answer.

1 Comment

I edited my question so could you please have a look at it?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.