代码改变世界

自动完成服务端控件(2)--整体结构的设计

2010-05-25 22:34  Kevin-wang  阅读(696)  评论(2编辑  收藏  举报

1.前端展现DOM 
  image 
   HTML dom 代码

<div class="suggest-container">
    
<ul ItemsCount="" ItemIndex="">
        
<li Index="" Key="" HideValue="">
            
<div>
                内容
            
</div>
        
</li>
    
</ul>
</div> 

2.CSS样式

/*
最外面的DIV
*/
.suggest-container
{
    margin-top
: -2px;
    border
: 1px solid #999;
    position
:absolute;
    background-color
:#FFF;
}
.suggest-container ul
{
    padding
: 0px;
    margin
: 0px;
}
.suggest-container li
{
    padding-right
: 0px;
    padding-left
: 0px;
    font-size
: 13px;
    float
: left;
    padding-bottom
: 2px;
    width
: 100%;
    color
: #404040;
    line-height
: 18px;
    padding-top
: 1px;
    margin-left
: 0px;
    cursor
: default;
}
/*
列表项内的DIV
*/
.suggest-container li div
{
    font-size
:15px;
    padding
:2px;
    padding-left
:5px;
    line-height
: 18px;
}
/*
正常的情况下列表项的背景色为白色
*/
.suggest-container li.normal
{
    background-color
: #fff;
}
/*
选中后的列表荐背景色为兰色
*/
.suggest-container li.selected
{
    background-color
: #d5e2ff;
} 


3.JS 脚本

代码
/*
自动完成控件客户端JS脚本
*/
//参数说明:
//
id:div容器 id,clientid:控件ID
AutoComplete = function(id, clientid) {
    
//最外层的div容器id
    this._ID = id;

    
this._TextBoxControlID = '';

    
this._AsyncClientScript = '';
    
//防止重复触发onpropertychange事件
    var _Completed = false;

    
//初始化方法
    this.Initialize = function() {
        
var _TextBoxControl = $(this._TextBoxControlID);
        
var _Container = $(this._ID);

        
//给document对象添加OnMouseDown事件
        document.attachEvent('onmousedown'function() { _Container.style.display = 'none'; });

        
//给关联的文本框添加事件

        
if (_TextBoxControl) {
            
//附加OnMouseDown事件,禁止文本框的onmousedown事件上传到document对象上
            _TextBoxControl.attachEvent('onmousedown'function() {
                window.event.cancelBubble 
= true;
            });
            _TextBoxControl.attachEvent(
'onfocus'function() {
                
for (var i = 0; i < __AutoCompleteExtender.length; i++) {
                    
if (__AutoCompleteExtender[i] != (clientid)) {
                        eval(__AutoCompleteExtender[i] 
+ '.Hide()');
                    }
                }
            });
            
//附加OnPropertyChange事件
            _TextBoxControl.attachEvent('onpropertychange'function() {
                
if (_Completed) return;

                
if (_TextBoxControl.value.Trim().length <= 0) {
                    _Container.innerHTML 
= '';
                    _Container.style.display 
= 'none';

                }
                
else {
                    eval(clientid 
+ '.AsyncDataBind()');
                }
            });
            _TextBoxControl.attachEvent(
'onblur'function() {
                
var valid = false;
                
var _ULs = _Container.getElementsByTagName('ul');
                
if (_ULs.length > 0) {
                    
if (_ULs[0].childNodes.length > 0) {
                        
var index = -1;
                        
for (var i = 0; i < _ULs[0].childNodes.length; i++) {
                            
if (_TextBoxControl.value.Trim() == _ULs[0].childNodes[i].innerText.Trim()) {
                                index 
= i;
                                valid 
= true;
                                
break;
                            }
                        }

                        
if (index > -1) {
                            
var _Key = _ULs[0].childNodes[index].Key;
                            
var _Text = _ULs[0].childNodes[index].innerText;
                            
var _HideValue = _ULs[0].childNodes[index].HideValue;

                            _Completed 
= true;
                            _TextBoxControl._Text 
= _Text;
                            _TextBoxControl._Value 
= _Key;
                            _TextBoxControl._HideValue 
= _HideValue;
                            _Completed 
= false;
                        }
                    }
                }
                
if (!valid) {
                    _Completed 
= true;

                    _TextBoxControl._Text 
= '';
                    _TextBoxControl._Value 
= '';
                    _TextBoxControl._HideValue 
= '';
                    _Completed 
= false;
                }

                eval(clientid 
+ '.OnCompleting()');
                
//alert(_ULs[0].childNodes.length);
            }
            )

            _TextBoxControl.attachEvent(
'onkeydown'function() {
                
var _KeyCode = window.event.keyCode;
                
var _ULs = _Container.getElementsByTagName('ul');
                
if (_ULs.length <= 0return;
                
//上方向键
                if (_KeyCode == 38) {
                    
if (_ULs[0].ItemIndex == -1) {
                        _ULs[
0].ItemIndex = 0;
                    }
                    _ULs[
0].childNodes[_ULs[0].ItemIndex].className = 'normal';

                    _ULs[
0].ItemIndex--;

                    
if (_ULs[0].ItemIndex < 0) {
                        _ULs[
0].ItemIndex = _ULs[0].ItemsCount - 1;
                    }
                    _ULs[
0].childNodes[_ULs[0].ItemIndex].className = 'selected';

                    _Completed 
= true;

                    
var _Key = _ULs[0].childNodes[_ULs[0].ItemIndex].Key;
                    
var _Text = _ULs[0].childNodes[_ULs[0].ItemIndex].innerText;
                    
var _HideValue = _ULs[0].childNodes[_ULs[0].ItemIndex].HideValue;
                    _TextBoxControl.value 
= _Key;
                    _TextBoxControl._Text 
= _Text;
                    _TextBoxControl._Value 
= _Key;
                    _TextBoxControl._HideValue 
= _HideValue;
                    _Completed 
= false;

                }
                
//下方向键
                else if (_KeyCode == 40) {

                    
if (_ULs[0].ItemIndex == -1) {
                        _ULs[
0].ItemIndex = _ULs[0].ItemsCount - 1;
                    }
                    _ULs[
0].childNodes[_ULs[0].ItemIndex].className = 'normal';
                    _ULs[
0].ItemIndex++;
                    
if (_ULs[0].ItemIndex >= _ULs[0].ItemsCount) {
                        _ULs[
0].ItemIndex = 0;
                    }
                    _ULs[
0].childNodes[_ULs[0].ItemIndex].className = 'selected';

                    _Completed 
= true;

                    
var _Key = _ULs[0].childNodes[_ULs[0].ItemIndex].Key;
                    
var _Text = _ULs[0].childNodes[_ULs[0].ItemIndex].innerText;
                    
var _HideValue = _ULs[0].childNodes[_ULs[0].ItemIndex].HideValue;
                    _TextBoxControl.value 
= _Key;

                    _TextBoxControl._Text 
= _Text;
                    _TextBoxControl._Value 
= _Key;
                    _TextBoxControl._HideValue 
= _HideValue;

                    _Completed 
= false;

                }
                
else if (_KeyCode == 13) {
                    _Container.style.display 
= 'none';
                    eval(clientid 
+ '.OnCompleting()');
                    
return false;
                }

            });

        }
        
if (document.compatMode == 'CSS1Compat') {
            _Container.style.width 
= _TextBoxControl.clientWidth;
        }
        
else if (document.compatMode == 'BackCompat') {
            _Container.style.width 
= _TextBoxControl.clientWidth + 3;
        }

        
this.SetContainerAttribute();

        _Container.style.display 
= 'none';

        window.attachEvent(
'onresize'function() {
            eval(clientid 
+ '.SetContainerAttribute()');
        });

    }
    
//设置容器的定位属性
    this.SetContainerAttribute = function() {
        
var _TextBoxControl = $(this._TextBoxControlID);
        
var _Container = $(this._ID);

        _Container.style.height 
= 'auto';
        _Container.style.left 
= PageX(_TextBoxControl);
        _Container.style.top 
= PageY(_TextBoxControl) + _TextBoxControl.clientHeight + 6;

    }

    
//服务器回调方法
    this.CallBack = function(result, id) {
        
if (id) {
            
var _TextBox = eval(clientid + '._TextBoxControlID');
            
if ($(_TextBox).value.Trim().length == 0) {
                setTimeout(
function() {
                    id.innerHTML 
= '';
                    id.style.display 
= 'none';
                }, 
1);
            }

            
if (result) {
                id.innerHTML 
= result;
                id.style.display 
= '';

                eval(clientid 
+ '.AttachEvents()');
            }
            
else {
                id.innerHTML 
= '';
                id.style.display 
= 'none';

            }
        }
    }

    
//给UL对象添加事件
    this.AttachEvents = function() {
        
var _Container = $(this._ID);
        
var _TextBoxControl = $(this._TextBoxControlID);

        
var _ULs = _Container.getElementsByTagName('ul');

        
if (_ULs.length <= 0return;

        _ULs[
0].onmousedown = function() { window.event.cancelBubble = true; }

        
for (var i = 0; i < _ULs[0].childNodes.length; i++) {
            
//onclick事件
            _ULs[0].childNodes[i].onclick = function() {
                _Completed 
= true;
                _TextBoxControl.value 
= this.Key;

                _Container.style.display 
= 'none';
                _TextBoxControl.focus();
                
var _R = _TextBoxControl.createTextRange();
                _R.moveStart(
'character', _TextBoxControl.value.length);
                _R.collapse();
                _R.select();

                _TextBoxControl._Text 
= this.innerText;
                _TextBoxControl._Value 
= this.Key;
                _TextBoxControl._HideValue 
= this.HideValue;
                _Completed 
= false;

                eval(clientid 
+ '.OnCompleting()');
            }
            
//onmouseover事件
            _ULs[0].childNodes[i].onmouseover = function() {
                
if (_ULs[0].ItemIndex != -1)
                    _ULs[
0].childNodes[_ULs[0].ItemIndex].className = 'normal';

                _ULs[
0].ItemIndex = this.Index;

                
this.className = 'selected';
            }
            
//onmouseout事件
            _ULs[0].childNodes[i].onmouseout = function() {
                
this.className = 'normal';
            }
        }
    }

    
//异步调用
    this.AsyncDataBind = function() {
        
var _TextBoxControl = $(this._TextBoxControlID);
        
var _Container = $(this._ID);

        
if (this._AsyncClientScript) {
            
var args = _TextBoxControl.value;
            eval(
this._AsyncClientScript);
        }
    }

    
//隐藏
    this.Hide = function() {
        $(
this._ID).style.display = 'none';
    }

    
this.GetText = function(html) {
        
var node = document.createElement('div');
        node.innerHTML 
= html;

        
return node.innerText;
    }

    
this.OnCompleting = function() {

        
var _TextBoxControl = $(this._TextBoxControlID);
        
if (_TextBoxControl) {
            
var _t = _TextBoxControl._Text;
            
var _v = _TextBoxControl._Value;
            
var _hv = _TextBoxControl._HideValue;
            
var obj = {};
            obj.Text 
= _t;
            obj.Value 
= _v;
            obj.HideValue 
= _hv;
            
this.OnCompleted(obj);
        }
    }

    
this.OnCompleted = function(obj) {
        
var _JS = 'if(typeof(' + clientid + '_OnCompleted)=="function") ' + clientid + '_OnCompleted(obj.Text,obj.Value,obj.HideValue);';
        eval(_JS);
    }


4.服务端类设计
image


AutoCompleteExtender继承WebControl;实现ICallbackEventHandler,实现ICallbackEventHandler接口实现控件的异步调用功能;
ICallbackEventHandler的接口包含两个方法;

    public string GetCallbackResult()
    {
        
//返回到客户端的文本
        return “………”;
    }

    
public void RaiseCallbackEvent(string eventArgument)
    {
         
//服务端处理代码
    }

关联的TextBox控件ID

    public string GetCallbackResult()
    {
        
//返回到客户端的文本
        return “………”;
    }

    
public void RaiseCallbackEvent(string eventArgument)
    {
         
//服务端处理代码
    }

设计器的类型转换器

代码
/// <summary>
/// 目标TextBox控件ID 类型转换器
/// </summary>
public class AutoCompleteTargetControlConverter : ControlIDConverter
{
    
protected override bool FilterControl(Control control)
    {
        
if (control is TextBox)
            
return true;
        
else
        {
            
return false;
        }

    }

TargetControl属性可以通过一个DropDownList控件选择WebForm中的TextBox控件;

image

添加控件在客户端的唯一标识属性,ClientID+字符串“_obj”

/// <summary>
/// 获取在客户端的对象唯一标识
/// </summary>
[Browsable(false)]
[Description(
"获取在客户端的对象唯一标识")]
public string ClientObjectID
{
    
get
    {
        
return this.ClientID + "_obj";
    }
}

 

添加面板展开时的透明属性,取值为1至100;


//面板透明度
private int m_Opacity = 100;

/// <summary>
/// 设置或获取对话框移动时的透明度,取值1至100
/// </summary>
[DefaultValue("100")]
[Description(
"设置或获取对话框移动时的透明度,取值1至100")]
public int Opacity
{
    
get
    {
        
return m_Opacity;
    }
    
set
    {
        
if (value > 0 && value <= 100)
        {
            m_Opacity 
= value;
        }
        
else
        {
            
throw new Exception("取值为1至100.");
        }
    }
}


事件(控件的关键事件)

/// <summary>
/// 异步数据绑定事件
/// </summary>
[Description("异步数据绑定事件")]
public event AsyncDataBindEventHandler AsyncDataBind;

/// <summary>
/// 异步数据绑定事件执行方法
/// </summary>
/// <param name="e"></param>
protected virtual void OnAsyncDataBind(AsyncDataBindEventArgs e)
{
    
if (AsyncDataBind != null)
    {
        AsyncDataBind(
this, e);
    }
}


更改控件的默认的tag标记

/// <summary>
/// 构造器
/// </summary>
public AutoCompleteExtender()
    : 
base(HtmlTextWriterTag.Div)
{}


有些属性是必须设置的,在运行时检查控件的属性,TargetControl属性必须设置,未设置时抛出异常;

代码
/// <summary>
/// 检查控件属性的正确性
/// </summary>
private void CheckValidate()
{
    
if (TargetControl == string.Empty)
    {
        
throw new Exception("TargetControl未赋值");
    }

    
if (Page.FindControl(TargetControl) == null)
    {
        
throw new Exception(string.Format("未找到控件ID为{0}的文本框", TargetControl));
    }
}

控件的呈现

/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnPreRender(EventArgs e)
{
    
//检查属性
    CheckValidate();
    
//注册JavaScript脚本
    RegisterJavaScript();
    
//注册CSS样式
    RegisterCSS();
    
base.OnPreRender(e);

为控件添加样式特性

/// <summary>
///
/// </summary>
/// <param name="writer"></param>
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
    UnitConverter _u 
= new UnitConverter();
    
this.Height = (Unit)_u.ConvertFrom("100%");
    
base.AddAttributesToRender(writer);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, 
"suggest-container");
    writer.AddStyleAttribute(HtmlTextWriterStyle.Filter, 
string.Format("Alpha(Opacity={0})",this.m_Opacity));
}


 

控件的内容是在异步执行时才呈现,所以在第一次呈现时只呈现出一个空的框架Tag

/// <summary>
///
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer)
{
    
base.RenderBeginTag(writer);
    
base.RenderEndTag(writer);
}


 

根据列表项呈现控件内容

/// <summary>
///
/// </summary>
/// <param name="writer"></param>
protected override void RenderContents(HtmlTextWriter writer)
{
    
if (Items.Count > 0)
    {
        writer.AddAttribute(
"ItemsCount", Items.Count.ToString());
        writer.AddAttribute(
"ItemIndex""-1");
        writer.RenderBeginTag(HtmlTextWriterTag.Ul);
        
int _Index = 0;
        
foreach (AutoCompleteListItem _Item in Items)
        {
            writer.AddAttribute(
"Index", (_Index++).ToString());
            writer.AddAttribute(
"Key", _Item.Value);
            writer.AddAttribute(
"HideValue", _Item.HideValue);
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.Write(_Item.Text);
            writer.RenderEndTag();
            writer.RenderEndTag();
        }
        writer.RenderEndTag();
    }
}

 


ICallbackEventHandler接口实现方法

public string GetCallbackResult()
{
    StringBuilder sb 
= new StringBuilder();
    System.IO.StringWriter tw 
= new System.IO.StringWriter(sb);
    HtmlTextWriter htw 
= new HtmlTextWriter(tw);
    
this.RenderContents(htw);
    m_InnerHTML 
= sb.ToString();
    
return m_InnerHTML;
}

public void RaiseCallbackEvent(string eventArgument)
{
    
//调用异步处理事件
    if (AsyncDataBind != null)
    {
        AsyncDataBindEventArgs _Arg 
= new AsyncDataBindEventArgs();
        _Arg.ObjectArgs 
= eventArgument;
        m_Args 
= eventArgument;
        OnAsyncDataBind(_Arg);
    }
}


源码下载:DSKJ.Controls.DLL  未完待续