Ajax AutoCompleteExtender стилизует отдельные элементы?

Краткая версия:

Я хочу знать, как я могу изменить стиль CSS отдельных элементов, сгенерированных расширителем автозаполнения, но элемент управления позволяет мне установить стиль только для всех элементов как для группы.

Длинная версия:

У меня есть окно поиска, в котором используется AutoCompleteExtender. По мере ввода результаты поиска появляются в поле под окном поиска.

<asp:TextBox ID="txtSearch" OnChange="javascript: Changed(this);"  runat="server" style="width:360px;" />
<cc1:AutoCompleteExtender ID="Search_AutoCompleteExtender" runat="server" 
                    BehaviorID="Search_AutoCompleteExtender"
                    MinimumPrefixLength="3" 
                    DelimiterCharacters="" 
                    Enabled="True" 
                    ServicePath="~/Services/Search.asmx" 
                    ServiceMethod="GetResults"
                    TargetControlID="txtSearch"
                    FirstRowSelected="true"
                    CompletionListItemCssClass="AutoExtenderList"
                    CompletionListHighlightedItemCssClass="AutoExtenderHighlight"
                    CompletionInterval="1" 
                    EnableCaching="false"
                    CompletionSetCount="20"
                    CompletionListElementID="autocompleteDropDownPanel1" />

<asp:Panel ID="autocompleteDropDownPanel1" runat="server" ScrollBars="Vertical" Height="250" style="overflow-y:scroll;position:absolute;left:0;top:0" /

AutoCompleteExtender имеет свойство назначать класс CSS каждому элементу, CompletionListItemCssClass и CompletionListHighlightedItemCssClass для выделенного элемента. Что я хочу знать, так это то, как я могу индивидуально стилизовать определенные элементы на основе определенных критериев.

В моей службе я запускаю хранимую процедуру, немного манипулирую результатами, а затем возвращаю массив строк обратно в расширитель автозаполнения. У меня есть эта хранимая процедура, настроенная для включения определенного флага в каждую запись. Я хочу использовать этот флаг, чтобы выделить определенные строки результатов поиска (например, сделать текст красным).

Как я могу это сделать? Кажется, я не могу найти способ получить доступ к отдельным элементам, чтобы изменить их стиль.

Я искал повсюду помощь и прихожу с пустыми руками. Спасибо.


person user1003916    schedule 21.09.2012    source источник


Ответы (1)


Такое поведение не поддерживается по умолчанию, но вы можете настроить источники AutoCompleteExtender, чтобы добавить эту функцию. На самом деле доступно два способа: первый — расширить объект, возвращенный расширителю из веб-сервиса, с новым свойством для класса элемента css и применить значение этого свойства в клиентском скрипте расширителя. И второй доступный подход — ввести новое событие для создания элемента, обработать его на странице и применить пользовательскую логику к элементу в зависимости от его значения. Давайте реализуем оба подхода здесь. Сначала вам нужно скачать исходники AjaxControlToolkit, если вы еще этого не сделали, и открыть Client/MicrosoftAjax.Extended/AutoComplete/AutoCompleteBehavior.pre.js файл. В этом файле вам нужно изменить метод _update, как показано ниже:

_update: function (prefixText, completionItems, cacheResults) {
    /// <summary>
    /// Method to update the status of the autocomplete behavior
    /// </summary>
    /// <param name="prefixText" type="String" DomElement="false" mayBeNull="true" />
    /// <param name="completionItems" type="Object" DomElement="false" mayBeNull="true" />
    /// <param name="cacheResults" type="Object" DomElement="false" mayBeNull="true" />
    /// <returns />       
    if (cacheResults && this.get_enableCaching()) {
        if (!this._cache) {
            this._cache = {};
        }
        this._cache[prefixText] = completionItems;
    }

    // If the target control loses focus or 
    // if the value in textbox has changed before the webservice returned
    // completion items we don't need to show popup 
    if ((!this._textBoxHasFocus) || (prefixText != this._currentCompletionWord())) {
        this._hideCompletionList();
        return;
    }

    if (completionItems && completionItems.length) {
        this._completionListElement.innerHTML = '';
        this._selectIndex = -1;
        var _firstChild = null;
        var text = null;
        var value = null;
        var cssClass = null;
        var dataItem = null;

        //remove this line if you don't need to implement itemDataBinding event
        var itemDataBindingHandler = this.get_events().getHandler('itemDataBinding');

        for (var i = 0; i < completionItems.length; i++) {
            // Create the item                
            var itemElement = null;
            if (this._completionListElementID) {
                // the completion element has been created by the user and li won't necessarily work
                itemElement = document.createElement('div');
            } else {
                itemElement = document.createElement('li');
            }

            // set the first child if it is null
            if (_firstChild == null) {
                _firstChild = itemElement;
            }

            // Get the text/value for the item
            try {
                dataItem = Sys.Serialization.JavaScriptSerializer.deserialize( completionItems[i] );
                if (dataItem && dataItem.First) {
                    // Use the text and value pair returned from the web service
                    text = dataItem.First;
                    value = dataItem.Second;

                    if (dataItem.CssClass) {
                        cssClass = dataItem.CssClass;
                    }
                }
                else {
                    // If the web service only returned a regular string, use it for
                    // both the text and the value
                    text = completionItems[i];
                    value = text;
                }
            } catch (ex) {
                text = completionItems[i];
                value = completionItems[i];
            }


            // Set the text/value for the item
            // ShowOnlyCurrentWordInCompletionListItem support
            var optionText = this._showOnlyCurrentWordInCompletionListItem ? text : this._getTextWithInsertedWord(text);
            itemElement.appendChild(document.createTextNode(optionText));
            itemElement._value = value;
            itemElement.__item = '';

            if (this._completionListItemCssClass) {
                Sys.UI.DomElement.addCssClass(itemElement, this._completionListItemCssClass);
            } else {
                var itemElementStyle = itemElement.style;
                itemElementStyle.padding = '0px';
                itemElementStyle.textAlign = 'left';
                itemElementStyle.textOverflow = 'ellipsis';
                // workaround for safari since normal colors do not
                // show well there.
                if (Sys.Browser.agent === Sys.Browser.Safari) {
                    itemElementStyle.backgroundColor = 'white';
                    itemElementStyle.color = 'black';
                } else {
                    itemElementStyle.backgroundColor = this._textBackground;
                    itemElementStyle.color = this._textColor;
                }
            }

            if (cssClass) {
                Sys.UI.DomElement.addCssClass(itemElement, cssClass);   
            }

            //remove this if you don't need to implement itemDataBinding event
            if (itemDataBindingHandler) {
                itemDataBindingHandler(itemElement, dataItem || completionItems[i]);
            }

            this._completionListElement.appendChild(itemElement);
        }
        var elementBounds = $common.getBounds(this.get_element());
        this._completionListElement.style.width = Math.max(1, elementBounds.width - 2) + 'px';
        this._completionListElement.scrollTop = 0;

        this.raisePopulated(Sys.EventArgs.Empty);

        var eventArgs = new Sys.CancelEventArgs();
        this.raiseShowing(eventArgs);
        if (!eventArgs.get_cancel()) {
            this.showPopup();
            // Check if the first Row is to be selected by default and if yes highlight it and updated selectIndex.
            if (this._firstRowSelected && (_firstChild != null)) {
                this._highlightItem(_firstChild);
                this._selectIndex = 0;
            }
        }
    } else {
        this._hideCompletionList();
    }
}

Если вы не собирались реализовывать сценарий с новым событием на стороне клиента, это все изменения в проекте, которые вам нужны. Вы уже можете передать класс элемента css из метода веб-сервера вместе с текстом и значением элемента. Единственное отличие заключается в использовании пользовательского класса вместо Pair, который используется в элементе управления AjaxControlToolkit по умолчанию:

[WebMethod]
public string[] GetCompletionList(string prefixText, int count)
{
    var serializer = new JavaScriptSerializer();
    var items = Enumerable.Range(1, count)
        .Select(id => serializer.Serialize(
                        new
                        {
                            Second = id,
                            First = prefixText + "_" + Guid.NewGuid().ToString(),
                            CssClass = id % 2 == 0 ? "even" : "odd"
                        }));


    return items.ToArray();
}

ЕСЛИ вы хотите также реализовать настраиваемое клиентское событие (что действительно дает большую гибкость), вам нужно добавить еще немного кода в файл AutoCompleteBehavior.pre.js. Добавьте код ниже где-нибудь в объекте Sys.Extended.UI.AutoCompleteBehavior.prototype:

add_itemDataBinding: function (handler) {
    this.get_events().addHandler('itemDataBinding', handler);
},

remove_itemDataBinding: function (handler) {
    this.get_events().removeHandler('itemDataBinding', handler);
},

Кроме того, откройте файл Server/AjaxControlToolkit/AutoComplete/AutoCompleteExtender.cs и добавьте это свойство в класс AutoCompleteExtender:

/// <summary>
/// Handler to attach to the client-side item data binding event
/// </summary>
[DefaultValue("")]
[ExtenderControlEvent]
[ClientPropertyName("itemDataBinding")]
public string OnClientItemDataBinding
{
    get { return GetPropertyValue("OnClientItemDataBinding", string.Empty); }
    set { SetPropertyValue("OnClientItemDataBinding", value); }
}

После пересборки проекта вы можете использовать собственную сборку AjaxControlToolkit с дополнительным функционалом.

<script type="text/javascript">
    function itemDataBinding(item, dataItem) {
        var value = parseInt(dataItem.Second, 10);
        if (!isNaN(value)) {
            item.className = value % 2 == 0 ? "even" : "odd";
        }
    }
</script>

<ajaxToolkit:AutoCompleteExtender runat="server" ID="autoComplete1" 
    TargetControlID="myTextBox" ServicePath="AutoComplete.asmx" ServiceMethod="GetCompletionList" 
    OnClientItemDataBinding="itemDataBinding">
person Yuriy Rozhovetskiy    schedule 21.09.2012
comment
Это один из лучших ответов, которые я когда-либо видел. Большое спасибо, что нашли время, чтобы помочь мне. - person user1003916; 24.09.2012