根据MVVM设计模式从ViewModel访问this.content

时间:2014-01-13 03:41:24

标签: c# .net wpf xaml mvvm

我确定我错过了一些非常愚蠢和愚蠢的事情,当我看到它时,我可能会踢自己,但我只是有一个简单的问题。

我在代码隐藏的构造函数中有一些带有网格的视图的代码,它执行以下操作:

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

所以我的问题是如何从ViewModel中执行此操作? ViewModel不知道“this”是什么,也没有“this”的引用。

据我所知,视图模型对象是在XAML中通过调用:

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

设置数据上下文如下:

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

但这并没有给TimersHostViewModel任何关于“this.Content”的知识,并且说TimersHost.Content没有帮助,因为TimersHost不是一个实际的对象,而是一个类,我需要一个实际的对象来从中获取“.Content”,它应该是正确的对象,来自后面代码的对象,但是如何将其转换为视图模型?

毕竟以下MVVM意味着ViewModel不应该对View有任何了解,并且View不应该对ViewModel有任何了解,并且他们只是通过绑定和INotifyPropertyChanged以及其他此类消息传递来回通信技术。我在另一个应用程序中做了很多这样的事情,所以我对基础知识有所了解,但仍然有些新的,仍在学习甚至重新学习。

我在下面列出了完整的来源。正如您所看到的那样,我正在尝试将代码从代码中移出并进入ViewModel,但是当我尝试从主网格获取this.Content时,我遇到了编译器错误。

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>

代码背后:

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);
    }
}
}

视图模型:

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);
    }
}
}

1 个答案:

答案 0 :(得分:1)

查看此链接:

ContextMenu in MVVM

您需要将上下文菜单项绑定到viewmodel中的集合/属性。 “这个。”将无效,因为这是背后的代码,不会转换为视图模型。

将其置于视图模型中:

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

ObservableCollection<ContextItem> Items {get;set;}

然后在你视图的上下文菜单中:

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

您想要“传递”到视图的任何内容都需要是视图模型中的属性/集合,您将永远不会直接在视图模型中使用像Gird / Context菜单这样的可视元素对象。 WPF为您处理绑定,这是WPF的主要优点。只需确保为属性实现INotifyPropertyChanged。我没有简化样本。

现在这并不意味着背后的代码永远不会出现,但它应该只涉及视觉元素,而不是视觉元素绑定的数据。

希望这有帮助