如何在asp.net中实现观察者模式以使用用户控件

时间:2012-07-21 07:54:33

标签: asp.net design-patterns

我有两个名为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

1 个答案:

答案 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;
    }
}

现在订阅UserUpdatedUserProfile控件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...
    }
}