修正导致.Net 4.0编译通不过的问题
Stone authored at 2012-05-26 17:55:38
7.59 KiB
X_NET20
using System;
using System.Collections;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using DropDownList_Old = System.Web.UI.WebControls.DropDownList;

namespace XControl
{
    /*
     * 逻辑分析:
     * 选择项、绑定列表项 两者的先后顺序影响着控件的工作
     * AppendDataBoundItems=true
     *  在这种情况下,多次绑定后,会造成叠加,一般不使用。绑定前和绑定后一样,区别只是选择项加在开头还是结尾而已
     *      绑定前:如果静态Items没有,往里面加一个项,绑定的时候保留静态项。
     *      绑定后:每次绑定都会保留原来的项,这就导致了一直增加重复的选项!
     *
     * AppendDataBoundItems=false
     *      绑定前:如果静态Items没有,往里面加一个项,绑定的时候会清空静态项,如果绑定的列表项刚好没有选择项,会抛异常!
     *              解决方法,绑定前一刻,人为清空所有项,添加选择项,绑定完成后视情况决定是否删除。
     *      绑定后:如果列表项中没有选择项,就会多一个异常项。正确!
     *
     * */

    /*
     * 2012-05-22 by netwjx :
     *
     * 针对SelectedValue赋值和DataBind()可能会抛出异常的现象修改, 理想的策略是
     *
     * 无论任何时候 给SelectedValue赋值, 都确保不抛出异常, 而是选择添加异常项或缓存设置的值,暂时使用旧值
     * 无论任何时候 DataBind()时, 都确保不抛出异常, 而是选择添加异常项或尝试使用缓存的值或者使用默认第一项,或者null
     * TODO 尚未实现SelectedIndex赋值的处理
     * */

    /// <summary>下拉列表。绑定时,如果没有对应的选择项,则自动加上。</summary>
    /// <remarks>
    /// 注意:在实践中发现DropDownList.SelectedIndex有可能修改this.Items[0].Selected 为true
    ///
    /// 进而会导致错误的结果,所以尽量不在这个控件内部使用SelectedIndex SelectedValue属性
    /// </remarks>
    [ToolboxData("<{0}:DropDownList runat=\"server\"> </{0}:DropDownList>")]
    public class DropDownList : DropDownList_Old
    {
        private const String ExceptionString = "无效的值:{0}";

        /// <summary>
        /// 当前实际选中项的值,没有值将返回null,而不是SelectedValue会修改为第一项默认值
        /// </summary>
        public string RealSelectedValue
        {
            get
            {
                for (int i = 0; i < Items.Count; i++)
                {
                    var item = Items[i];
                    if (item.Selected) return item.Value;
                }
                return null;
            }
        }

        /// <summary>
        /// 是否不添加异常项
        /// </summary>
        [WebCategory("专用属性 "), DefaultValue(false),
        Description("当SelectedValue的值是不存在的项时会添加一个异常的项, 这个属性控制是否不要添加这个项, 注意: 最终仍旧不存在时将会默认选中第一项")]
        public bool NoExceptionItem
        {
            get
            {
                object val = ViewState["NoExceptionItem"];
                if (val != null)
                {
                    return (bool)val;
                }
                return false;
            }
            set
            {
                ViewState["NoExceptionItem"] = value;
            }
        }

        private bool hasExceptItem;
        private string cachedSelectedValue;

        /// <summary>已重载。加上未添加到列表的项。</summary>
        public override string SelectedValue
        {
            get
            {
                return base.SelectedValue;
            }
            set
            {
                RemoveExceptItem();
                cachedSelectedValue = null;
                try
                {
                    base.SelectedValue = value;
                    if (value != null && base.SelectedValue != value) throw new ArgumentOutOfRangeException();
                }
                catch (ArgumentOutOfRangeException)
                {
                    cachedSelectedValue = value;
                    if (NoExceptionItem)
                    {
                        // 记录真实值后跳出
                        return;
                    }
                    else
                    {
                        // 列表项中并没有该选项,自动加入,并打上异常标识
                        ClearSelection();
                        Items.Insert(0, new ListItem(string.Format(ExceptionString, value), value) { Selected = true });
                        hasExceptItem = true;
                        base.SelectedValue = value;
                    }
                }
            }
        }

        private void RemoveExceptItem()
        {
            if (hasExceptItem && cachedSelectedValue != null)
            {
                // 尝试移除上次调用时产生的异常项
                var item = Items.FindByValue(cachedSelectedValue);
                if (item != null)
                {
                    Items.Remove(item);
                }
            }
        }

        /// <summary>绑定数据</summary>
        /// <param name="dataSource"></param>
        protected override void PerformDataBinding(IEnumerable dataSource)
        {
            string real = cachedSelectedValue ?? RealSelectedValue;
            RemoveExceptItem();
            try
            {
                base.PerformDataBinding(dataSource);
            }
            catch (ArgumentException)
            {
                // SelectedValue会在抛出异常时改变为第一项的Value
            }
            if (!string.IsNullOrEmpty(real) && SelectedValue != real)
            {
                var item = Items.FindByValue(real);
                if (item != null)
                {
                    ClearSelection();
                    item.Selected = true;
                }
                else
                {
                    if (NoExceptionItem)
                    {
                        if (Items.Count > 0)
                        {
                            ClearSelection();
                            Items[0].Selected = true;
                        }
                    }
                    else
                    {
                        ClearSelection();
                        item = new ListItem(string.Format(ExceptionString, real), real) { Selected = true };
                        Items.Insert(0, item);
                    }
                }
            }
        }

        //protected override void RenderContents(HtmlTextWriter writer)
        //{
        //    base.RenderContents(writer);
        //}

        private Boolean selecting = false;

        /// <summary>已重载。避免绑定时重入该方法</summary>
        protected override void PerformSelect()
        {
            if (selecting) return;

            selecting = true;
            if (!this.AppendDataBoundItems)
            {
                if (this.DataSource != null || !string.IsNullOrEmpty(this.DataSourceID))
                {
                    // DropDownList在绑定时,如果数据源返回null,它将不做任何动作,而我们一般习惯清空
                    this.Items.Clear();
                }
            }
            base.PerformSelect();
            selecting = false;
        }
    }
}