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();//第二个实例试图“启动”时,自动把已经运行的实例激活并显示
        }
    }

}

最后上图二张:

时间: 2024-10-31 07:39:56

WPF:自动执行"机器人"程序若干注意事项的相关文章

如何在服务器空间中每隔一段时间自动执行一个程序,现在有的条件是服务器空间,没有服务器内部的权限,不能通过iis,求大神指导。

问题描述 如何在服务器空间中每隔一段时间自动执行一个程序,现在有的条件是服务器空间,没有服务器内部的权限,不能通过iis,求大神指导.详细说下现在要实现的功能,是这样,我们公司是做房产的,中介会上传一些房源图片,而现在这些图都是通过人工下载.要实现的功能是,自动下载这些图,并且转存到另一个空间中.与此同时,因为中介上传的时间不能确定,所以该程序,每隔两个小时运行一次.这个该怎么实现,求思路!!! 解决方案 解决方案二:你要下载到哪里,就应该在哪里执行你的程序啊你扔服务器上执行,图本来不就是在服务

Windows中使用计划任务自动执行PHP程序实例_php实例

所谓任务计划就是由计算机自动调用用户事先设置好的应用程序,从而达到简化用户操作的目的.利用Windows 2000的任务计划程序(相当与*NIX下的cron程序,这里不再对其详述),我们可以安排任何脚本.程序或文档在最恰当的时候运行,从而满足自己的需要.下面以Windows 2000为例. 具体来说,我们若需利用任务计划程序自动运行则应执行如下步骤: 单击"开始"按钮,然后依次选择"程序"→"附件"→"系统工具"→"

我有个java连接网络的程序,想要在windows登陆前执行这个程序,请教该如何做

问题描述 我有个java连接网络的程序,想要在windows开机以后,用户登陆前执行这个程序,请教该如何做 问题补充:ZavaKid 写道 解决方案 楼主可以参考这个,很可能对楼主有用: http://www.cnblogs.com/codeyu/archive/2009/09/03/1559736.html解决方案二:首先windows登陆前网卡不一定初始化好了,这时候连接网络有可能是不通的.可以考虑驱动的形式解决方案三:引用恩,就是我自己写个java程序封装成了exe可执行的程序,这个程序是

用php程序作为linux自动执行脚本

用PHP作一社区, 在写到计算最高上线人数时, 成功调试出把PHP作为 shell script直接在服务器上运行. 在作社区时, 时常需要统计上线人数等数据. 一般做法是, 把这段代码放在用户login或者某一个页面中, 以便在用户登录或访问到该时,触发运行该代码. 这样一来, 会造成一个问题, 如果该代码教复杂, 明显减慢该页的正常调用速度.利用PHP 的这种特性,加上Linux的crontab指令,即可自动定时执行某一个php文件(统计在线人数等). 具体方法: 在安装PHP的时候,会产生

Asp.Net(C#)自动执行计划任务的程序实例分析分享

 这篇文章主要介绍了Asp.Net(C#)自动执行计划任务的程序实例分析,有需要的朋友可以参考一下 在业务复杂的应用程序中,有时候会要求一个或者多个任务在一定的时间或者一定的时间间隔内计划进行,比如定时备份或同步数据库,定时发送电子邮件等,我们称之为计划任务.实现计划任务的方法也有很多,可以采用SQLAgent执行存储过程来实现,也可以采用Windows任务调度程序来实现,也可以使用Windows服务来完成我们的计划任务,这些方法都是很好的解决方案.但是,对于Web应用程序来说,这些方法实现起来

如何用程序自动执行批处理文件?

问题描述 如何用程序自动执行批处理文件? 最经工作中,反复需要在dos命令行界面下,输入某些批处理命令,现在想写一个程序代替这部分 手动操作,想请教有没有让程序自动调用执行批处理文件的API函数,非常感谢. 解决方案 可以先把命令写在 bat 文件,然后设置 windows 计划任务 解决方案二: 直接写个C程序,在C程序中调用System(str)命令,str为DOS命令字符串 解决方案三: 用ShellExecute

程序启动的时候自动执行一个bat命令的问题

问题描述 程序启动的时候自动执行一个bat命令的问题 能不能在程序执行以前先自动运行一个bat文件,怎么在程序打开以前得知程序运行的事件呢? 解决方案 将需要执行的程序添加到启动项中,http://www.jb51.net/os/19806.html 解决方案二: 是java语言吗? java语言可以在运行主程序的时候先执行bat,当bat执行成功后,在启动主要业务. 解决方案三: 需要写一个外壳程序,去run bat,然后 run 你的程序,比如实现程序自动更新! 解决方案四: 调用系统api

如何利用程序自动执行ftp上传下载操作?

问题描述 如何利用程序自动执行ftp上传下载操作? 最近工作中反复要用ftp工具,对某些固定的文件做下载,修改,再上传的操作,觉得很麻烦.想 编一个程序,可以自动执行ftp链接,对于某个设置好的路径和文件进行上传下载,想请教大家实现的方法,比如可以调用哪些API之类的?非常感谢 解决方案 可以使用perl,python等语言完成. python可以使用ftplib. import ftplib session = ftplib.FTP('xxx.xxx.xxx.xxx','username','

我的IE网页调用cgi程序时,浏览器却自动跳出下载窗口。要怎么设置,或者修改程序让IE执行cgi程序?

问题描述 我的IE网页调用cgi程序时,浏览器却自动跳出下载窗口.而用google浏览器,就能正常执行cgi程序.请问要怎么设置,或者修改程序让IE执行cgi程序?求各种帮拖,大爱 解决方案 解决方案二:在IIS上运行CGI有十个简单的步骤:1)安装InternetServiceManager.2)从列表中选择WWWServive.3)选择Properties/ServiceProperties命令.4)单击Directories标签.5)单击Add按钮.6)指定自己的cgi-bin目录的完整路