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

    最后上图二张:

  • 相关阅读:
    小tips: HTTP 请求过程示例图及名称解释
    小tips:使用vuecli2脚手架配置vant自定义主题
    axios使用备忘录
    知乎问题:为什么很多web项目还是使用 px,而不是 rem?
    小tips:HTML5的ruby标签实现给汉字加拼音、details标签实现折叠面板、原生进度条progress和度量meter
    ES6之常用开发知识点:入门(一)
    GitBook相关使用以及配置笔记
    小tips:使用vue-cli脚手架搭建项目,关于eslint语法检测配置
    小tips:JS/CSS实现字符串单词首字母大写
    vue动态子组件的实现方式
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/3329548.html
Copyright © 2011-2022 走看看