简体   繁体   English

按照MVVM设计模式从ViewModel访问this.content

[英]Accessing this.content from ViewModel following MVVM design pattern

I'm sure I'm missing something really silly and stupid here, and I'll probably kick myself when I see it, but I just have a simple question. 我确定我在这里错过了一些非常愚蠢和愚蠢的东西,当我看到它时我可能会踢自己,但我只是有一个简单的问题。

I've got some code in the constructor of the code-behind for a view with a grid, that does the following: 对于带有网格的视图,我在代码的构造函数中提供了一些代码,该代码执行以下操作:

Grid mainGrid = this.Content as Grid;
MenuItem item = mainGrid.ContextMenu.Items[0] as MenuItem;
this.ApplySkinFromMenuItem(item);

So my question is how can I do this from the ViewModel? 所以我的问题是如何从ViewModel中做到这一点? The ViewModel doesn't know what "this" is, and doesn't have a reference to "this" either. ViewModel不知道“ this”是什么,也没有对“ this”的引用。

It is my understanding that the view model object is created in XAML by calling: 据我了解,视图模型对象是通过以下方式在XAML中创建的:

<ObjectDataProvider x:Key="TimersHostViewModel" ObjectType="{x:Type local:TimersHostViewModel}"/>

And setting the data context like so: 像这样设置数据上下文:

<Grid DataContext="{StaticResource TimersHostViewModel}" Style="{DynamicResource styleBackground}">

But this doesn't give the TimersHostViewModel any knowledge about "this.Content", and saying TimersHost.Content doesn't help, because TimersHost isn't an actual object, but a class, and I need an actual object to get the ".Content" from, and it should be the right object, the object that is from the code behind, but how can I get that into the view model? 但这并不能为TimersHostViewModel提供有关“ this.Content”的任何知识,并且说TimersHost.Content没有帮助,因为TimersHost不是实际的对象,而是一个类,并且我需要一个实际的对象来获取“ “内容”,它应该是正确的对象,即来自后面代码的对象,但是如何将其放入视图模型?

After all following MVVM means that the ViewModel shouldn't have any knowledge about the View, and the View shouldn't have any knowledge about the ViewModel, and they just communicate back and forth with bindings and INotifyPropertyChanged and other such message passing techniques. 毕竟,以下MVVM意味着ViewModel不应该对View有任何了解,而View不应该对ViewModel有任何了解,它们只是通过绑定和INotifyPropertyChanged以及其他此类消息传递技术来回通信。 I've done a fair bit of this stuff in another application, so I'm some-what familiar with the basics, but still some-what new, and still learning and even re-learning. 我已经在另一个应用程序中做了相当多的工作,所以我对基础知识有些熟悉,但对某些事物还是很新的,并且还在学习甚至重新学习。

I've included the full source below. 我在下面提供了完整的源代码。 As you can see I'm in the process of trying to get the code out of the code behind and into the ViewModel, but I'm running into a compiler error when attempting to get this.Content from the main grid. 如您所见,我正在尝试将代码从背后的代码中提取出来并放入ViewModel中,但是在尝试从主网格中获取this.Content时遇到了编译器错误。

XAML: XAML:

<Window 
x:Class="TimersXP.TimersHost"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:local="clr-namespace:TimersXP"
Name="TimersHostView"
SizeToContent="Height"
Title="TimersXP"
WindowStartupLocation="CenterScreen"
WindowStyle="ToolWindow">
<Window.Resources>
    <ObjectDataProvider x:Key="TimersHostViewModel" ObjectType="{x:Type local:TimersHostViewModel}"/>
</Window.Resources>

<Grid.RowDefinitions>
    <RowDefinition Height="21"/>
    <RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="90"/>
</Grid.ColumnDefinitions>

<!--Main Menu-->
<Menu IsMainMenu="True" Style="{DynamicResource styleBanner}" Margin="0,0,0,1">
    <MenuItem Header="_Help" Style="{DynamicResource styleBanner}">
        <MenuItem Header="_About" Style="{DynamicResource styleBanner}"/>
    </MenuItem>
</Menu>

<!--Top Most Check Box-->
<CheckBox Content="Top Most" Grid.Column="1" Height="16" HorizontalAlignment="Left" Margin="11,2,0,0" Name="checkBox1" VerticalAlignment="Top" />

<!--Stopwatch & Countdown Tab Defintions-->
<TabControl Grid.Row="1" Grid.ColumnSpan="2" Style="{DynamicResource styleContentArea}">
    <TabItem Header="Stopwatch"/>
    <TabItem Header="Countdown"/>
</TabControl>

<!-- CONTEXT MENU -->
<Grid.ContextMenu>
  <ContextMenu Style="{DynamicResource styleBanner}" MenuItem.Click="OnMenuItemClick">
    <MenuItem Tag=".\Resources\Skins\BlackSkin.xaml" IsChecked="True">
        <MenuItem.Header>
        <Rectangle Width="120" Height="40" Fill="Black" />
        </MenuItem.Header>
    </MenuItem>
    <MenuItem Tag=".\Resources\Skins\GreenSkin.xaml">
        <MenuItem.Header>
        <Rectangle Width="120" Height="40" Fill="Green" />
        </MenuItem.Header>
    </MenuItem>
    <MenuItem Tag=".\Resources\Skins\BlueSkin.xaml">
        <MenuItem.Header>
        <Rectangle Width="120" Height="40" Fill="Blue" />
        </MenuItem.Header>
    </MenuItem>
  </ContextMenu>
</Grid.ContextMenu>
</Grid>
</Window>

Code Behind: 背后的代码:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;

namespace TimersXP
{
public partial class TimersHost : Window
{
    public TimersHost()
    {
        try
        {
            InitializeComponent();
        }
        catch (Exception ex)
        {
            Debug.WriteLine("CTOR Exception: " + ex.Message);
        }

        // Load the default skin.
        Grid mainGrid = this.Content as Grid;
        MenuItem item = mainGrid.ContextMenu.Items[0] as MenuItem;
        this.ApplySkinFromMenuItem(item);

    }

    public void OnMenuItemClick(object sender, RoutedEventArgs e)
    {
        MenuItem item = e.OriginalSource as MenuItem;

        // Update the checked state of the menu items.
        //Grid mainGrid = this.Content as Grid;
        //foreach (MenuItem mi in mainGrid.ContextMenu.Items)
        //mi.IsChecked = mi == item;

        // Load the selected skin.
        this.ApplySkinFromMenuItem(item);
    }

    void ApplySkinFromMenuItem(MenuItem item)
    {
        // Get a relative path to the ResourceDictionary which
        // contains the selected skin.
        string skinDictPath = item.Tag as string;
        Uri skinDictUri = new Uri(skinDictPath, UriKind.Relative);

        // Tell the Application to load the skin resources.
        App app = Application.Current as App;
        app.ApplySkin(skinDictUri);
    }
}
}

ViewModel: 视图模型:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace TimersXP
{
public class TimersHostViewModel
{
    public TimersHostViewModel()
    {
        // Load the default skin.
        Grid mainGrid = this.Content as Grid; <---- ERROR HERE
    }
    //public void TimersHostViewModel()
    //{
    //    // Load the default skin.
    //    Grid mainGrid = TimersHost.Content as Grid;
    //    MenuItem item = mainGrid.ContextMenu.Items[0] as MenuItem;
    //    //this.ApplySkinFromMenuItem(item);
    //}

    public void OnMenuItemClick(object sender, RoutedEventArgs e)
    {
        MenuItem item = e.OriginalSource as MenuItem;

        // Update the checked state of the menu items.
        //Grid mainGrid = this.Content as Grid;
        //foreach (MenuItem mi in mainGrid.ContextMenu.Items)
        //    mi.IsChecked = mi == item;

        // Load the selected skin.
        this.ApplySkinFromMenuItem(item);
    }

    void ApplySkinFromMenuItem(MenuItem item)
    {
        // Get a relative path to the ResourceDictionary which contains the selected skin.
        string skinDictPath = item.Tag as string;
        Uri skinDictUri = new Uri(skinDictPath, UriKind.Relative);

        // Tell the Application to load the skin resources.
        App app = Application.Current as App;
        app.ApplySkin(skinDictUri);
    }
}
}

check out this link: 查看此链接:

ContextMenu in MVVM MVVM中的ContextMenu

You need to bind your context menu items to a collection/property in your viewmodel. 您需要将上下文菜单项绑定到视图模型中的集合/属性。 "This." “这个。” will not work because that is the code behind and does not translate across to a view model. 将不起作用,因为这是背后的代码,并且不会转换为视图模型。

Put this in view model: 将其放在视图模型中:

class ContextItem : INotifyPropertyChanged
{
    public string Name;
    public ICommand Action;
    public Brush Icon;
}

ObservableCollection<ContextItem> Items {get;set;}

then in your view's context menu: 然后在视图的上下文菜单中:

<Grid.ContextMenu>
    <ContextMenu ItemsSource="{Binding Items}/>

Anything you want to "pass" to the view needs to be a property/collection in your view model, you will never directly use a visual element object like a Gird/Context menu in you viewmodel. 任何要“传递”到视图的东西都必须是视图模型中的属性/集合,而您绝不会在viewmodel中像Gird / Context菜单那样直接使用视觉元素对象。 WPF handles the binding for you, which is the main benefit of WPF. WPF为您处理绑定,这是WPF的主要优点。 Just make sure you implement INotifyPropertyChanged for the properties. 只需确保为属性实现INotifyPropertyChanged。 I didn't to simplify the sample. 我没有简化示例。

Now this does not mean there is never a case for code behind, but it should only involve visual elements, and not the data the visual elements bind to. 现在,这并不意味着永远都不需要后面的代码,而应该只包含视觉元素,而不应该包含视觉元素绑定的数据。

Hope this helps 希望这可以帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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