企業應用中,經常會遇到一些需要定時自動執行的程序來完成某些功能,比如:自動定時從第三方web service取回數據、定時對歷史數據進行清理、定時向ftp上傳業務數據...
這類程序,我習慣稱為“機器人”程序,就象機器一樣機械、高效、重復的執行某些任務。通常部署上線后,都是放在服務器上一直開著,不允許輕易被關閉,而且最好要有一個界面,隨時可以手動方便控制狀態或查看運行情況,一旦發生異常情況,能及時通知管理員(Email或短信之類)
如果是采用WPF技術開發,以下是幾個需要注意的地方:
1、無邊框窗體(防止用戶不小心點到 右上角的關閉按鈕)
<Window x:Class="WeatherSpider.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowStyle="None" ...>
將主窗體的WindowStyle設置成None即可
2、無邊框窗體的移動
去掉頂上的邊框后,通常為了美觀,我們需要自己在頂上放一個偽造的標題欄,類似下面這樣
<Border Grid.Row="0" MouseLeftButtonDown="TitleBarOnMouseLeftButtonDown" > <Grid Margin="5,5,5,0" > <Grid.ColumnDefinitions> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock Foreground="GreenYellow" FontSize="16" x:Name="tbTitle">全國機場天氣-采集機器人</TextBlock> <TextBlock Text="最小化" Grid.Column="1" Foreground="GreenYellow" FontSize="12" VerticalAlignment="Center" TextAlignment="Right" x:Name="btnMin" Cursor="Hand" MouseLeftButtonDown="btnMin_MouseLeftButtonDown"></TextBlock> </Grid> </Border>
為了實現鼠標拖動標題欄時,窗體也能跟著拖動,需要在標題欄的對象上增加MouseLeftButtonDown事件處理(即:上面代碼Border上的MouseLeftButtonDown="TitleBarOnMouseLeftButtonDown" )
private void TitleBarOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { base.DragMove(); }
哦,原來 so easy !
3.最小化到系統托盤
Winform中的NotifyIcon控件在WPF中仍然可以繼續使用
先 using System.Windows.Forms; 添加Windows.Forms命名空間的引用
再聲明一個窗體級的變量
private readonly NotifyIcon notifyIcon;
最后在主窗體的構架函數中,加入下列這一段
notifyIcon = new NotifyIcon(); notifyIcon.BalloonTipText = Properties.Resources.AppTitle + " 正在運行!"; notifyIcon.Text = Properties.Resources.AppTitle;//指定托盤提示文字為資源中的AppTitle字符串 notifyIcon.Icon = Properties.Resources.App;//指定托盤圖標為資源中的"App"圖標 notifyIcon.Visible = false; notifyIcon.MouseClick += notifyIcon_MouseClick; //托盤右鍵菜單 MenuItem itemShowMainForm = new MenuItem("顯示主界面"); itemShowMainForm.Click += ShowMainWindow; MenuItem itemExit = new MenuItem("退出"); itemExit.Click += ExitApplication; MenuItem[] menuItems = new[] { itemShowMainForm, itemExit }; notifyIcon.ContextMenu = new ContextMenu(menuItems);
notifyIcon_MouseClick事件代碼如下:
public void Show() { Visibility = Visibility.Visible; Activate(); notifyIcon.Visible = false; } /// <summary> /// 托盤圖標鼠標點擊處理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void notifyIcon_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (Visibility == Visibility.Visible) { Visibility = Visibility.Hidden; notifyIcon.Visible = true; } else { Show(); } } } //顯示主界面 void ShowMainWindow(object sender, EventArgs e) { Show(); }
在上面提到的第2點中,可能已經有朋友注意到了“最小化”的文本上,已經加了 MouseLeftButtonDown="btnMin_MouseLeftButtonDown"事件處理,即點擊“最小化”這幾個字,可以縮小到托盤區,代碼如下:
private void btnMin_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Visibility = Visibility.Hidden;//隱藏主窗體 notifyIcon.Visible = true;//顯示托盤圖標 notifyIcon.ShowBalloonTip(1000);//顯示托盤圖標上的氣泡提示1秒鐘 }
4.程序退出時,主動提醒
雖然做了無邊框窗體的處理,但是如果用戶意外按了Alt+F4,甚至誤操作注銷或重啟Windows,程序還是會直接退出的,最好能給個提示,這樣管理員看到提示后,有機會取消誤操作
先給主窗體增加Closing事件處理,主窗體構造函數中,加入下面這一行
Closing += Window_Closing;
Window_Closing事件如下:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (MessageBox.Show("確定要退出[" + Properties.Resources.AppTitle + "]嗎?", Properties.Resources.AppTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes) { this.Closing -= Window_Closing;//注意:這里要注銷事件監聽,否則會連續彈出二次提示框才能退出 notifyIcon.Visible = false; e.Cancel = false; } else { e.Cancel = true; } }
經過上述處理后,用戶按Alt+F4時,就會提示是否退出。但這樣還不夠,如果Windows注銷時,仍然會直接退出
這就需要 using Microsoft.Win32;使用Win32命名空間下的某些功能了,主窗體構造函數中,增加:
//捕獲關機事件 SystemEvents.SessionEnding += SystemEvents_SessionEnding;
處理代碼如下:
void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) { if (MessageBox.Show("[" + Properties.Resources.AppTitle + "]正在運行中,確定要退出嗎?", Properties.Resources.AppTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes) { e.Cancel = false; } else { e.Cancel = true; } }
同時在剛才的Window_Closing中,增加一行代碼:(見下面的注釋行)
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (MessageBox.Show("確定要退出[" + Properties.Resources.AppTitle + "]嗎?", Properties.Resources.AppTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes) { SystemEvents.SessionEnding -= SystemEvents_SessionEnding; //取消關機事件監聽 this.Closing -= Window_Closing; notifyIcon.Visible = false; e.Cancel = false; } else { e.Cancel = true; } }
5.單實例運行
Winform中要實現單實例運行,非常容易(見 利用c#制作托盤程序,并禁止多個應用實例運行),但是WPF中就有點麻煩,網上搜索了一下,有朋友已經解決了這個問題
引用using Microsoft.VisualBasic.ApplicationServices; (注:必須先添加對Microsoft.VisualBasic的程序集引用)
然后把App.xaml編譯屬性改成Page,同時修改App.xaml.cs代碼如下:
using System.Windows; using System.Diagnostics; using System; using WeatherSpider.Helper; namespace WeatherSpider { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { /// <summary> /// Application Entry Point. /// </summary> [STAThread] [DebuggerNonUserCode] public static void Main(string[] a) { SingleApp app = new SingleApp();//SingleApp類后面馬上會提到 app.Run(a); } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); MainWindow w = new MainWindow(); w.Show();//即調用主窗體中的Show方法,顯示主窗體 } public void Activate() { (MainWindow as MainWindow).Show(); } } }
再創建一個SingleApp類
using Microsoft.VisualBasic.ApplicationServices; namespace WeatherSpider.Helper { public class SingleApp : WindowsFormsApplicationBase { App a; public SingleApp() { this.IsSingleInstance = true; } protected override bool OnStartup(StartupEventArgs eventArgs) { a = new App(); a.Run(); return false; } protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs) { base.OnStartupNextInstance(eventArgs); a.Activate();//第二個實例試圖“啟動”時,自動把已經運行的實例激活并顯示 } } }
最后上圖二張:
文章列表