首页 > 解决方案 > WPF 从后台线程更新 UI

问题描述

我知道类似的问题已经被问过很多次,但我一直找不到在这种情况下有用的东西。我有一个使用wpf-notifyicon最小化到任务栏的应用程序。主窗口打开、验证然后隐藏。一个进程在后台运行,我希望它将更新发送到主线程。大多数时候它都有效。但是,任务栏图标有一个上下文菜单,允许用户打开应用程序设置。如果我打开然后关闭设置窗口,下次我尝试更新气球时,我会收到空​​引用错误System.NullReferenceException: 'Object reference not set to an instance of an object.' System.Windows.Application.MainWindow.get returned null.

就像一旦我打开另一个窗口,主窗口就丢失了,我不知道如何再次找到它。

这就是我更新气球的方式。此代码位于通知服务中,可从 MainWindow 视图模型内部和其他服务内部调用。

// Show balloon update on the main thread
Application.Current.Dispatcher.Invoke( new Action( () =>
{
    var notifyIcon = ( TaskbarIcon )Application.Current.MainWindow.FindName( "NotifyIcon" );
    notifyIcon.ShowBalloonTip( title, message, balloonIcon );
} ), DispatcherPriority.Normal );

通知图标在主窗口的 XAML 中声明。

<tb:TaskbarIcon
    x:Name="NotifyIcon"
    IconSource="/Resources/Icons/card_16x16.ico"
    ToolTipText="Processor"
    MenuActivation="LeftOrRightClick"
    DoubleClickCommand="{Binding ShowStatusCommand}">

    <tb:TaskbarIcon.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Settings" Command="{Binding ShowSettingsCommand}" />
            <Separator />                    
            <MenuItem Header="Exit" Command="{Binding ExitApplicationCommand}" />
        </ContextMenu>
    </tb:TaskbarIcon.ContextMenu>

    <tb:TaskbarIcon.TrayToolTip>
        <Border Background="Gray"
                BorderBrush="DarkGray"
                BorderThickness="1"
                CornerRadius="3"
                Opacity="0.8"
                Width="180"
                Height="20">
            <TextBlock Text="{Binding ListeningMessage }" HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Border>
    </tb:TaskbarIcon.TrayToolTip>
</tb:TaskbarIcon>

如何从后台线程安全地更新气球图标?

更新1:

上下文菜单绑定到视图模型中的命令。打开设置窗口

<ContextMenu>
    <MenuItem Header="Settings" Command="{Binding ShowSettingsCommand}" />
    <Separator />
    <MenuItem Header="Exit" Command="{Binding ExitApplicationCommand}" />
</ContextMenu>

VM中的命令是:

    public ICommand ShowSettingsCommand => new DelegateCommand
    {
        CommandAction = () =>
        {
            Application.Current.MainWindow = new Views.SettingsWindow( _logger, _hidservice, _certificateService );
            Application.Current.MainWindow.Show();
        }
    };

要关闭设置窗口,我在后面的窗口代码中有一个动作

public ICommand CancelSettingsCommand => new DelegateCommand
{
    CommandAction = () => CloseAction()
};


// In Code Behind
vm.CloseAction = new Action( () =>  this.Close()  );

标签: c#wpfnotifyicon

解决方案


您被覆盖主窗口。你不应该那样做。只需创建它的一个新实例并调用Show().

public ICommand ShowSettingsCommand => new DelegateCommand
{
    CommandAction = () =>
    {
        var settingsWindow = = new Views.SettingsWindow( _logger, _hidservice, _certificateService );
        settingsWindow.Show();
    }
};

推荐阅读