MDI程序其中一个子窗体改变数据库,怎样让其他子窗体知道呢? RT;做进销存系统的时候,客户一般喜欢把库存子窗口和销售子窗口等等打开,这样最好能在销售窗口做销售时,库存窗口的数据及时变化。(PS:不止这两个窗口需要,类似的情况很多) 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 对于观察者模式,c#有一个比较简单的实现方案,把数据库更新的消息设计为c#的事件,这个事件放在MDI主窗口类中,凡是创建子窗口后,每个子窗口都响应这个事件,在这个事件中更新自己的界面,如果一个子窗口改变了数据,只需引发MDI主窗口的这个事件就可以, http://msdn.microsoft.com/zh-cn/library/system.componentmodel.inotifypropertychanged.aspxhttp://msdn.microsoft.com/zh-cn/library/system.collections.specialized.inotifycollectionchanged.aspx请尽量使用标准的、直接了当的接口规范。 看样子是单机版的,规模估计也不大,那你可以在每个窗体上,放一个时钟控件,每隔1秒钟访问一次数据库,这样呈现在你子窗体上的内容不就是最新的吗,数据都是同步的,不用担心效率,这个不会占系统多少资源的如果怕占用系统较多资源,可以改进,用一个公共变量bool CommonVar=false;当有数据改变时,设置CommonVar=true;定时检查CommonVar,以指令其他子窗体更新数据;这种也许要用时钟控件,但用一个就可以了建议不要追风那些时髦的技术,你描述的问题,本质上就是我上面说的。从根本解决问题,用软件工程那些本质的方法从根源上解决问题,新技术永远学不完,跟不上,越跟越傻瓜化,越有依赖,越不灵活 我推荐一下我项目实践中用的方法,我把它称为“另类观察者”模式。1. 首先定义接口:----------------------------------------------- /// <summary> /// 定义可以刷新的窗体或控件的接口 /// </summary> public interface ICanReLoad { /// <summary> /// 刷新的窗体或控件 /// </summary> void ReLoad(); } /// <summary> /// 可以跟踪别的对象的修改并触发默认行为 /// </summary> public interface ITracker : ICanReLoad { /// <summary> /// 被跟踪对象的字符串名称表,以逗号分隔 /// </summary> string TrackTypes { get; } /// <summary> /// 指示是否是在被跟踪对象改变时是否立即重新获取数据 /// 如果为否则等控件在被激活时才刷新。 /// </summary> bool FastReload { get; set; }2. 然后定义一个DataTracker用来协调各窗体的刷新:------------------------------------------------------ /// <summary> /// 跟踪系统中数据的改变,用于窗体之间的联动 /// </summary> public class DataTracker { static Dictionary<ITracker, bool> componetsToReload = new Dictionary<ITracker, bool>(); static Dictionary<ITracker, string> namesToReload = new Dictionary<ITracker, string>(); /// <summary> /// 源对象在修改时通知对应的跟踪它的对象 /// </summary> /// <param name="sourceTypes">用逗号隔开的数据类型名称列表</param> public static void Change(string sourceTypes) { foreach (ITracker tracker in componetsToReload.Keys.ToArray()) { foreach (string dataType in sourceTypes.Split(',')) { string[] tracktypes = tracker.TrackTypes.Split(','); if (tracktypes.Contains(dataType)) { componetsToReload[tracker] = true; if (tracker.FastReload || //下面判断是否接受通知者和通知者是在一个类中 new StackTrace().GetFrame(1).GetMethod().DeclaringType == tracker.GetType()) { ReLoad(tracker); } break; } } } foreach (INamedTracker tracker in namesToReload.Keys.ToArray()) { foreach (string dataType in sourceTypes.Split(',')) { string[] tracktypes = tracker.TrackTypes.Split(','); if (tracktypes.Contains(dataType)) { namesToReload[tracker] += "," + dataType; if (tracker.FastReload || //下面判断是否接受通知者和通知者是在一个类中 new StackTrace().GetFrame(1).GetMethod().DeclaringType == tracker.GetType()) { ReLoad(tracker, dataType); } } } } } /// <summary> /// 判断并刷新指定的跟踪对象 /// </summary> /// <param name="tracker"></param> public static void ReLoad(ITracker tracker) { if (componetsToReload[tracker]) { tracker.ReLoad(); componetsToReload[tracker] = false; } } /// <summary> /// 判断并刷新指定的跟踪对象 /// </summary> /// <param name="tracker"></param> /// <param name="typeName"></param> public static void ReLoad(INamedTracker tracker, string typeName) { List<string> trackTypes = tracker.TrackTypes.Split(',').ToList(); string reloadNames = namesToReload[tracker]; string pattern = "\\W*" + typeName + "\\W*"; if (trackTypes.Contains(typeName) && Regex.IsMatch(reloadNames, pattern)) { tracker.ReLoad(typeName); namesToReload[tracker] = Regex.Replace(reloadNames, pattern, ","); } } /// <summary> /// 注册跟踪对象 /// </summary> /// <param name="tracker"></param> public static void Register(ITracker tracker) { if (tracker is INamedTracker && !namesToReload.ContainsKey(tracker)) { namesToReload.Add(tracker, ""); } else if (!componetsToReload.ContainsKey(tracker)) { componetsToReload.Add(tracker, false); } } /// <summary> /// 反注册跟踪对象 /// </summary> /// <param name="tracker"></param> public static void UnRegister(ITracker tracker) { if (componetsToReload.ContainsKey(tracker)) { componetsToReload.Remove(tracker); } if (namesToReload.ContainsKey(tracker)) { namesToReload.Remove(tracker); } } }3. 设计一个基类窗体继承自ITracker接口,其他需要刷新的窗体继承自这个基窗体:------------------------------------------------------------------ public partial class FormBase : Form, ITracker { /// <summary> /// 指定窗体是否在收到数据改变通知时是否立即刷新 王家新 /// </summary> public bool FastReload { get; set; } //在此定义窗体数据刷新的方法 public virtual void ReLoad(){ } //需要监视刷新的数据源名称 public virtual string TrackTypes { get {return "数据源名称"; } /// <summary> /// 构造函数,用于初始化窗体对象 /// </summary> public FormBase() { // 初始化窗体控件 InitializeComponent(); //如果继承Iracker接口,则代替子类注册到DataTracker中 ITracker tracker = this as ITracker; if (tracker == null) return; DataTracker.Register(tracker); bool firstload = true; //在窗体被激活时,判断是否需要刷新数据 ((Form)tracker).Activated += (s, e) => { if (!firstload) { if (tracker is INamedTracker) { foreach (string dataType in tracker.TrackTypes.Split(',')) { DataTracker.ReLoad((INamedTracker)tracker, dataType); } } else { DataTracker.ReLoad(tracker); } } else { firstload = false; } }; ((Form)tracker).FormClosed += (s, e) => { DataTracker.UnRegister(tracker); }; }}4. 在发起数据更新通知的窗体内写以下方法:----------------------------------------------------------- //更新数据,然后: DataTracker.Change("数据源名称,它和接收更新的窗体中定义的同名");说明:当然这是一种单机模式的联动。如果要求分布式的联动,可以将DataTraker重构成用某个网络服务来执行Changed事件,这是不难的。我自以为这种方法比起传统观察者模式的好处是观察者和被观察者都不需要知道对方的存在。双方的协议也非常简单,只需要知道相同的数据源名称即可。而且名称是随便由你起的,用你女朋友的名字都可以。 Process 运行外部程序的问题 引用类型问题??? 办公信息平台 程序通过了 数据库却丝毫没有变化 ACCESS,请高手点拨下小弟,谢谢了。 急求javascript代码 如何用.net调用fortran写的dll 删除GridView内容时弹出询问框老是出错,什么原因,谢谢! wo kao!关于c#发邮件的小问题,送分! 设计问题,苦恼2天了。 急,求关于导出文件为其他格式 用 XmlSerializer的Serialize方法 序列化类 当类中包含 html代码时 经常但不是每次会发生XML标记溢出 这个泛型 List<T>的正确的写法是什么呢?
http://msdn.microsoft.com/zh-cn/library/system.componentmodel.inotifypropertychanged.aspx
http://msdn.microsoft.com/zh-cn/library/system.collections.specialized.inotifycollectionchanged.aspx请尽量使用标准的、直接了当的接口规范。
1. 首先定义接口:
-----------------------------------------------
/// <summary>
/// 定义可以刷新的窗体或控件的接口
/// </summary>
public interface ICanReLoad
{
/// <summary>
/// 刷新的窗体或控件
/// </summary>
void ReLoad();
} /// <summary>
/// 可以跟踪别的对象的修改并触发默认行为
/// </summary>
public interface ITracker : ICanReLoad
{
/// <summary>
/// 被跟踪对象的字符串名称表,以逗号分隔
/// </summary>
string TrackTypes { get; }
/// <summary>
/// 指示是否是在被跟踪对象改变时是否立即重新获取数据
/// 如果为否则等控件在被激活时才刷新。
/// </summary>
bool FastReload { get; set; }
2. 然后定义一个DataTracker用来协调各窗体的刷新:
------------------------------------------------------
/// <summary>
/// 跟踪系统中数据的改变,用于窗体之间的联动
/// </summary>
public class DataTracker
{
static Dictionary<ITracker, bool> componetsToReload = new Dictionary<ITracker, bool>();
static Dictionary<ITracker, string> namesToReload = new Dictionary<ITracker, string>(); /// <summary>
/// 源对象在修改时通知对应的跟踪它的对象
/// </summary>
/// <param name="sourceTypes">用逗号隔开的数据类型名称列表</param>
public static void Change(string sourceTypes)
{
foreach (ITracker tracker in componetsToReload.Keys.ToArray())
{
foreach (string dataType in sourceTypes.Split(','))
{
string[] tracktypes = tracker.TrackTypes.Split(',');
if (tracktypes.Contains(dataType))
{
componetsToReload[tracker] = true;
if (tracker.FastReload ||
//下面判断是否接受通知者和通知者是在一个类中
new StackTrace().GetFrame(1).GetMethod().DeclaringType == tracker.GetType())
{
ReLoad(tracker);
}
break;
}
}
}
foreach (INamedTracker tracker in namesToReload.Keys.ToArray())
{
foreach (string dataType in sourceTypes.Split(','))
{
string[] tracktypes = tracker.TrackTypes.Split(',');
if (tracktypes.Contains(dataType))
{
namesToReload[tracker] += "," + dataType; if (tracker.FastReload ||
//下面判断是否接受通知者和通知者是在一个类中
new StackTrace().GetFrame(1).GetMethod().DeclaringType == tracker.GetType())
{
ReLoad(tracker, dataType);
}
}
}
}
} /// <summary>
/// 判断并刷新指定的跟踪对象
/// </summary>
/// <param name="tracker"></param>
public static void ReLoad(ITracker tracker)
{
if (componetsToReload[tracker])
{
tracker.ReLoad();
componetsToReload[tracker] = false;
}
}
/// <summary>
/// 判断并刷新指定的跟踪对象
/// </summary>
/// <param name="tracker"></param>
/// <param name="typeName"></param>
public static void ReLoad(INamedTracker tracker, string typeName)
{
List<string> trackTypes = tracker.TrackTypes.Split(',').ToList();
string reloadNames = namesToReload[tracker];
string pattern = "\\W*" + typeName + "\\W*";
if (trackTypes.Contains(typeName) && Regex.IsMatch(reloadNames, pattern))
{
tracker.ReLoad(typeName);
namesToReload[tracker] = Regex.Replace(reloadNames, pattern, ",");
}
} /// <summary>
/// 注册跟踪对象
/// </summary>
/// <param name="tracker"></param>
public static void Register(ITracker tracker)
{
if (tracker is INamedTracker && !namesToReload.ContainsKey(tracker))
{
namesToReload.Add(tracker, "");
}
else if (!componetsToReload.ContainsKey(tracker))
{
componetsToReload.Add(tracker, false);
}
} /// <summary>
/// 反注册跟踪对象
/// </summary>
/// <param name="tracker"></param>
public static void UnRegister(ITracker tracker)
{
if (componetsToReload.ContainsKey(tracker))
{
componetsToReload.Remove(tracker);
}
if (namesToReload.ContainsKey(tracker))
{
namesToReload.Remove(tracker);
}
}
}3. 设计一个基类窗体继承自ITracker接口,其他需要刷新的窗体继承自这个基窗体:
------------------------------------------------------------------
public partial class FormBase : Form, ITracker
{
/// <summary>
/// 指定窗体是否在收到数据改变通知时是否立即刷新 王家新
/// </summary>
public bool FastReload { get; set; } //在此定义窗体数据刷新的方法
public virtual void ReLoad(){
}
//需要监视刷新的数据源名称
public virtual string TrackTypes { get {return "数据源名称"; } /// <summary>
/// 构造函数,用于初始化窗体对象
/// </summary>
public FormBase()
{
// 初始化窗体控件
InitializeComponent();
//如果继承Iracker接口,则代替子类注册到DataTracker中
ITracker tracker = this as ITracker;
if (tracker == null) return;
DataTracker.Register(tracker);
bool firstload = true; //在窗体被激活时,判断是否需要刷新数据
((Form)tracker).Activated += (s, e) =>
{
if (!firstload)
{
if (tracker is INamedTracker)
{
foreach (string dataType in tracker.TrackTypes.Split(','))
{
DataTracker.ReLoad((INamedTracker)tracker, dataType);
}
}
else
{
DataTracker.ReLoad(tracker);
}
}
else
{
firstload = false;
}
}; ((Form)tracker).FormClosed += (s, e) =>
{
DataTracker.UnRegister(tracker);
};
}
}4. 在发起数据更新通知的窗体内写以下方法:
-----------------------------------------------------------
//更新数据,然后:
DataTracker.Change("数据源名称,它和接收更新的窗体中定义的同名");说明:
当然这是一种单机模式的联动。如果要求分布式的联动,可以将DataTraker重构成用某个网络服务来执行Changed事件,这是不难的。我自以为这种方法比起传统观察者模式的好处是观察者和被观察者都不需要知道对方的存在。双方的协议也非常简单,只需要知道相同的数据源名称即可。而且名称是随便由你起的,用你女朋友的名字都可以。