我刚开始使用 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 会影响 CommandParameter
的 {Binding}
值。
Execute
方法要求参数为 TaskbarIcon
,以便识别父 Window 对象,然后可以将其设置为显示或隐藏。
我尝试解决它的方法是将除 TaskbarIcon
之外的所有元素从 Window 移动到 UserControl,在 Grid 下并将 DataContext 应用于 Grid
<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>
它解决了通知图标的问题,但我想知道这是否是解决问题的正确方法。我认为另一种方法可能是在添加 DataContext
后将原始版本的 MenuItem
中的 CommandParameter
设置为适当的值,但是我有很难弄清楚这一点。
作为下一步,我尝试将 UserControl
对象的 DataContext
转换为 INotifyPropertyChanged
以订阅 PropertyChanged
事件,但是 DataContext
属性作为 null
出现,推测是因为它仅设置为 Grid
而不是 用户控件
:
INotifyPropertyChanged viewModel = (INotifyPropertyChanged)this.DataContext;
任何有关将这些部分正确组合在一起的指导将不胜感激。
编辑
拒绝访问,这些选项对 Button 元素很有帮助。
如果我想回到顶部的初始版本,MenuItem 元素使用 Command="{commands:ShowMainWindowCommand}"
和 CommandParameter="{Binding}
”。如果我添加 Window.DataContext
,是否可以对 MenuItem 的 Command/CommandParameter
属性进行更改以引用它们之前引用的内容(我假设,父元素)?我尝试了 CommandParameter="{Binding Path=mainTaskbarIcon}"
但它不起作用,这意味着,与以前一样,Execute/CanExecute 接收到 null。
<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">
<Window.DataContext>
<ViewModels:Presenter/>
</Window.DataContext>
<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 Path=mainTaskbarIcon}" />
<MenuItem Header="_Hide" Command="{commands:HideMainWindowCommand}" CommandParameter="{Binding Path=mainTaskbarIcon}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
...
</Grid>
</Window>
最佳答案
当您设置数据上下文时,它也会传播到内部控件,是的,它会影响绑定(bind)上下文。无需创建 UserControl,因为它不会阻止上下文传播。为了防止它更改控件的数据上下文或指定绑定(bind)源。例如,如果您想更改按钮的上下文。
使用 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)
{
...
}
关于c# - 指定 DataContext (WPF) 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53095124/