我想创建一个在后台执行操作并可通过Tray图标控制的应用程序。此托盘图标有一个上下文菜单,其复选框可以设置为“已启用”,然后启动后台任务。
我正在使用WPF和Hardcodet WPF NotifyIcon。
这是我的MainWindow.xaml:
<Window x:Name="mainWindow" x:Class="MyTrayApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyTrayApplication"
xmlns:tb="http://www.hardcodet.net/taskbar"
mc:Ignorable="d"
Title="MainWindow" Height="10" Width="10" Visibility="Hidden">
<tb:TaskbarIcon x:Name="taskbarIcon" IconSource="MyTrayApplication.ico" ToolTipText="My tray application" >
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem x:Name="enabledItem" Header="Enabled" IsCheckable="True" IsChecked="{Binding Path=Enabled, ElementName=mainWindow, UpdateSourceTrigger=PropertyChanged}"/>
<MenuItem x:Name="configureItem" Header="Configure..." Click="configureItem_Click"/>
<MenuItem x:Name="exitItem" Header="Exit" Click="exitItem_Click"/>
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</Window>
这是我的代码背后:
using System;
using System.Windows;
namespace MyTrayApplication {
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
private bool _enabled;
public MainWindow() {
InitializeComponent();
}
private void exitItem_Click(object sender, RoutedEventArgs e) {
Close();
}
private void configureItem_Click(object sender, RoutedEventArgs e) {
// Code to fire up configuration
}
public bool Enabled {
get {
return _enabled;
}
set {
_enabled = value;
Console.WriteLine(value);
// Code to enable the background task
}
}
}
}
这不起作用。运行时我在输出中得到以下内容:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=mainWindow'. BindingExpression:Path=Enabled; DataItem=null; target element is 'MenuItem' (Name='enabledItem'); target property is 'IsChecked' (type 'Boolean')
奇怪的是:当我为mainWindow
绑定输入ElementName=
时,在XAML编辑器中找到了IsChecked
IS ,因为IntelliSense向我展示了它。
DOES 的工作原理是在DataContext
之后立即以编程方式设置InitializeComponent();
:
DataContext = this;
然后将绑定更改为:
IsChecked="{Binding Enabled, UpdateSourceTrigger=PropertyChanged}"
这会输出以下内容:
System.Windows.Data Error: 40 : BindingExpression path error: 'Enabled' property not found on 'object' ''TaskbarIcon' (Name='taskbarIcon')'. BindingExpression:Path=Enabled; DataItem='TaskbarIcon' (Name='taskbarIcon'); target element is 'MenuItem' (Name='enabledItem'); target property is 'IsChecked' (type 'Boolean')
但它确实有效。
我在这里做错了什么?或者以编程方式设置DataContext
的方式?
答案 0 :(得分:6)
您正在处理ContextMenu
。这是最棘手的控件之一,因为它实际上是一个Window,它有自己的可视树。你必须想象底层是做类似的事情:
Window contextMenu = new Window();
contextMenu.Show();
这个窗口实例显然无法直接绑定到创建它的MainWindow,可以吗?
<强>装订强>
ContextMenu本身就是一个独立的实体。默认情况下,其DataContext
与父级(展示位置目标)隔离。幸运的是,ContextMenu具有PlacementTarget
属性,该属性自动成为您在XAML中放入的对象。
<SomeControl>
<SomeControl.ContextMenu>
<ContextMenu ....>
<!-- This ContextMenu's PlacementTarget is automatically SomeControl -->
<ContextMenu>
</SomeControl.ContextMenu>
</SomeControl>
为了进行绑定,您需要这样做:
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.DataContext}" ...>
对于您的情况,您使用的是一些外部API。显然,这个自定义控件已经将DataContext设置为其放置目标的DataContext。
您的问题是因为您的窗口的DataContext未设置为自己,您已经解决了。 xaml或代码隐藏工作同样适用。
<强>的ElementName 强>
使用ElementName
时,绑定引擎通常会忽略DataContext。它将开始在自己的可视树中查找具有特定名称的元素。就像我最初所说的那样,ContextMenu有自己的可视树,所以很明显它无法在其可视化树中找到该元素。
答案 1 :(得分:2)
这有点棘手。问题是ContextMenu不是可视树的一部分。请参阅此帖子以获取解决方案:WPF: Binding a ContextMenu to an MVVM Command
答案 2 :(得分:0)
元素名称区分大小写,您应该将其从“mainWindow”更新为“MainWindow”,因为绑定无法找到该元素,因为错误建议