我想创建一个在后台执行操作并可通过托盘图标进行控制的应用程序。这个托盘图标有一个带有复选框的上下文菜单,可以将其设置为已启用,然后后台任务就会启动。
我正在使用 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')
奇怪的是:当我为 IsChecked
输入 ElementName=
时,在 XAML 编辑器中发现 mainWindow
IS> 绑定(bind),正如 IntelliSense 向我展示的那样。
做 的工作是在 InitializeComponent();
之后以编程方式在代码隐藏中设置 DataContext
:
DataContext = this;
然后将绑定(bind)更改为:
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
?
最佳答案
您正在处理 ContextMenu
。这是最难处理的控件之一,因为它实际上是一个独立的窗口,它有自己的可视化树。你必须想象底层正在做类似这样的事情:
Window contextMenu = new Window();
contextMenu.Show();
这个窗口实例显然不能直接绑定(bind)到创建它的 MainWindow,可以吗?
绑定(bind)
ContextMenu 本身就是一个单独的实体。默认情况下,它的 DataContext
与父级(放置目标)隔离。幸运的是,ContextMenu 具有 PlacementTarget
属性,它自动成为您将其放入 XAML 中的对象。
<SomeControl>
<SomeControl.ContextMenu>
<ContextMenu ....>
<!-- This ContextMenu's PlacementTarget is automatically SomeControl -->
<ContextMenu>
</SomeControl.ContextMenu>
</SomeControl>
为了进行绑定(bind),您需要这样做:
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.DataContext}" ...>
对于您的情况,您正在使用一些外部 API。显然这个自定义控件已经将 DataContext 设置为其放置目标的 DataContext。
你的问题是因为你的窗口的 DataContext 没有设置为它自己,你已经解决了。 xaml 或代码隐藏都同样有效。
元素名称
当您使用ElementName
时,绑定(bind)引擎通常会忽略DataContext。它将开始在它自己的可视树 中寻找具有特定名称的元素。就像我最初所说的那样,ContextMenu 有自己的可视化树,因此显然它无法在其可视化树中找到该元素。
关于c# - 未找到具有显式 ElementName 的 WPF DataBinding,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38139705/