WPF:自动执行"机器人"程序若干注意事项
企业应用中,经常会遇到一些需要定时自动执行的程序来完成某些功能,比如:自动定时从第三方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();//第二个实例试图“启动”时,自动把已经运行的实例激活并显示
}
}
}
最后上图二张:
- 中国版Space X首台“民营火箭”发动机试车成功;贾跃亭甘薇在美欢度圣诞,证监局喊破嗓子他也不回家;谷歌AI新技能
- 我的写作工具链
- 微信小程序更新新能力:四大功能让小程序更火爆!一起来了解更新吧,快来学习吧
- CentOS下ssh免密码问题
- flask-script
- 仿照wtform自定义Form组件
- 神速!一域名注册不到两个月就被启用
- Django中的信号
- 惊爆!小程序连发7种新能力,开直播、连Wi-Fi、分包加载……你们想要的都来了!
- 浅淡python中with的用法,上下文管理器
- 国家档案局:“大数据”时代我国档案工作要加快转型升级
- 大庆的实体店为什么一定要做小程序?
- 类和对象的创建过程(元类,__new__,__init__,__call__)
- wtforms
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 解决django migrate报错ORA-02000: missing ALWAYS keyword
- PHP封装的数据库模型Model类完整示例【基于PDO】
- PHP的简单跳转提示的实现详解
- PHP多进程通信-消息队列使用
- PHP implode()函数用法讲解
- PHP精确到毫秒秒杀倒计时实例详解
- django 装饰器 检测登录状态操作
- 使用PyWeChatSpy自动回复微信拍一拍功能的实现代码
- Django Session和Cookie分别实现记住用户登录状态操作
- 使用PHPUnit进行单元测试并生成代码覆盖率报告的方法
- PHP封装XML和JSON格式数据接口操作示例
- 浅谈PHP进程管理
- php使用fullcalendar日历插件详解
- PHP htmlspecialchars()函数用法与实例讲解
- 浅谈PHP匿名函数和闭包