指定DataContext(WPF)的正确方法

时间:2018-11-01 04:14:44

标签: c# wpf mvvm

我刚刚开始使用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

2 个答案:

答案 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事件,它们可以从“视图”的后台代码中调用命令操作。

  1. 添加Click="{Your click event name}"
  2. 点击事件上的F12以创建/转到该事件。

顺便说一句,这里是一种将VM绑定到数据对象的方法,而无需在XAML中进行。

Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding

相关问题