我刚刚开始使用WPF。在我的新应用程序中,我首先实现了带有上下文菜单的通知图标。接下来,我开始构建MVVM框架,发现新的更改会影响已经实现的代码。
我正在使用Hardcodet中的NotifyIcon。我的初始版本是这样的:
<Window x:Class="ScanManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpf="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:commands="clr-namespace:ScanManager.Commands"
Title="Scan" Height="542" Width="821">
<Grid Visibility="Visible" Loaded="form_Loaded">
...
<tb:TaskbarIcon HorizontalAlignment="Left" Margin="357,537,0,0" Name="mainTaskbarIcon" VerticalAlignment="Top" IconSource="/Icons/TestIcon.ico" IsHitTestVisible="True" ToolTipText="Test Test" >
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="_Show" Command="{commands:ShowMainWindowCommand}" CommandParameter="{Binding}" />
<MenuItem Header="_Hide" Command="{commands:HideMainWindowCommand}" CommandParameter="{Binding}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
<Button Name="hideButton" Content="Hide window" Height="23" HorizontalAlignment="Right" Margin="0,408,50,0" VerticalAlignment="Top" Width="92" IsEnabled="True" Click="hideButton_Click" />
</Grid>
</Window>
接下来,我开始根据文章The World's Simplest C# WPF MVVM Example引入MVVM模式。示例项目添加了指向ViewModel类的DataContext。
<Window.DataContext>
<ViewModels:Presenter/>
</Window.DataContext>
此更改影响了通知图标的工作方式。简而言之,ShowMainWindowCommand和HideMainWindowCommand对象的覆盖方法ICommand.CanExecute(object parameter)
和ICommand.Execute(object parameter)
开始接收Presenter
中定义的对象Window.DataContext
而不是原始的Hardcodet.Wpf.TaskbarNotification.TaskbarIcon
。我猜这是因为添加的DataContext影响了{Binding}
的{{1}}值。
CommandParameter
方法期望参数为Execute
以便标识父Window对象,然后可以将其设置为显示或隐藏。
我尝试解决该问题的方式是将TaskbarIcon
之外的所有元素从Window移到了网格下的UserControl并将DataContext应用于了网格
TaskbarIcon
它通过“通知”图标解决了该问题,但是我想知道这是否是解决问题的正确方法。我认为另一种方法可能是在添加<UserControl x:Class="ScanManager.Views.SControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
d:DataContext="{d:DesignInstance ViewModels:Presenter}">
<Grid Visibility="Visible">
<Grid.DataContext>
<ViewModels:Presenter/>
</Grid.DataContext>
<Button Name="hideButton" Command="{Binding Path=HideMainWindowCommand}" CommandParameter="{Binding}" Content="Hide window" Height="23" HorizontalAlignment="Right" Margin="0,408,50,0" VerticalAlignment="Top" Width="92" IsEnabled="True" Click="hideButton_Click" />
...
</Grid>
</UserControl>
后将原始版本的CommandParameter
中的MenuItem
设置为适当的值,但是我很难弄清楚这一点。
下一步,我尝试将DataContext
对象的DataContext
投射到UserControl
以便订阅INotifyPropertyChanged
事件,但是{{1} }属性以PropertyChanged
的形式出现,可能是因为它仅设置为DataContext
而不是null
:
Grid
任何将这些零件正确组装在一起的指导都将不胜感激。
修改
拒绝访问,这些选项对Button元素很有帮助。
如果我想回到顶部的初始版本,该MenuItem元素使用UserControl
和 INotifyPropertyChanged viewModel = (INotifyPropertyChanged)this.DataContext;
“。如果我添加Command="{commands:ShowMainWindowCommand}"
,可以进行更改吗?为了引用MenuItem的CommandParameter="{Binding}
属性,以便引用它们之前引用的内容(我假设是父元素),我尝试了Window.DataContext
,但它没有任何意义,就像之前一样,Execute / CanExecute接收到null。
Command/CommandParameter
答案 0 :(得分:0)
设置datacontext时,它也会传播到内部控件,是的,它会影响Binding上下文。无需创建UserControl,因为它不会阻止上下文传播。为了防止它更改控件的datacontext或指定绑定源。例如,如果您想更改按钮的上下文。
使用DataContext覆盖的方法:
<Grid Visibility="Visible">
<Grid.Resources>
<ViewModels:Presenter x:Key="buttonContext"/>
</Grid.Resources>
<Button DataContext="{StaticResource buttonContext}" Name="hideButton" Command="{Binding Path=HideMainWindowCommand}" CommandParameter="{Binding}" Content="Hide window" Height="23" HorizontalAlignment="Right" Margin="0,408,50,0" VerticalAlignment="Top" Width="92" IsEnabled="True" Click="hideButton_Click"/>
指定来源的方法:
<Grid.Resources>
<ViewModels:Presenter x:Key="buttonContext"/>
</Grid.Resources>
<Button Name="hideButton" Command="{Binding Source={StaticResource buttonContext}, Path=HideMainWindowCommand}" Content="Hide window" Height="23" HorizontalAlignment="Right" Margin="0,408,50,0" VerticalAlignment="Top" Width="92" IsEnabled="True" Click="hideButton_Click"/>
或者您也可以在根viewModel中具有ButtonContext属性,并通过以下方式解决它:
<Button DataContext="{Binding ButtonContext}" Name="hideButton" Command="{Binding Path=HideMainWindowCommand}" CommandParameter="{Binding}" Content="Hide window" Height="23" HorizontalAlignment="Right" Margin="0,408,50,0" VerticalAlignment="Top" Width="92" IsEnabled="True" Click="hideButton_Click"/>
如何订阅DataContextChanged事件:
public MainWindow()
{
InitializeComponent();
DataContextChanged += MainWindow_DataContextChanged;
处理事件:
private void MainWindow_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null && e.OldValue is INotifyPropertyChanged)
{
((INotifyPropertyChanged)e.OldValue).PropertyChanged -= MainWindow_PropertyChanged;
}
if (e.NewValue != null && e.NewValue is INotifyPropertyChanged)
{
((INotifyPropertyChanged)e.NewValue).PropertyChanged += MainWindow_PropertyChanged;
}
}
private void MainWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
...
}
答案 1 :(得分:-1)
您不必遵守Commanding
。更改菜单项以改为使用click事件,它们可以从“视图”的后台代码中调用命令操作。
Click="{Your click event name}"
顺便说一句,这里是一种将VM绑定到数据对象的方法,而无需在XAML中进行。
Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding