我确定我错过了一些非常愚蠢和愚蠢的事情,当我看到它时,我可能会踢自己,但我只是有一个简单的问题。
我在代码隐藏的构造函数中有一些带有网格的视图的代码,它执行以下操作:
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);
}
}
}
答案 0 :(得分:1)
查看此链接:
您需要将上下文菜单项绑定到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。我没有简化样本。
现在这并不意味着背后的代码永远不会出现,但它应该只涉及视觉元素,而不是视觉元素绑定的数据。
希望这有帮助