我有一個使用 .NET 5 的相當簡單的 C# WPF 應用程式。基本上它位于后臺并為最終用戶計時特定事件。這些事件是從外部生成的 xml 檔案構建的。
該應用程式由 2 個視窗組成,一個隱藏的視窗負責所有的思考。如果它檢測到一個事件到期,它會引發一個 toast 訊息,單擊該訊息時會打開另一個視窗以向用戶顯示事件詳細資訊。一切正常并按預期運行,除非在 Windows 睡眠/暫停和恢復之后。我們顯然不希望事件在睡眠/暫停時累加,因此我們關閉隱藏的視窗并在恢復時再次打開它。那里沒有問題,但是一旦系統恢復并引發事件,可見視窗將拒絕顯示。如果在睡眠/掛起時可見視窗是打開的,那么在恢復時整個視窗被凍結并拒絕回應(關閉視窗的唯一方法是殺死應用程式并重新啟動)
APP代碼如下:-
public static Forms.NotifyIcon notifyIcon;
public static MainWindow mw;
public static ConfigWindow cw;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
SystemEvents.PowerModeChanged = new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
// Listen to notification activation
ToastNotificationManagerCompat.OnActivated = toastArgs =>
{
// Obtain the arguments from the notification
ToastArguments args = ToastArguments.Parse(toastArgs.Argument);
// Obtain any user input (text boxes, menu selections) from the notification
ValueSet userInput = toastArgs.UserInput;
// Need to dispatch to UI thread if performing UI operations
Application.Current.Dispatcher.Invoke(delegate
{
ToastControl.HandleToast(args);
});
};
ConfNotifyIcon();
OpenApp();
}
private void ConfNotifyIcon()
{
notifyIcon = new Forms.NotifyIcon();
notifyIcon.Icon = new System.Drawing.Icon("Images/Wellformation.ico");
notifyIcon.DoubleClick = OnClick;
notifyIcon.ContextMenuStrip = new Forms.ContextMenuStrip();
notifyIcon.ContextMenuStrip.Items.Add("Open", System.Drawing.Image.FromFile("Images/Wellformation.ico"), OnClick);
notifyIcon.ContextMenuStrip.Items.Add("Close", System.Drawing.Image.FromFile("Images/Wellformation.ico"), OnClose);
notifyIcon.ContextMenuStrip.Items.Add(new Forms.ToolStripSeparator());
notifyIcon.ContextMenuStrip.Items.Add("Exit", System.Drawing.Image.FromFile("Images/Wellformation.ico"), OnExit);
}
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
switch (e.Mode)
{
case PowerModes.Suspend:
this.Dispatcher.BeginInvoke((Action)(() =>
{
PrepareLock();
}), null);
break;
case PowerModes.Resume:
this.Dispatcher.BeginInvoke((Action)(() =>
{
PrepareAwake();
}), null);
break;
default:
break;
}
}
private void PrepareAwake()
{
OpenApp();
ConfNotifyIcon();
notifyIcon.Visible = true;
}
private void PrepareLock()
{
notifyIcon.Dispose();
cw.Close();
}
private void OnExit(object sender, EventArgs e)
{
Application.Current.Shutdown();
}
private void OnClose(object sender, EventArgs e)
{
mw.Close();
}
private void OnClick(object sender, EventArgs e)
{
OpenMain();
}
private void OpenMain()
{
mw = new();
mw.Show();
mw.Activate();
}
public static void OpenApp()
{
cw = new ConfigWindow();
}
隱藏的視窗 XAML 如下:-
<Window x:Class="WellformationDesktopApplication.ConfigWindow"
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:WellformationDesktopApplication"
mc:Ignorable="d"
Title="ConfigWindow" Height="1" Width="1" Visibility="Hidden" WindowState="Minimized">
<Grid>
</Grid>
</Window>
代碼如下:-
Timer at = new();
public ConfigWindow()
{
BuildConfig();
InitializeComponent();
}
public void refreshconfig()
{
myObjects.Clear();
myObjects = NudgeManager.GetNudges();
NudgeHandler(myObjects);
}
public void BuildConfig()
{
myObjects.Clear();
myObjects = GetEvents(); // pulls a list of event names with intervals from the config file
EventHandler(myObjects); //Goes through the list of events and figures out when the next event is due based upon the interval in the configuration
ActionTimer();
}
private void ActionTimer()
{
at.Interval = 60000;
at.Elapsed = ChecktActions;
at.AutoReset = true;
at.Enabled = true;
}
private void ChecktActions(object sender, ElapsedEventArgs e)
{
//Go through the trigger times for all events and see if those time have passed, if they have raise a toast showing the event name.
//If an event is raised reset the trigger time for the event based upon the interval and reset that time.
}
and the visible window XAML is as follows :-
<Window x:Class="WellformationDesktopApplication.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:WellformationDesktopApplication"
mc:Ignorable="d"
ResizeMode="NoResize"
WindowStyle="None"
Title="MainWindow" Height="500" Width="800" Background="{x:Null}" Foreground="{x:Null}" AllowsTransparency="True">
<Grid x:Name="BG">
<TextBlock x:Name="Display" HorizontalAlignment="Left" Margin="546,13,0,0" Text="Show event name and appropriate information about the event here..." VerticalAlignment="Top" FontSize="22"/>
</Grid>
</Window>
With Code as follows :-
public MainWindow()
{
InitializeComponent();
setstyles();
this.MouseLeftButtonDown = delegate { DragMove(); };
}
We know that everything to do with the ConfigWindow works fine, we know it is closed upon suspend and a new one is opened on resume with new timings set and all the appropriate alerts working.
The issue is with MainWindow as after a suspend and resume it cannot be interacted with at all. The open button on the icon does nothing, if the window is opened is is completely frozen and cannot be interacted with in any way, and if a toast is clicked on the window does not open but the rest of the toast handling code works fine around it. This happens on Win8, Win10 and Win11.
Any help out there as I am completely at a loss for how this is happening?
Thanks
uj5u.com熱心網友回復:
經過大量作業并逐節瀏覽代碼,將其注釋掉以查看它是否有所作為,我發現了問題。
隱藏在隱藏視窗代碼的深處(4 次呼叫函式),我發現 EventHandler() 也在為
SystemEvents.SessionSwitch = new SessionSwitchEventHandler(OnSessionSwitch);
將所有相關函式隱藏在一個單獨的類中,該類不是直接從視窗本身參考的。
當這條線被注釋掉時,一切正常,在視窗暫停/恢復之后,它在隱藏視窗中并附加到隱藏視窗中,整個代碼中不會發生 UI 更改(因此隱藏視窗繼續完全正常作業,因為它沒有與用戶界面)。
通過將此代碼提升到 APP 空間并在那里而不是在視窗中處理它,問題就消失了(盡管揭示了我現在必須修復的 Windows 恢復時未處理的其他問題)。
所以答案是,對于 WPF 應用程式,任何型別的 SystemEvents 的偵聽器都需要放置在 APP 代碼空間中,而不是在視窗中。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/335571.html
