Создание настраиваемых невизуальных клиентских компонентов

Visual Studio 2010

Обновлен: Ноябрь 2007

В этом разделе описывается процедура создания невизуального клиентского компонента AJAX в ASP.NET на основе клиентского базового класса Sys.Component, а также способы использования этого компонента на странице.

В данном руководстве рассматриваются следующие вопросы:

  • Использование шаблона разработки прототипа для определения класса невизуального компонента в ECMAScript (JavaScript).

  • Регистрация невизуального компонента как класс на основе базового класса Component.

  • Инициализация базового класса Component невизуального компонента и вызов его методов.

  • Создание свойств, генерирующих уведомление об изменении.

  • Использование компонента на странице и привязка к событиям компонента.

В этом обзоре в качестве примера невизуального клиентского компонента рассматривается компонент Timer. Компонент Timer создает события, которые можно обрабатывать.

В этом разделе основной упор делается на объекты на базе клиентского невизуального компонента. Эти компоненты являются производными от Component, и обычно им не соответствуют элементы пользовательского интерфейса. Существует два дополнительных типа объектов клиентских компонентов ASP.NET AJAX, расширяющих функциональные возможности основного компонента: поведения, производные от Sys.UI.Behavior, и элементы управления, производные от Sys.UI.Control. В следующей таблице приведены различия между компонентами, поведениями и элементами управления.

Типы объектов клиентских компонентов

Сводка

Компоненты

  • Производные от базового класса Component.

  • Обычно им не соответствуют элементы пользовательского интерфейса (например, компонент Timer, который периодически создает события, но не отображается на странице).

  • Не имеют связанных элементов DOM.

  • Инкапсулируют клиентский код, предназначенный для повторного использования в разных приложениях.

Поведения

  • Производные от базового класса Behavior, расширяющего базовый класс Component.

  • Расширяют поведение элементов DOM (например, поведение отображения водяных знаков, которое можно присоединить к существующему текстовому полю).

  • Могут создавать элементы пользовательского интерфейса, хотя они обычно не изменяют основное поведение элемента DOM, с которым они связаны.

  • Доступны непосредственно из элемента DOM с использованием настраиваемого атрибута (expando). Атрибуту будет соответствовать имя поведения, если оно задано; в противном случае ему будет соответствовать имя типа (неполное).

  • Не требуют связи с другим клиентским объектом, таким как класс, производный от классов Control или Behavior.

  • Могут ссылаться на элемент управления или на не управляющий элемент HTML в своем свойстве element .

Элементы управления

  • Производные от базового класса Control, расширяющего базовый класс Component.

  • Представляют элемент DOM как клиентский объект, часто меняя обычное поведение исходного элемента DOM и добавляя к нему новые функциональные возможности. Например, элемент управления меню может считывать элементы li из элемента ul как исходные данные, но не может отображать маркированный список.

  • Доступны непосредственно из элемента DOM с помощью элемента управления expando.

Для запуска примера, рассмотренного в этом разделе, необходимы:

Невизуальный клиентский компонент ASP.NET AJAX инкапсулирует код JavaScript, предназначенный для повторного использования в разных приложениях. Примером невизуального компонента является компонент Timer, создающий события через заданные интервалы времени.

Поскольку это настраиваемый компонент является производным от базового класса Component, он автоматически наследует следующие возможности:

  • Независимая от обозревателя модель для управления привязками обработчика к событиям клиентского объекта.

  • Автоматическая регистрация компонента в клиентском приложении в качестве удаляемого объекта, реализующего интерфейс Sys.IDisposable.

  • Возможность создавать события уведомления при изменении свойств.

  • Возможность выполнять пакетную обработку параметров свойства компонента. Это более эффективно с точки зрения размера сценария и времени обработки, чем обработка всей логики в методах доступа get и set для отдельного свойства.

  • Перезапись метода Sys.Component.initialize для инициализации любых свойств и прослушивателей событий.

Для реализации настраиваемого клиентского компонента, производного от класса Component выполните следующую процедуру.

  • Определите класс компонентов, используя шаблон разработки прототипа.

  • Инициализируйте основной экземпляр Component компонента.

  • Отобразите все методы доступа к свойству и при необходимости создайте событие уведомления propertyChanged.

  • Перезапишите метод dispose для освобождения ресурсов (например, очистка обработчиков событий).

В следующих подразделах подробно описаны этапы процедуры реализации.

Определение класса компонентов с помощью модели разработки прототипа

Клиентский класс ASP.NET AJAX, включающий класс компонентов, определяется в JavaScript с использованием модели разработки прототипа. Для определения класса компонентов с использованием модели разработки прототипа выполните следующую процедуру.

  • Зарегистрируйте пространство имен для своего класса компонентов.

  • Создайте функцию-конструктор компонента и используйте ее для определения любых закрытых полей и задания начальных значений.

  • Определите прототип компонента.

  • Зарегистрируйте функцию компонента как класс, производный от класса Component.

Дополнительные сведения см. в разделе Создание клиентского класса компонентов с помощью модели прототипа.

Инициализация базового класса

В функции-конструкторе компонента вызовите наследованный метод Type.initializeBase для инициализации базового типа зарегистрированного класса. Класс невизуальных компонентов регистрируется как класс, имеющий базовый тип Component. После инициализации базового класса Component его методы доступны компоненту, и он автоматически регистрирует компонент как удаляемый объект с приложением ASP.NET с поддержкой AJAX. Дополнительные сведения см. в разделе Интерфейс Sys.IDisposable.

Все классы компонентов, производные от Component, должны инициализировать свой базовый класс из конструктора. До запуска любого другого пода в конструкторе обычно следует вызвать initializeBase. В следующем примере демонстрируются функция-конструктор невизуального компонента, производного от Component.

Samples.SimpleComponent = function()
{
    Samples.SimpleComponent.initializeBase(this);
}

Определение свойств и создание уведомлений об изменении свойства

Свойства определяются в классе компонента, который разработчики страниц могут получать и задавать. Компонент ASP.NET AJAX, производный от Component, наследует метод Sys.Component.raisePropertyChanged, вызываемый для создания события propertyChanged для свойств компонента. Разработчики страниц, использующий этот компонент, могут затем выполнить привязку к этим событиям. Дополнительные сведения см. в разделе Определение пользовательских свойств компонентов и создание событий PropertyChanged.

Инициализация свойств и прослушивателей событий

Если настраиваемый компонент должен выполнять инициализацию каких-либо свойств или прослушивателей событий, необходимо перезаписать метод Sys.Component.initialize в прототипе компонента. Например, невизуальный компонент, производный от Component, может назначить событию делегата (например, window.onFocus). На последнем этапе вызовите базовый метод initialize для включения базового класса компонента, чтобы завершить инициализацию.

ASP.NET содержит классы и методы, обеспечивающие стандартное управление событиями для компонентов и элементов DOM. Для управления событиями компонента используйте класс Sys.EventHandlerList. Например, выполните привязку событий, используя метод Sys.EventHandlerList.addHandler, и освободите их, используя метод Sys.EventHandlerList.removeHandler. Дополнительные сведения см. в разделе Класс Sys.EventHandlerList.

Для управления обработчиками событий для элементов DOM или объектов window используйте класс Sys.UI.DomEvent. Например, можно выполнить или отменить привязку обработчиков событий, используя методы Sys.UI.DomEvent addHandler и Sys.UI.DomEvent removeHandler. Дополнительные сведения см. в разделе Класс Sys.UI.DomEvent.

Освобождение ресурсов

Если до удаления настраиваемого компонента он должен высвободить ресурсы, перезапишите метод dispose и освободите ресурсы в перезаписанном методе. Это обеспечит освобождение ресурсов непосредственно перед удалением компонента. К освобождаемым ресурсам могут относиться обработчики для событий DOM. Убедитесь, что объект можно удалить, проверив, что удалены все возможные циклические ссылки между элементами DOM и объектом компонента. Дополнительные сведения см. в разделе Освобождение ресурсов компонентов.

Для использования настраиваемого клиентского компонента на странице приложения ASP.NET AJAX выполните следующую процедуру.

  • Зарегистрируйте библиотеку сценариев компонента на веб-странице.

  • Создайте экземпляр компонента.

В следующих подразделах подробно описаны этапы этой процедуры.

Регистрация библиотеки сценариев компонента на веб-странице.

Можно зарегистрировать сценарии, необходимые для клиентского элемента управления на странице, используя элемент управления ScriptManager, декларативно или программно. В следующем примере приведена декларативная разметка для элемента управления ScriptManager, регистрирующего сценарий компонента.

<form id="form1" runat="server">
  <asp:ScriptManager runat="server" ID="ScriptManager01">
    <scripts>
      <asp:ScriptReference path="DemoTimer.js" />
    </scripts>
  </asp:ScriptManager>
</form>

Элемент asp:ScriptManager содержит элемент asp:ScriptReference внутри узла scripts. Атрибут path элемента asp:ScriptReference ссылается на путь к файлу JS (в примере: DemoTimer.js), определяющему класс компонента. Дополнительные сведения см. в разделе Динамическое присваивание ссылок на сценарии и общих сведениях о классе ScriptManager.

Кроме регистрации файлов сценариев с использованием элемента управления ScriptManager, можно управлять клиентскими компонентами, используя настраиваемый серверный элемент управления, реализующий интерфейс IScriptControl. Настраиваемый серверный элемент управления может автоматически регистрировать необходимые сценарии компоненты и отобрать декларативную разметку для настройки свойств компонентов и привязок событий. При регистрации сценария с использованием настраиваемого серверного элемента управления можно облегчить процесс использования компонента для разработчика. Дополнительные сведения см. в общих сведениях о классе IScriptControl.

Bb386520.alert_note(ru-ru,VS.100).gifПримечание.

Все автономные файлы сценария, которые необходимо регистрировать с использованием элемента управления ScriptManager, должны вызывать метод notifyScriptLoaded, чтобы уведомить приложение об окончании загрузки сценария. Сценарии, внедренные в сборку, в большинстве случаев не должны вызывать этот метод. Дополнительные сведения см. в разделе Метод Sys.Application.notifyScriptLoaded.

Создание экземпляра пользовательского компонента

Инициализация клиентского компонента выполняется вызовом метода Sys.Component.create или ярлыка $create. Передайте параметры методу $create для указания типа компонента. Кроме того, передайте объект JSON, содержащий необходимое значение идентификатора и дополнительные начальные значения параметра и дополнительные привязки обработчика событий.

В следующем примере демонстрируется процедура создания экземпляра компонента, вызывая метод $create.

var app = Sys.Application;
app.add_init(applicationInitHandler);

function applicationInitHandler(sender, args) 
{
    $create(Demo.Timer, {enabled:true,id:"demoTimer1", interval:2000}, 
        {tick:OnTick}, null);
}

Дополнительные сведения см. в разделах Метод Sys.Component.create и Метод Sys.Component $create.

В этом разделе предлагается создать настраиваемый клиентский компонент с именем Demo.Timer, расширяющий базовый класс Component, после чего этот компонент используется на странице. Demo.Timer — это простой компонент Timer, определяющий событие tick, отображающий свойства enabled и interval и создающий события уведомления об изменении для свойства interval. Разработчик страниц, использующий компонент Demo.Timer может обрабатывать событие tick. Разработчик может также выполнить привязку к событию изменения свойства и принимать меры при каждом обновлении свойства interval.

Создание кода для компонента Demo.Timer

  1. В корневом каталоги веб-приложения ASP.NET с поддержкой AJAX создайте файл с именем DemoTimer.js.

  2. Добавьте в этот файл следующий код:

    
    Type.registerNamespace("Demo");
    
    Demo.Timer = function() {
        Demo.Timer.initializeBase(this);
    
        this._interval = 1000;
        this._enabled = false;
        this._timer = null;
    }
    
    Demo.Timer.prototype = {
        // OK to declare value types in the prototype
    
    
        get_interval: function() {
            /// <value type="Number">Interval in milliseconds</value>
            return this._interval;
        },
        set_interval: function(value) {
            if (this._interval !== value) {
                this._interval = value;
                this.raisePropertyChanged('interval');
    
                if (!this.get_isUpdating() && (this._timer !== null)) {
                    this._restartTimer();
                }
            }
        },
    
        get_enabled: function() {
            /// <value type="Boolean">True if timer is enabled, false if disabled.</value>
            return this._enabled;
        },
        set_enabled: function(value) {
            if (value !== this.get_enabled()) {
                this._enabled = value;
                this.raisePropertyChanged('enabled');
                if (!this.get_isUpdating()) {
                    if (value) {
                        this._startTimer();
                    }
                    else {
                        this._stopTimer();
                    }
                }
            }
        },
    
        // events
        add_tick: function(handler) {
            /// <summary>Adds a event handler for the tick event.</summary>
            /// <param name="handler" type="Function">The handler to add to the event.</param>
            this.get_events().addHandler("tick", handler);
        },
        remove_tick: function(handler) {
            /// <summary>Removes a event handler for the tick event.</summary>
            /// <param name="handler" type="Function">The handler to remove from the event.</param>
            this.get_events().removeHandler("tick", handler);
        },
    
        dispose: function() {
            // call set_enabled so the property changed event fires, for potentially attached listeners.
            this.set_enabled(false);
            // make sure it stopped so we aren't called after disposal
            this._stopTimer();
            // be sure to call base.dispose()
            Demo.Timer.callBaseMethod(this, 'dispose');
        },
    
        updated: function() {
            Demo.Timer.callBaseMethod(this, 'updated');
            // called after batch updates, this.beginUpdate(), this.endUpdate().
            if (this._enabled) {
                this._restartTimer();
            }
        },
    
        _timerCallback: function() {
            var handler = this.get_events().getHandler("tick");
            if (handler) {
                handler(this, Sys.EventArgs.Empty);
            }
        },
    
        _restartTimer: function() {
            this._stopTimer();
            this._startTimer();
        },
    
        _startTimer: function() {
            // save timer cookie for removal later
            this._timer = window.setInterval(Function.createDelegate(this, this._timerCallback), this._interval);
        },
    
        _stopTimer: function() {
            if(this._timer) {
                window.clearInterval(this._timer);
                this._timer = null;
            }
        }
    }
    
    Demo.Timer.registerClass('Demo.Timer', Sys.Component);
    
    // Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler
    // invoke Sys.Application.notifyScriptLoaded to notify ScriptManager 
    // that this is the end of the script.
    if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
    
    
    
    

Рассмотрение кода

Этот код регистрирует пространство имен Demo, вызывая метод Type.registerNamespace. Рекомендуется объявить и инициализировать все закрытые поля в конструкторе (например, interval в этом примере). Конструктор вызывает унаследованный метод initializeBase, в результате чего становятся доступными методы базового класса Component. Инициализированный базовый класс в свою очередь регистрирует экземпляр Demo.Timer в клиентском приложении в виде удаляемого объекта.

В прототипе код объявляет и инициализирует два общих свойства: interval и enabled. Определения свойств включают закрытые поля, содержащие их значения, и методы доступа get и set для каждого свойства. В методе set для каждого общего свойства код создает событие propertyChanged, вызывающее метод raisePropertyChanged. Это событие уведомляет разработчиков страниц о каждом изменении этого свойства.

Методы add_tick и remove_tick позволяют разработчику страниц добавлять и удалять методы прослушивания события tick. Эти методы в свою очередь добавляют или удаляют определенный обработчик, используя коллекцию Sys.EventHandlerList компонента. Объект EventHandlerList содержит коллекцию обработчиков событий компонента благодаря унаследованному свойству Sys.Component.events. В этом примере код вызывает методы Sys.EventHandlerList.addHandler и Sys.EventHandlerList.removeHandler возвращаемого объекта EventHandlerList, чтобы добавить или удалить определенный обработчик.

Класс Demo.Timer переопределяет метод dispose базового класса для обновления свойства enabled и для информирования потребителей об отключении компонента. Метод доступа set для свойства enabled создает событие propertyChanged, отправляющее уведомление. Код вызывает закрытый метод _stopTimer для прекращения создания событий tick. И, наконец, код вызывает базовый метод dispose, чтобы разрешить приложению освободить компонент.

Можно управлять экземплярами клиентского компонента ASP.NET AJAX на странице с помощью настраиваемого серверного элемента управления или клиентского сценария на веб-странице. В этом разделе рассматривается создание экземпляра компонента с использованием клиентского сценария на веб-странице.

Создание страницы, использующей компонент Demo.Timer

  1. В каталоге, где размещается файл DemoTimer.js, создайте файл с именем DemoTimer.aspx и добавьте в него следующую разметку и код:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
            <title>Demo Timer Component</title>
    </head>
    <body>
        <form id="form1" runat="server"> 
            <div>
                <asp:ScriptManager ID="ScriptManager1" runat="server">
                    <Scripts>
                        <asp:ScriptReference Path="DemoTimer.js"/>
                    </Scripts>
                </asp:ScriptManager>
    
                Timer Tick Count: <span id="result">0</span>
            </div>
    
            <script type="text/javascript">
    
           	    function OnTick(sender, args) {
                    var result = $get("result");
                    result.innerText = parseInt(result.innerText) + 1;
                }
               	
           	     var app = Sys.Application;
                 app.add_init(applicationInitHandler);
    
                 function applicationInitHandler(sender, args) {
    		        // Create the DemoTimer component instance.  
    		        // Set properties and bind events.
    		        $create(Demo.Timer, 
    		            {enabled:true,id:"demoTimer1",interval:2000}, 
    		            {tick:OnTick}, null, null);
    		    }
    
            </script> 
        </form>
    </body>
    </html>
    
    
    
  2. В этом же каталоге создайте файл с именем TestDemoTimer.js и добавьте в него следующий код:

    function OnTick(sender, args) {
        var result = $get("result");
        result.innerText = parseInt(result.innerText) + 1;
    }
    
     var app = Sys.Application;
     app.add_init(applicationInitHandler);
    
     function applicationInitHandler(sender, args) {
        // Create the DemoTimer component instance.  
        // Set properties and bind events.
        $create(Demo.Timer, 
            {enabled:true,id:"demoTimer1",interval:2000}, 
            {tick:OnTick}, null, null);
    }
    
    
    

Рассмотрение кода

Демонстрационная страница загружает файл TestDemoTimer.js, используя код JavaScript, содержащий функции OnTick и applicationInitHandler. Функция OnTick используется для обработки события tick компонента Demo.Timer и обновляет значение счетчика в HTML-элементе span.

Функция applicationInitHandler — это обработчик для события app_init. При использовании этой функции создается экземпляр компонента Demo.Timer в клиентском сценарии в результате вызова метода $create, передающего следующий аргумент:

  • Аргумент type — это класс Demo.Timer, созданный ранее.

  • Аргумент properties состоит из объекта JSON, содержащего необходимое значение идентификатора компонента, за которым следуют пары имени и значения свойства, которые указывают имена и начальные значения свойств. Для демонстрационных целей свойству interval сначала присвоено значение 2000 миллисекунд, чтобы таймер создавал событие tick каждые две секунды. (В производственном приложении этому интервалу, возможно, необходимо присвоить меньшее значение для снижения объема сетевого трафика). Свойству enabled компонента присваивается значение true, чтобы таймер запускался сразу после создания экземпляра.

  • Аргумент events содержит объект, содержащий пары названия события и соответствующего обработчика. В этом случае обработчик onTick назначается событию tick, определенному в элементе script страницы.

Файл DemoTimer.aspx — это веб-страница ASP.NET, на которой размещается компонент. В элементе управления ScriptManager этой страницы атрибут path элемента asp:ScriptReference ссылается на путь к файлу DemoTimer.js, определяющему класс компонента Demo.Timer.

Показ: