MDI程序其中一个子窗体改变数据库,怎样让其他子窗体知道呢?

leafly0719 2012-06-21 12:42:01
RT;
做进销存系统的时候,客户一般喜欢把库存子窗口和销售子窗口等等打开,这样最好能在销售窗口做销售时,库存窗口的数据及时变化。(PS:不止这两个窗口需要,类似的情况很多)
...全文
131 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
cnwin 2012-06-21
  • 打赏
  • 举报
回复
单机的话好弄,使用数据绑定都会自动更新。当然还可以考虑事件方式或是观察者模式。
bwangel 2012-06-21
  • 打赏
  • 举报
回复
我推荐一下我项目实践中用的方法,我把它称为“另类观察者”模式。
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事件,这是不难的。

我自以为这种方法比起传统观察者模式的好处是观察者和被观察者都不需要知道对方的存在。双方的协议也非常简单,只需要知道相同的数据源名称即可。而且名称是随便由你起的,用你女朋友的名字都可以。
秋的红果实 2012-06-21
  • 打赏
  • 举报
回复
看样子是单机版的,规模估计也不大,那你可以在每个窗体上,放一个时钟控件,每隔1秒钟访问一次数据库,这样呈现在你子窗体上的内容不就是最新的吗,数据都是同步的,不用担心效率,这个不会占系统多少资源的

如果怕占用系统较多资源,可以改进,用一个公共变量bool CommonVar=false;当有数据改变时,设置CommonVar=true;定时检查CommonVar,以指令其他子窗体更新数据;这种也许要用时钟控件,但用一个就可以了

建议不要追风那些时髦的技术,你描述的问题,本质上就是我上面说的。从根本解决问题,用软件工程那些本质的方法从根源上解决问题,新技术永远学不完,跟不上,越跟越傻瓜化,越有依赖,越不灵活
  • 打赏
  • 举报
回复
不过话要说回来了,你这个程序设计只有单机的思路。当然如果你就是做个个人用的小工具,可以的。但是假设给企业,这种设计就完全是不懂网络了。

有时候设计师编程貌似很好,可是假设格局不高,做出来的程序也有严重的问题。
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
RT;
做进销存系统的时候,客户一般喜欢把库存子窗口和销售子窗口等等打开,这样最好能在销售窗口做销售时,库存窗口的数据及时变化。(PS:不止这两个窗口需要,类似的情况很多)
[/Quote]

http://msdn.microsoft.com/zh-cn/library/system.componentmodel.inotifypropertychanged.aspx
http://msdn.microsoft.com/zh-cn/library/system.collections.specialized.inotifycollectionchanged.aspx

请尽量使用标准的、直接了当的接口规范。
stonespace 2012-06-21
  • 打赏
  • 举报
回复
对于观察者模式,c#有一个比较简单的实现方案,把数据库更新的消息设计为c#的事件,这个事件放在MDI主窗口类中,凡是创建子窗口后,每个子窗口都响应这个事件,在这个事件中更新自己的界面,

如果一个子窗口改变了数据,只需引发MDI主窗口的这个事件就可以,
stonespace 2012-06-21
  • 打赏
  • 举报
回复
你可以参考设计模式中的观察者模式,最适合解决你的这个问题,所有子窗口注册成为观察者,一旦某个子窗口更改了数据库,就像观察者消息中心发送一个数据更新消息,中心把这个消息转发给其他注册的观察者,其他观察者就可以更改界面,

110,537

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧