如何为ASP.NET用户控件创建客户端API?

3
例如:我有一个用户控件,叫做"MyControl1",我想创建"Show()"和"Hide()" Javascript函数,可以从父页面的Javascript中调用,类似于"MyControl1.Show();"。
显然页面上可能有多个控件,所以如果您在页面上放置了两个控件:
   <MyTag:MyControl ID="MyControl1" runat="server" />
   <MyTag:MyControl ID="MyControl2" runat="server" />

希望能够像这样做一些事情:

   function ParentPageJavascriptFunction(){
      MyControl1.Show();
      MyControl2.Hide();
   }

我该如何开始做这个?我找到的每篇文章都是关于如何在用户控件中挂接事件,我只想让父页面能够随意调用某些函数——或者也许有一种方法可以使用事件来完成我正在尝试的事情,但我不理解它。
编辑:这似乎传达得不正确,那么这个怎么样?
这就是我的假设控件长什么样子:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyControl.ascx.cs" Inherits="controls_MyControl" %>

<script type="text/javascript">
    function Display(msg) {
        var Label1ClientID = '<%= Label1.ClientID %>';
        document.getElementById(Label1ClientID)... (display the message)
    } 
</script>

<asp:Label ID="Label1" runat="server"> </asp:Label>

现在,“父级”页面会像这样注册控件:
<%@ Register Src="~/controls/MyControl.ascx" TagName="MyControl" TagPrefix="MyTag" %>

在文本主体中,它包含以下内容:

<asp:Button ID="MyButton1" runat="server" Text="Display" OnClientClick="javascript:MyControl1.Display('hello'); return false;" />
    <MyTag:MyControl Id="MyControl1" runat="server">
    </MyTag:MyControl>

唯一的问题是,显然这不起作用 - 但这就是我想要解决的问题。如何使其起作用?我希望能够允许嵌套用户控件的页面(父页面)调用该用户控件的显示功能。我知道你可以直接调用显示功能,因为它只是js(无论它是否在用户控件中),但如果你在该页面上有2个这样的控件,那么这种方法就行不通了,所以你如何创建javascript代码,使得父页面可以区分哪个用户控件的函数需要被调用?

如果您使用ClientID来区分,则没有问题。那么它只是“获取用特定ClientId表示的客户端对象”和“调用其上的方法”。问题在于大多数代码没有保持实例的清晰分离... - user166390
3个回答

2
您要做的事情非常简单。asp.net控件是将纯html包装成页面上的控件的笨重方式。为了能够在控件上调用javascript事件,您需要知道控件呈现到页面上的结果的ID。
注意:所有.net控件都有一个名为.ClientID的成员,它代表运行时将生成作为其设计封装/呈现的HTML的ID。
为了在呈现时获取页面上控件的ID,您需要执行以下操作:
var dotNetHtmlClientID = '<%= txtMessage.ClientID %>';
$('#' + dotNetHtmlClientID).bind('onclick', function(){

    //dostuff

});

第一行是C#内联代码,用于从运行时获取ClientID。

更新**

如果您想从所在页面获取用户控件的clientID,则需要执行以下操作:

//在dot net页面服务器端上

protected string controlClientId;

void Page_Load(object sender, Eventargs e)
{

     controlClientId = Page.FindControl("MyControlThatImBindingEventToDotNetId").ClientID;

}

//在页面客户端

var dotNetHtmlClientID = '<%= controlClientId %>';
$('#' + dotNetHtmlClientID).bind('onclick', function(){

    //dostuff

});

我不想调用事件 - 我想从父页面调用特定于该用户控件的函数。 - Xolamee
好的,我修改了我的答案,希望这能有所帮助。 - Exitos

1
你可以通过创建一个JavaScript模块来解决这个问题。简单来说,这意味着你可能会有一个名为xyzClientApi.js的文件,其中包含一些增强页面功能的函数集合。
在该文件中,你将创建一个对象,该对象引用你想要添加脚本功能的控件。在JavaScript中,你可以将变量或对象嵌套在函数内部,以有效地对它们进行命名空间处理(这将减少你的JavaScript与其他可能正在使用的JavaScript代码之间的冲突)。
这个函数将为你的客户端API创建一个作用域:
// this is the variable that you'll reference from your view markup
// the style shown here is a self executing anonymous function.
// sounds scarier than it is! don't be afraid.
var xyzClientApi = (function($) {

    // this is the object that will be assigned to xyzClientApi.
    // the {} in this line is just an object initializer
    // you can put things inside of it using this syntax: <name>:<value>
    var model = {};

    // you can use an init() function to accept parameters
    // when you initialize the api.
    // i.e. xyzClientApi.init({myUserControl:'#myUserControl1', myButton:'#myButton1'});
    model.init = function(params) {
        // add properties and functions to your model
        // based on parameters in here
        model.myUserControl = {
          display: function(text) {
              $(params.myUserControl).val(text);
          }
        };
        // assign a click handler to your button!
        $(params.myButton).click(function(){
           model.myUserControl.display('hello!');
        });
    };

    // remember to return model!
    return model;

// by passing jQuery here, the '$' parameter
// is guaranteed to be jQuery when you use it
// this is to play nice with other js libraries
// that might also use '$' at the global scope.
})(jQuery);

然后,您将能够在您的标记中使用它:
<script type="text/javascript" src="xyzClientApi.js"></script>
<script type="text/javascript">
    // standard jQuery DOM ready function
    $().ready(function(){
        // call the init from above
        // this is using the object initializer syntax in javascript
        // it's just a nice way of grouping your parameters
        // each property of the object is declared, followed by a colon,
        // which is followed by a value. this value can be any valid javascript
        // object, like a string, value, or some other object.
        // in this case, we're passing the known control IDs in.
        xyzClientApi.init({
           myUserControl: '<%= MyUserControlClientID %>',
           myButton: '<%= MyButtonControlClientID %>'
        });
    });
</script>

如果您不知道控件的ID,或者无法合理确定它的ID,可以尝试将其包装在div或其他HTML元素中:

<div id="MyControlIsInhere">...your control...</div>

然后你可以使用这样的选择器:

'#MyControlIsInHere input[type=button]'

这似乎是朝着正确方向迈出的一步 - 你能否提供一些更详细的解释,说明这段代码的作用?我只是举了Show和Hide作为例子,客户端函数的真正目的将会更加复杂。 - Xolamee
@Xolamee,你能举一个更复杂的交互例子吗?只要你坚持使用客户端代码,我相信你可以做任何你需要做的事情。 - John Nelson
@John,我正在使用DevExpress控件来构建我的用户控件,它们每个都有自己的客户端API,因此我的用户控件函数将通过它们的客户端API访问和操作各种devexpress控件。我同意只要我坚持使用客户端代码,我应该能够做到我想做的事情,但我并不完全理解你提供的代码。pageModule是什么?"var model = { myUserControl1 : myUserControl1 };"这一行是做什么的?我了解一点jquery,但我无法真正理解这里发生了什么。 - Xolamee
我明白了,很抱歉。等我有机会的时候,我会详细说明一下。 - John Nelson
@John Oh,我还更新了我的帖子,并更详细地描述了我想要做的事情,请告诉我是否有所帮助。 - Xolamee
显示剩余3条评论

1
我的解决方案涉及您在用户控件中添加一个包装器div,然后使用jQuery进行扩展 - 添加任何客户端方法和属性。诀窍是将包装器div的ID设置为与用户控件相同,如id='<%= this.ID %>'
用户控件本身实际上不会呈现任何内容(仅内容),因此div只是给您一个带有ID的对象,您可以在包含页面上引用它(因为您知道用户控件的ID)。通过从div扩展的属性/方法,一切都被整齐地封装(全局命名空间中没有任何内容),您可以使用标准的$get('myUcWhatever')使用用户控件的ID来简单地获取对象引用 - 然后您就可以开始了。
如果您想要更多详细信息,我在我的博客上发布了完整的解释和演示解决方案:http://programmerramblings.blogspot.com/2011/07/clientside-api-for-aspnet-user-controls.html

应该使用 ClientID,而不是其他什么。另外,为什么不使用完整的 ASP.NET 控件支持呢? - user166390
如果您使用了ClientID,那么您就不需要在包含页面上使用$get进行引用。另外,您所说的“完整的ASP.NET控件支持”是什么意思? - kman
支持"ASP.NET AJAX",Sys.UI.Control,IScriptControl等(我猜这假设使用了AJAX扩展)。我真的认为应该是ClientID,根据$get(id),其中id表示DOM节点ID。如果使用this.ID生成它,则可以用它来访问它,但为什么不使用this.ClientID呢?(也就是说,为什么要使用元素ID而不是ClientID?) - user166390
实际上,IScriptControl只需要ScriptManager,而不需要“AJAX”或UpdatePanels,尽管它可以与它们一起使用。但是,我不确定使用它会带来哪些ASP.NET要求。关于ClientID或ID,我并不完全确定哪个在这里是“最佳/正确”的,我很乐意阅读一个好的参考资料。 - user166390
你关于使用ClientID更合适的说法可能是正确的。在这种情况下,你只需要在$get()调用中使用服务器标记,例如$get('<%= myUc1.ClientID %>')。但我仍然不确定你在IScriptControl方面的想法。从我所记得的很少的信息来看,似乎对于这么简单的事情来说,这将会更加复杂/过度。? - kman
我必须承认,我的IScriptControl经验仅限于自定义控件 - 我甚至从未真正考虑过为用户控件实现它。 这似乎是一个新颖的想法 - 但是对于像所描述的情况这样的事情,它仍然似乎过于复杂。 也许如果您有一个相当复杂的客户端API? - kman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接