简体   繁体   中英

Commands in WPF UserControls

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 the Command`-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?

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, eg:

<TextBlock Text="{Binding SomePropertyInProjectViewModel" />

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM