我有两个名为UCCreateProfile.ascx的用户控件(用于创建/编辑配置文件数据)和UCProfileList.ascx(用于在GridView中显示配置文件数据)。现在,无论何时创建新的配置文件,我都要更新我的UCProfileList控件以显示新条目。
针对上述问题的最佳解决方案我将采用观察者模式。在我的例子中,UCCreatedProfile是一个Subject / Observable,而UCProfileList是一个Observer,并且根据模式定义,当观察者初始化时,它知道谁是我的Subject / Observable并将自己添加到Subject / Observable列表中。因此,只要在Subject / Observable中发生更改,就会通知它。
这种模式最符合我的要求,但实现此描述的问题很少,如下所示。
我在CMS(Umbraco)下工作,我没有任何物理容器页面(.aspx)。我要做的是使用以下代码在UCProfileList(Observer)onLoad事件中找到UCCreateProfile(Subject / Observable)。
private Control FindCreateProfileControl()
{
Control control = null;
Control frm = GetFormInstance();
control = GetControlRecursive(frm.Controls);
return control;
}
其中GetFormInstance()方法是
private Control GetFormInstance()
{
Control ctrl = this.Parent;
while (true)
{
ctrl = ctrl.Parent;
if (ctrl is HtmlForm)
{
break;
}
}
return ctrl;
}
和GetControlRecursive()方法是
private Control GetControlRecursive(ControlCollection ctrls)
{
Control result = null;
foreach (Control item in ctrls)
{
if (result != null) break;
if (item is UCCreateProfile)
{
result = item;
return result;
}
if (item.Controls != null)
result = GetControlRecursive(item.Controls);
}
return result;
}
这样我可以在UCProfileList(Observer)中找到UCCreateProfile(Subject / Observable)用户控件,但找出(Subject / Observable)的方法并不是那么快。正如您所看到的,我需要遍历所有控件并首先找到HtmlForm控件,然后循环遍历HtmlForm控件下的所有子控件,并找到我们正在寻找的相应控件。
其次,如果UCCreatedProfile.ascx(Subject / Observable)位于UCProfileList.ascx(Observer)之前非常重要,我的代码只会放在容器中,因为这样UCCreateProfile将首先加载并在UCProfileList中查找。但是如果有人改变了这两个控件的位置,我的代码将无效。
因此,为了摆脱这些问题,我需要一些解决方案,它可以更快地运行并独立于控件的位置。
我已经找到了如下所述的解决方案。如果这是一个很好的方法,请告诉我。如果有其他选择,请告诉我。
我有一个会话级变量(带Dictionary<ISubject, List<Observer>>
的字典)。无论首先初始化/加载哪个用户控件,User Control都会将自己添加到此字典中。
如果首先添加了Subject / Observable,则会在此词典中找到相应的观察者。
如果首先添加Observer,它将添加到带有空条目的字典中。当主题添加时,将建立关联。
此致
/ RIZWAN
答案 0 :(得分:2)
Observer模式最好通过事件和委托在.NET中实现。如果您使用事件和代理,则您提到的Dictionary
变得完全没必要。请参阅下面的代码(仅显示重要部分):
public partial class UserProfile : System.Web.UI.UserControl
{
//This is the event handler for when a user is updated on the UserProfile Control
public event EventHandler<UserUpdatedEventArgs> UserUpdated;
protected void btnUpdate_Click(object sender, EventArgs e)
{
//Do whatever you need above and then see who's subscribed to this event
var userUpdated = UserUpdated;
if (userUpdated != null)
{
//Initialize UserUpdatedEventArgs as you want. You can, for example,
//pass a "User" object if you have one
userUpdated(this,new UserUpdatedEventArgs({....}));
}
}
}
public class UserUpdatedEventArgs : EventArgs
{
public User UserUpdated {get;set;}
public UserUpdatedEventArgs (User u)
{
UserUpdated=u;
}
}
现在订阅UserUpdated
上UserProfile
控件UserListControl
的事件就像这样简单:
public partial class UserList : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
//Find the UserProfile control in the page. It seems that you already have a
//recursive function that finds it. I wouldn't do that but that's for another topic...
UserProfile up = this.Parent.FindControl("UserProfile1") as UserProfile;
if(up!=null)
//Register for the event
up.UserUpdated += new EventHandler<UserUpdatedEventArgs>(up_UserUpdated);
}
//This will be called automatically every time a user is updated on the UserProfile control
protected void up_UserUpdated(object sender, UserUpdatedEventArgs e)
{
User u = e.UserUpdated;
//Do something with u...
}
}