关键词:委托 线程 异步操作 大数据存储过程分页 实时刷新界面数据 声音报警 任务栏提示 动态任务栏图标切换
需求:启动监控程序后,每隔10秒(可配置多少秒)从后台数据库查询一次,
查询条件(sql语句可配置),然后返回结果显示和进行判断。
即时插入的数据:(第一列)
判断的条件(尽量也可以灵活配置):再返回的数据里面,空号2大于0的多少倍,
就要间隔1秒再取一次,如果发现还是2的倍数大,那就就报警,
如果0的倍数大于2很多,就每隔10秒(可配置)查询一次
就要间隔1秒再取一次,如果发现还是2的倍数大,那就就报警,
如果0的倍数大于2很多,就每隔10秒(可配置)查询一次

程序界面:

配置:


实现代码:
using
System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace SMMMMonitoring
{
public partial class Form1 : Form
{
private static string strConn = ConfigurationManager.AppSettings[ " conStr " ]; ;
private static string strProcedure = ConfigurationManager.AppSettings[ " procName " ];
private static int pageindex = Convert.ToInt32( ConfigurationManager.AppSettings[ " pageindex " ]); // 刷新第几页
private static int multiples = Convert.ToInt32(ConfigurationManager.AppSettings[ " Multiple " ]); // 报警倍数
StoreProcedure sa = new StoreProcedure(strProcedure, strConn);
public Form1()
{
InitializeComponent();
BindDGV();
}
private void BindDataWithPage( int Index)
{
winFormPager1.PageIndex = Index;
string table = ConfigurationManager.AppSettings[ " table " ];
string field = ConfigurationManager.AppSettings[ " Field " ];
string orderby = ConfigurationManager.AppSettings[ " orderby " ];
string where = ConfigurationManager.AppSettings[ " where " ];
DataTable dt = sa.ExecuteDataTable(table, field, orderby, 10 , Index, 0 , 0 , where );
dataGridView1.DataSource = dt;
winFormPager1.RecordCount = Convert.ToInt32(sa.ExecuteDataTable(table, field, orderby, 10 , Index, 1 , 0 , where ).Rows[ 0 ][ 0 ]);
}
private void winFormPager1_PageIndexChanged( object sender, EventArgs e)
{
BindDataWithPage(winFormPager1.PageIndex);
}
// 线程间操作无效: 从不是创建控件“dataGridView1”的线程访问它。
// 调用 : BindDGV(9,BindDataWithPage); 传递参数
/* private delegate void BindDGVDelegate(int n);
private void BindDGV(int n, BindDGVDelegate myDelegate)
{
if (this.InvokeRequired)
{
this.Invoke(myDelegate, n);//同步
}
else
{
myDelegate(n);
}
}
*/
/*
* Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。
* 也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,
* 千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,相对于invokeThread线程同步的,因为这些是依然阻塞UI线程的,造成界面的假死。
* 用Thread来调用BeginInvoke和Invoke
* 执行顺序: A--- BC (B和C同时执行,B执行在线程UI上,C执行在线程beginInvokeThread上) --DE
*/
private Thread beginInvokeThread;
private delegate void beginInvokeDelegate();
private void StartMethod()
{
// C代码段...... 耗费长时间的操作
dataGridView1.BeginInvoke( new beginInvokeDelegate(beginInvokeMethod));
// D代码段...... 删掉
}
private void beginInvokeMethod()
{
// E代码段 更新界面的方法。
BindDataWithPage(pageindex);
}
private void BindDGV()
{
// A代码段.......
beginInvokeThread = new Thread( new ThreadStart(StartMethod));
beginInvokeThread.Start();
// B代码段......
}
/// <summary>
/// 大于多少倍就报警
/// </summary>
/// <param name="dgv"></param>
/// <param name="n"></param>
public bool Alarm(DataGridView dgv, int n)
{
int Space = 0 ; int Success = 0 ;
Dictionary < string , int > dict = DictionaryColumns(dgv);
foreach (KeyValuePair < string , int > kvp in dict)
{
if (kvp.Key == " 2 " )
{
Space = kvp.Value;
}
if (kvp.Key == " 0 " )
{
Success = kvp.Value;
}
}
if (Space >= Success * n)
{
// 报警
// MessageBox.Show("报警!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return true ;
}
else
{
// MessageBox.Show("bu报警!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return false ;
}
}
public Dictionary < string , int > DictionaryColumns(DataGridView dgv)
{
Dictionary < string , int > dct = new Dictionary < string , int > ();
string str = "" ;
for ( int i = 0 ; i < dgv.Rows.Count - 1 ; i ++ )
{
str = Convert.ToString(dgv.Rows[i].Cells[ 4 ].Value);
if (dct.ContainsKey(str)) // 如果字典中已存在这个键,给这个键值加1
{
dct[str] ++ ;
}
else
{
dct.Add(str, 1 ); // 字典中不存在这个键,加入这个键
}
}
return dct;
}
private DateTime lastRefresh = new DateTime( 0 );
private int refreshInterval = 10 ;
private string lastTitle = string .Empty;
private string lastMessage = string .Empty;
/// <summary>
/// 更新数据
/// </summary>
public void RefreshAllMessages()
{
BindDGV();
}
/// <summary>
/// 更新待办事情
/// </summary>
private void RefreshAsync()
{
// 更新最后执行时间
UpdateLastRefreshTime();
// 建立查詢中状态
BuildMessagesQuerying();
// 异步更新 为了不卡住
RefreshAllMessagesDelegate refresher = new RefreshAllMessagesDelegate( this .RefreshAllMessages);
IAsyncResult result = refresher.BeginInvoke( new AsyncCallback(RefreshAllCallback), refresher);
// 等待更新完成
while ( ! result.IsCompleted)
{
Application.DoEvents(); // 在不加的时候,因为优先级的问题,程序会执行主进程的代码,再执行Tick的代码,而加了以后就可以同步执行。
}
// 更新完成,警报并显示待办件数
BuildMessagesResult();
}
/// <summary>
/// 更新最后执行时间
/// </summary>
private void UpdateLastRefreshTime()
{
this .lastRefresh = DateTime.Now;
// 時間为n秒之間
refreshInterval = int .Parse( this .txtRefreshInterval.Text);
lblNextRefreshTime.Text = String.Format( " {0:yyyy/MM/dd HH:mm:ss} " , lastRefresh.AddSeconds(refreshInterval)); ;
}
/// <summary>
/// 建立查詢中状态
/// </summary>
private void BuildMessagesQuerying()
{
lastMessage = " 正在检查中..... " ;
// linkTodo.Enabled = false;
// linkTodo.Text = lastMessage;
btnRefresh.Enabled = false ;
mnuExit.Enabled = false ;
txtRefreshInterval.Enabled = false ;
// 將icon改成更新中,改成Refresh.ico
ChangeNotifyIcon(NotifyIconType.Actions);
}
/// <summary>
/// 依最后结果设定页面
/// </summary>
private void BuildMessagesResult()
{
bool IsShowNotify = Alarm(dataGridView1, multiples);
// 如果有待办件数的话,就改成报警,否則就使用原始
ChangeNotifyIcon(IsShowNotify ? NotifyIconType.Phonographe : NotifyIconType.rabbit);
notifyIcon1.Text = lastMessage;
mnuExit.Enabled = true ;
txtRefreshInterval.Enabled = true ;
if (IsShowNotify && this .WindowState == FormWindowState.Minimized || IsShowNotify)
{
// 要显示通知信息
PlaySound( " msg.wav " , true , true , false ); // 报警
lastMessage = " 报警…… " ;
notifyIcon1.ShowBalloonTip( 3000 , this .Text,lastMessage, ToolTipIcon.Info);
}
}
// 回异步调
public delegate void RefreshAllMessagesDelegate();
static void RefreshAllCallback(IAsyncResult result)
{
// 委托
Form1.RefreshAllMessagesDelegate dlgt = (Form1.RefreshAllMessagesDelegate)result.AsyncState;
// 调用 EndInvoke 返回结果
dlgt.EndInvoke(result);
}
/// <summary>
/// 实时更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void refreshTimer_Tick( object sender, EventArgs e)
{
TimeSpan diff = DateTime.Now - this .lastRefresh;
if (diff > new TimeSpan( 0 , 0 , refreshInterval))
{
// 更新
StopSound();
RefreshAsync();
}
}
/// <summary>
/// 更换NotifyIcon
/// </summary>
private void ChangeNotifyIcon(NotifyIconType notiType)
{
const string imageFolderName = " Resources " ;
try
{
// 內嵌资源名称 namespace.foldername.filename 而且大小写要相同哦 图片-右键属性-嵌入的资源
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
string fileName = asm.GetName().Name + " . " + imageFolderName + " . " + notiType.ToString() + " .ico " ;
Stream iconStream = asm.GetManifestResourceStream(fileName);
Icon newIcon = new Icon(iconStream);
this .notifyIcon1.Icon = newIcon;
iconStream.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public enum NotifyIconType
{
rabbit,
Actions,
Phonographe
}
#region 报警声音
/* 当例如监测xx大于xx
* 通过PlaySound(”msg.wav”, true, true, false)方法实现连续报警
* 还可以同时弹出窗口提醒之类的信息.
* 关闭报警通过方法StopSound()实现就可以了. */
[DllImport( " winmm.dll " , EntryPoint = " PlaySound " )]
private static extern bool Win32_PlaySound( string pszSound, IntPtr hmod, uint fdwSound);
/// <summary>
/// 播放一个wav音频文件
/// </summary>
/// <param name="path"></param>
/// <param name="asynchronous"></param>
/// <param name="loop"></param>
/// <param name="doNotStopPlay"></param>
public static void PlaySound( string path, bool asynchronous, bool loop, bool doNotStopPlay)
{
Win32_PlaySound(path, IntPtr.Zero, ( uint )((asynchronous ?
PlaySoundMessage.SND_ASYNC : PlaySoundMessage.SND_SYNC) | (loop ?
PlaySoundMessage.SND_LOOP : 0 ) | (doNotStopPlay ?
PlaySoundMessage.SND_NOSTOP : 0 ) | PlaySoundMessage.SND_FILENAME));
}
/// <summary>
/// 停止播放
/// </summary>
public static void StopSound()
{
Win32_PlaySound( null , IntPtr.Zero, 0 );
}
[Flags()]
internal enum PlaySoundMessage
{
SND_SYNC = 0x0000 ,
SND_ASYNC = 0x0001 ,
SND_LOOP = 0x0008 ,
SND_NOSTOP = 0x0010 ,
SND_FILENAME = 0x00020000
}
#endregion
#region 窗体事件
private void notifyIcon1_DoubleClick( object sender, EventArgs e)
{
ShowForm();
}
private void ShowForm()
{
if ( this .WindowState == FormWindowState.Minimized)
{
this .Show();
this .WindowState = FormWindowState.Normal;
}
this .Activate();
this .Focus();
}
private void mnuExit_Click( object sender, EventArgs e)
{
this .WindowState = FormWindowState.Minimized;
Close();
}
/// <summary>
/// 視窗关闭,程序并不关闭,而是最小化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosing( object sender, FormClosingEventArgs e)
{
if ( this .WindowState != FormWindowState.Minimized)
{
e.Cancel = true ;
this .WindowState = FormWindowState.Minimized;
notifyIcon1.Tag = string .Empty;
notifyIcon1.ShowBalloonTip( 3000 , this .Text, " 程序未结束,要结束请在图示上按右键,选取结束功能! " , ToolTipIcon.Info);
}
}
/// <summary>
/// 在NotifyBallonTip上按下Click,就將Form开启出來
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void notifyIcon1_BalloonTipClicked( object sender, EventArgs e)
{
ShowForm();
}
#endregion
private void btnSetting_Click( object sender, EventArgs e)
{
Setting st = new Setting();
st.Show();
}
}
}
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace SMMMMonitoring
{
public partial class Form1 : Form
{
private static string strConn = ConfigurationManager.AppSettings[ " conStr " ]; ;
private static string strProcedure = ConfigurationManager.AppSettings[ " procName " ];
private static int pageindex = Convert.ToInt32( ConfigurationManager.AppSettings[ " pageindex " ]); // 刷新第几页
private static int multiples = Convert.ToInt32(ConfigurationManager.AppSettings[ " Multiple " ]); // 报警倍数
StoreProcedure sa = new StoreProcedure(strProcedure, strConn);
public Form1()
{
InitializeComponent();
BindDGV();
}
private void BindDataWithPage( int Index)
{
winFormPager1.PageIndex = Index;
string table = ConfigurationManager.AppSettings[ " table " ];
string field = ConfigurationManager.AppSettings[ " Field " ];
string orderby = ConfigurationManager.AppSettings[ " orderby " ];
string where = ConfigurationManager.AppSettings[ " where " ];
DataTable dt = sa.ExecuteDataTable(table, field, orderby, 10 , Index, 0 , 0 , where );
dataGridView1.DataSource = dt;
winFormPager1.RecordCount = Convert.ToInt32(sa.ExecuteDataTable(table, field, orderby, 10 , Index, 1 , 0 , where ).Rows[ 0 ][ 0 ]);
}
private void winFormPager1_PageIndexChanged( object sender, EventArgs e)
{
BindDataWithPage(winFormPager1.PageIndex);
}
// 线程间操作无效: 从不是创建控件“dataGridView1”的线程访问它。
// 调用 : BindDGV(9,BindDataWithPage); 传递参数
/* private delegate void BindDGVDelegate(int n);
private void BindDGV(int n, BindDGVDelegate myDelegate)
{
if (this.InvokeRequired)
{
this.Invoke(myDelegate, n);//同步
}
else
{
myDelegate(n);
}
}
*/
/*
* Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。
* 也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,
* 千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,相对于invokeThread线程同步的,因为这些是依然阻塞UI线程的,造成界面的假死。
* 用Thread来调用BeginInvoke和Invoke
* 执行顺序: A--- BC (B和C同时执行,B执行在线程UI上,C执行在线程beginInvokeThread上) --DE
*/
private Thread beginInvokeThread;
private delegate void beginInvokeDelegate();
private void StartMethod()
{
// C代码段...... 耗费长时间的操作
dataGridView1.BeginInvoke( new beginInvokeDelegate(beginInvokeMethod));
// D代码段...... 删掉
}
private void beginInvokeMethod()
{
// E代码段 更新界面的方法。
BindDataWithPage(pageindex);
}
private void BindDGV()
{
// A代码段.......
beginInvokeThread = new Thread( new ThreadStart(StartMethod));
beginInvokeThread.Start();
// B代码段......
}
/// <summary>
/// 大于多少倍就报警
/// </summary>
/// <param name="dgv"></param>
/// <param name="n"></param>
public bool Alarm(DataGridView dgv, int n)
{
int Space = 0 ; int Success = 0 ;
Dictionary < string , int > dict = DictionaryColumns(dgv);
foreach (KeyValuePair < string , int > kvp in dict)
{
if (kvp.Key == " 2 " )
{
Space = kvp.Value;
}
if (kvp.Key == " 0 " )
{
Success = kvp.Value;
}
}
if (Space >= Success * n)
{
// 报警
// MessageBox.Show("报警!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return true ;
}
else
{
// MessageBox.Show("bu报警!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return false ;
}
}
public Dictionary < string , int > DictionaryColumns(DataGridView dgv)
{
Dictionary < string , int > dct = new Dictionary < string , int > ();
string str = "" ;
for ( int i = 0 ; i < dgv.Rows.Count - 1 ; i ++ )
{
str = Convert.ToString(dgv.Rows[i].Cells[ 4 ].Value);
if (dct.ContainsKey(str)) // 如果字典中已存在这个键,给这个键值加1
{
dct[str] ++ ;
}
else
{
dct.Add(str, 1 ); // 字典中不存在这个键,加入这个键
}
}
return dct;
}
private DateTime lastRefresh = new DateTime( 0 );
private int refreshInterval = 10 ;
private string lastTitle = string .Empty;
private string lastMessage = string .Empty;
/// <summary>
/// 更新数据
/// </summary>
public void RefreshAllMessages()
{
BindDGV();
}
/// <summary>
/// 更新待办事情
/// </summary>
private void RefreshAsync()
{
// 更新最后执行时间
UpdateLastRefreshTime();
// 建立查詢中状态
BuildMessagesQuerying();
// 异步更新 为了不卡住
RefreshAllMessagesDelegate refresher = new RefreshAllMessagesDelegate( this .RefreshAllMessages);
IAsyncResult result = refresher.BeginInvoke( new AsyncCallback(RefreshAllCallback), refresher);
// 等待更新完成
while ( ! result.IsCompleted)
{
Application.DoEvents(); // 在不加的时候,因为优先级的问题,程序会执行主进程的代码,再执行Tick的代码,而加了以后就可以同步执行。
}
// 更新完成,警报并显示待办件数
BuildMessagesResult();
}
/// <summary>
/// 更新最后执行时间
/// </summary>
private void UpdateLastRefreshTime()
{
this .lastRefresh = DateTime.Now;
// 時間为n秒之間
refreshInterval = int .Parse( this .txtRefreshInterval.Text);
lblNextRefreshTime.Text = String.Format( " {0:yyyy/MM/dd HH:mm:ss} " , lastRefresh.AddSeconds(refreshInterval)); ;
}
/// <summary>
/// 建立查詢中状态
/// </summary>
private void BuildMessagesQuerying()
{
lastMessage = " 正在检查中..... " ;
// linkTodo.Enabled = false;
// linkTodo.Text = lastMessage;
btnRefresh.Enabled = false ;
mnuExit.Enabled = false ;
txtRefreshInterval.Enabled = false ;
// 將icon改成更新中,改成Refresh.ico
ChangeNotifyIcon(NotifyIconType.Actions);
}
/// <summary>
/// 依最后结果设定页面
/// </summary>
private void BuildMessagesResult()
{
bool IsShowNotify = Alarm(dataGridView1, multiples);
// 如果有待办件数的话,就改成报警,否則就使用原始
ChangeNotifyIcon(IsShowNotify ? NotifyIconType.Phonographe : NotifyIconType.rabbit);
notifyIcon1.Text = lastMessage;
mnuExit.Enabled = true ;
txtRefreshInterval.Enabled = true ;
if (IsShowNotify && this .WindowState == FormWindowState.Minimized || IsShowNotify)
{
// 要显示通知信息
PlaySound( " msg.wav " , true , true , false ); // 报警
lastMessage = " 报警…… " ;
notifyIcon1.ShowBalloonTip( 3000 , this .Text,lastMessage, ToolTipIcon.Info);
}
}
// 回异步调
public delegate void RefreshAllMessagesDelegate();
static void RefreshAllCallback(IAsyncResult result)
{
// 委托
Form1.RefreshAllMessagesDelegate dlgt = (Form1.RefreshAllMessagesDelegate)result.AsyncState;
// 调用 EndInvoke 返回结果
dlgt.EndInvoke(result);
}
/// <summary>
/// 实时更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void refreshTimer_Tick( object sender, EventArgs e)
{
TimeSpan diff = DateTime.Now - this .lastRefresh;
if (diff > new TimeSpan( 0 , 0 , refreshInterval))
{
// 更新
StopSound();
RefreshAsync();
}
}
/// <summary>
/// 更换NotifyIcon
/// </summary>
private void ChangeNotifyIcon(NotifyIconType notiType)
{
const string imageFolderName = " Resources " ;
try
{
// 內嵌资源名称 namespace.foldername.filename 而且大小写要相同哦 图片-右键属性-嵌入的资源
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
string fileName = asm.GetName().Name + " . " + imageFolderName + " . " + notiType.ToString() + " .ico " ;
Stream iconStream = asm.GetManifestResourceStream(fileName);
Icon newIcon = new Icon(iconStream);
this .notifyIcon1.Icon = newIcon;
iconStream.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public enum NotifyIconType
{
rabbit,
Actions,
Phonographe
}
#region 报警声音
/* 当例如监测xx大于xx
* 通过PlaySound(”msg.wav”, true, true, false)方法实现连续报警
* 还可以同时弹出窗口提醒之类的信息.
* 关闭报警通过方法StopSound()实现就可以了. */
[DllImport( " winmm.dll " , EntryPoint = " PlaySound " )]
private static extern bool Win32_PlaySound( string pszSound, IntPtr hmod, uint fdwSound);
/// <summary>
/// 播放一个wav音频文件
/// </summary>
/// <param name="path"></param>
/// <param name="asynchronous"></param>
/// <param name="loop"></param>
/// <param name="doNotStopPlay"></param>
public static void PlaySound( string path, bool asynchronous, bool loop, bool doNotStopPlay)
{
Win32_PlaySound(path, IntPtr.Zero, ( uint )((asynchronous ?
PlaySoundMessage.SND_ASYNC : PlaySoundMessage.SND_SYNC) | (loop ?
PlaySoundMessage.SND_LOOP : 0 ) | (doNotStopPlay ?
PlaySoundMessage.SND_NOSTOP : 0 ) | PlaySoundMessage.SND_FILENAME));
}
/// <summary>
/// 停止播放
/// </summary>
public static void StopSound()
{
Win32_PlaySound( null , IntPtr.Zero, 0 );
}
[Flags()]
internal enum PlaySoundMessage
{
SND_SYNC = 0x0000 ,
SND_ASYNC = 0x0001 ,
SND_LOOP = 0x0008 ,
SND_NOSTOP = 0x0010 ,
SND_FILENAME = 0x00020000
}
#endregion
#region 窗体事件
private void notifyIcon1_DoubleClick( object sender, EventArgs e)
{
ShowForm();
}
private void ShowForm()
{
if ( this .WindowState == FormWindowState.Minimized)
{
this .Show();
this .WindowState = FormWindowState.Normal;
}
this .Activate();
this .Focus();
}
private void mnuExit_Click( object sender, EventArgs e)
{
this .WindowState = FormWindowState.Minimized;
Close();
}
/// <summary>
/// 視窗关闭,程序并不关闭,而是最小化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosing( object sender, FormClosingEventArgs e)
{
if ( this .WindowState != FormWindowState.Minimized)
{
e.Cancel = true ;
this .WindowState = FormWindowState.Minimized;
notifyIcon1.Tag = string .Empty;
notifyIcon1.ShowBalloonTip( 3000 , this .Text, " 程序未结束,要结束请在图示上按右键,选取结束功能! " , ToolTipIcon.Info);
}
}
/// <summary>
/// 在NotifyBallonTip上按下Click,就將Form开启出來
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void notifyIcon1_BalloonTipClicked( object sender, EventArgs e)
{
ShowForm();
}
#endregion
private void btnSetting_Click( object sender, EventArgs e)
{
Setting st = new Setting();
st.Show();
}
}
}