我写了一个控件Inherits TextBox,里面有一个复杂属性Tip,但每次postback的时候都说加载viewstate失败,除非我在!postback的情况下给Tip.xxx赋值.
下面我贴出代码,我已经搞了一天了,搞不出什么原因。
JTextBox控件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Madou.WebControls
{
[DefaultProperty("Text")]
[ParseChildren(true), PersistChildren(false)]
[ToolboxData("<{0}:JTextBox runat=server></{0}:JTextBox>")]
public class JTextBox : System.Web.UI.WebControls.TextBox
{
public JTextBox()
{
BackColor = System.Drawing.Color.Beige;
}
"Private"#region"Private"
private ClipTipComponent _tip;
#endregion
"定义属性"#region"定义属性"
[Bindable(true)]
[Category("Behavior")]
[Description("Tip")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[NotifyParentProperty(true)]
public virtual ClipTipComponent Tip
{
get
{
if (_tip == null)
{
_tip = new ClipTipComponent();
if (IsTrackingViewState)
{
((IStateManager)_tip).TrackViewState();
}
}
return _tip;
}
}
#endregion
"override"#region"override"
state management#region state management
protected override void LoadViewState(object savedState)
{
Pair p = savedState as Pair;
//HttpContext.Current.Response.Write(savedState.GetType().ToString());
// HttpContext.Current.Response.Write(savedState==null);
// HttpContext.Current.Response.End();
if (p != null)
{
base.LoadViewState(p.First);
((IStateManager)Tip).LoadViewState(p.Second);
}
else
{
base.LoadViewState(savedState);
}
}
protected override object SaveViewState()
{
object baseState = base.SaveViewState();
object thisState = null;
if (Tip != null)
{
thisState = ((IStateManager)Tip).SaveViewState();
}
if (thisState != null)
{
return new Pair(baseState, thisState);
}
else
{
return baseState;
}
}
protected override void TrackViewState()
{
if (_tip != null)
{
((IStateManager)_tip).TrackViewState();
}
base.TrackViewState();
}
#endregion
protected override void OnPreRender(EventArgs e)
{
Tip.RegisterJs(this);
base.OnPreRender(e);
}
protected override void OnLoad(EventArgs e)
{
if (!Page.IsPostBack)
{
Tip.CloseText = "关闭";
}
base.OnLoad(e);
}
#endregion
}
}
Tip属性类
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Madou.WebControls.Converter;
namespace Madou.WebControls
{
[TypeConverter(typeof(ClipTipComponentConverter))]
public class ClipTipComponent : IStateManager
{
"Private"#region"Private"
private bool _isTrackingViewState;
private StateBag _viewState;
#endregion
public ClipTipComponent() { }
"属性"#region"属性"
[Category("Behavior")]
[DefaultValue(false)]
[Description("是否启用Ajax效果")]
[NotifyParentProperty(true)]
public bool EnabledAjax
{
get
{
return ViewState["EnabledAjax"] == null ? false : Convert.ToBoolean(ViewState["EnabledAjax"]);
}
set
{
ViewState["EnabledAjax"] = value;
}
}
/**//// <summary>
/// 是否使用Tip功能
/// </summary>
[Category("Behavior")]
[DefaultValue(true)]
[Description("是否使用ClipTip功能")]
[NotifyParentProperty(true)]
public bool EnabledClipTip
{
get
{
return ViewState["EnabledClipTip"] == null ? true : Convert.ToBoolean(ViewState["EnabledClipTip"]);
}
set
{
ViewState["EnabledClipTip"] = value;
}
}
/**//// <summary>
/// // The width of the clueTip
/// </summary>
[Category("Behavior")]
[DefaultValue("275")]
[Description("The width of the clueTip")]
[NotifyParentProperty(true)]
public string TipWidth
{
get
{
return ViewState["TipWidth"] == null ? "275" : ViewState["TipWidth"].ToString();
}
set
{
ViewState["TipWidth"] = value;
}
}
/**//// <summary>
/// // The height of the clueTip.
/// </summary>
[Category("Behavior")]
[DefaultValue("auto")]
[Description("The height of the clueTip")]
[NotifyParentProperty(true)]
public string TipHeigth
{
get
{
return ViewState["TipHeigth"] == null ? "auto" : ViewState["TipHeigth"].ToString();
}
set
{
ViewState["TipHeigth"] = value;
}
}
/**//// <summary>
/// A character used to split the title attribute into the clueTip title and divs
/// </summary>
[Category("Behavior")]
[DefaultValue("")]
[Description("A character used to split the title attribute into the clueTip title and divs")]
[NotifyParentProperty(true)]
public string SplitTitle
{
get
{
return ViewState["SplitTitle"] == null ? string.Empty : Convert.ToString(ViewState["SplitTitle"]);
}
set
{
ViewState["SplitTitle"] = value;
}
}
/**//// <summary>
/// show title bar of the clueTip, even if title attribute not set
/// </summary>
[Category("Behavior")]
[DefaultValue(true)]
[Description("show title bar of the clueTip, even if title attribute not set")]
[NotifyParentProperty(true)]
public bool ShowTitle
{
get
{
return ViewState["ShowTitle"] == null ? true : Convert.ToBoolean(ViewState["ShowTitle"]);
}
set
{
ViewState["ShowTitle"] = value;
}
}
/**//// <summary>
/// his is also used for a "directional" class on the same div, depending on where the clueTip is in relation to the invoking element. The class appears in the form of 'cluetip-' + direction + cluetipClass. this allows you to create your own clueTip theme in a separate CSS file or use one of the three pre-packaged themes: default, jtip, or rounded.
/// </summary>
[Category("Behavior")]
[DefaultValue("default")]
[Description("default, jtip, or rounded")]
[NotifyParentProperty(true)]
[Bindable(false)]
[TypeConverter(typeof(Madou.WebControls.Converter.ClueTipClassConverter))]
public string ClueTipClass
{
get
{
return ViewState["ClueTipClass"] == null ? "default" : Convert.ToString(ViewState["ClueTipClass"]);
}
set
{
ViewState["ClueTipClass"] = value;
}
}
/**//// <summary>
/// class applied to the invoking element onmouseover and removed onmouseout
/// </summary>
[Category("Behavior")]
[DefaultValue("")]
[Description("class applied to the invoking element onmouseover and removed onmouseout")]
[NotifyParentProperty(true)]
public string HoverClass
{
get
{
return ViewState["HoverClass"] == null ? string.Empty : Convert.ToString(ViewState["HoverClass"]);
}
set
{
ViewState["HoverClass"] = value;
}
}
/**//// <summary>
/// if true, displays arrow on appropriate side of clueTip. more info below [8]
/// </summary>
[Category("Behavior")]
[DefaultValue(false)]
[Description("if true, displays arrow on appropriate side of clueTip")]
[NotifyParentProperty(true)]
public bool Arrows
{
get
{
return ViewState["Arrows"] == null ? false : Convert.ToBoolean(ViewState["Arrows"]);
}
set
{
ViewState["Arrows"] = value;
}
}
[Category("Behavior")]
[DefaultValue(true)]
[Description("是否有Shadow")]
[NotifyParentProperty(true)]
public bool DropShadow
{
get
{
return ViewState["DropShadow"] == null ? true : Convert.ToBoolean(ViewState["DropShadow"]);
}
set
{
ViewState["DropShadow"] = value;
}
}
[Category("Behavior")]
[DefaultValue(6)]
[Description("Shadow Steps")]
[NotifyParentProperty(true)]
public int DropShadowSteps
{
get
{
return ViewState["DropShadowSteps"] == null ? 6 : Convert.ToInt32(ViewState["DropShadowSteps"]);
}
set
{
ViewState["DropShadowSteps"] = value;
}
}
/**//// <summary>
/// 如果ture,鼠标点击close才关闭
/// </summary>
[Category("Behavior")]
[DefaultValue(false)]
[Description("如果ture,鼠标点击close才关闭")]
[NotifyParentProperty(true)]
public bool Sticky
{
get
{
return ViewState["Sticky"] == null ? false : Convert.ToBoolean(ViewState["Sticky"]);
}
set
{
ViewState["Sticky"] = value;
}
}
/**//// <summary>
/// close when clueTip is moused out
/// </summary>
[Category("Behavior")]
[DefaultValue(false)]
[Description("close when clueTip is moused out")]
[NotifyParentProperty(true)]
public bool MouseOutClose
{
get
{
return ViewState["MouseOutClose"] == null ? false : Convert.ToBoolean(ViewState["MouseOutClose"]);
}
set
{
ViewState["MouseOutClose"] = value;
}
}
/**//// <summary>
/// 'hover'OR 'click', // set to 'click' to force user to click to show clueTip
/// </summary>
[Category("Behavior")]
[DefaultValue("hover")]
[Description("hover'OR 'click'")]
[NotifyParentProperty(true)]
[TypeConverter(typeof(Madou.WebControls.Converter.ActivationConverter))]
public string Activation
{
get
{
return ViewState["Activation"] == null ? "hover" : ViewState["Activation"].ToString();
}
set
{
ViewState["Activation"] = value;
}
}
/**//// <summary>
/// if true, and activation is not 'click', then clicking on a clueTipped link will take user to
/// the link's href, even if href and tipAttribute are equal
/// </summary>
[Category("Behavior")]
[DefaultValue(false)]
[Description("if true, and activation is not 'click'")]
[NotifyParentProperty(true)]
public bool ClickThrough
{
get
{
return ViewState["ClickThrough"] == null ? false : Convert.ToBoolean(ViewState["ClickThrough"]);
}
set
{
ViewState["ClickThrough"] = value;
}
}
/**//// <summary>
/// // if true, clueTip will track mouse movement (experimental)
/// </summary>
[Category("Behavior")]
[DefaultValue(false)]
[Description("if true, clueTip will track mouse movement (experimental)")]
[NotifyParentProperty(true)]
public bool Tracking
{
get
{
return ViewState["Tracking"] == null ? false : Convert.ToBoolean(ViewState["Tracking"]);
}
set
{
ViewState["Tracking"] = value;
}
}
/**//// <summary>
/// close clueTip on a timed delay (experimental)
/// </summary>
[Category("Behavior")]
[DefaultValue(0)]
[Description("close clueTip on a timed delay (experimental)")]
[NotifyParentProperty(true)]
public int DelaydClose
{
get
{
return ViewState["DelaydClose"] == null ? 0 : Convert.ToInt32(ViewState["DelaydClose"]);
}
set
{
ViewState["DelaydClose"] = value;
}
}
/**//// <summary>
/// location of close text for sticky cluetips; can be 'top' or 'bottom' or 'title'
/// </summary>
[Category("Behavior")]
[DefaultValue("top")]
[Description("location of close text for sticky cluetips; can be 'top' or 'bottom' or 'title'")]
[NotifyParentProperty(true)]
[TypeConverter(typeof(Madou.WebControls.Converter.ClosePositionConverter))]
public string ClosePosition
{
get
{
return ViewState["ClosePosition"] == null ? "top" : ViewState["ClosePosition"].ToString();
}
set
{
ViewState["ClosePosition"] = value;
}
}
[Category("Behavior")]
[DefaultValue("关闭")]
[Description("Close Text")]
[NotifyParentProperty(true)]
public string CloseText
{
get
{
return ViewState["CloseText"] == null ? "关闭" : ViewState["CloseText"].ToString();
}
set
{
ViewState["CloseText"] = value;
}
}
/**//// <summary>
/// 字符数
/// </summary>
[Category("Behavior")]
[DefaultValue(0)]
[Description("字符数")]
[NotifyParentProperty(true)]
public int Truncate
{
get
{
return ViewState["Truncate"] == null ? 0 : Convert.ToInt32(ViewState["Truncate"]);
}
set
{
ViewState["Truncate"] = value;
}
}
#endregion
"HELP#region"HELP
protected virtual StateBag ViewState
{
get
{
if (_viewState == null)
{
_viewState = new StateBag(true);
if (_isTrackingViewState)
((IStateManager)_viewState).TrackViewState();
}
return _viewState;
}
}
#endregion
"重写的方法"#region"重写的方法"
public override string ToString()
{
return ToString(System.Globalization.CultureInfo.InvariantCulture);
}
public string ToString(System.Globalization.CultureInfo culture)
{
return TypeDescriptor.GetConverter(GetType()).ConvertToString(null, culture, this);
}
#endregion
IStateManager Members#region IStateManager Members
bool IStateManager.IsTrackingViewState
{
get { return _isTrackingViewState; }
}
void IStateManager.LoadViewState(object state)
{
if (state != null)
{
((IStateManager)ViewState).LoadViewState(state);
}
}
object IStateManager.SaveViewState()
{
object savedState = null;
if (_viewState != null)
{
savedState = ((IStateManager)_viewState).SaveViewState();
}
else{
// HttpContext.Current.Response.Write("saveState1=false");
}
return savedState;
}
void IStateManager.TrackViewState()
{
_isTrackingViewState = true;
if (_viewState != null)
{
((IStateManager)_viewState).TrackViewState();
}
}
#endregion
internal void SetDirty()
{
_viewState.SetDirty(true);
}
internal void RegisterJs(Control target, string clientId)
{
if (target is WebControl)
{
string tooltip = (target as WebControl).ToolTip;
if (string.IsNullOrEmpty(tooltip.Trim()))
{
if (!EnabledAjax)
return;
}
}
if (EnabledClipTip)
{
JavaScriptWriter w = new JavaScriptWriter();
w.AddLine("$(function()");
w.OpenBlock();
if (!EnabledAjax)
{
if (string.IsNullOrEmpty(SplitTitle))
SplitTitle = "|";
w.AddLine("$('#" + clientId + "').cluetip({splitTitle: '" + SplitTitle + "'");
}
else
{
w.AddLine("$('#" + clientId + "').cluetip({splitTitle: ''");
}
w.AddJSONItem("cluetipClass", ClueTipClass);
w.AddJSONItem("hoverClass", HoverClass);
w.AddJSONItem("arrows", Arrows);
w.AddJSONItem("dropShadow", DropShadow);
w.AddJSONItem("dropShadowSteps", DropShadowSteps);
w.AddJSONItem("sticky", Sticky);
w.AddJSONItem("mouseOutClose", MouseOutClose);
w.AddJSONItem("activation", Activation);
w.AddJSONItem("clickThrough", ClickThrough);
w.AddJSONItem("tracking", Tracking);
w.AddJSONItem("delayedClose", DelaydClose);
w.AddJSONItem("closePosition", ClosePosition);
w.AddJSONItem("width", TipWidth);
w.AddJSONItem("height", TipHeigth);
w.AddJSONItem("closeText", CloseText);
w.AddJSONItem("truncate", Truncate);
w.AddLine("});");
w.CloseBlock();
w.AddLine(");");
target.Page.ClientScript.RegisterClientScriptBlock(target.GetType(), clientId, w.ToString(), false);
}
}
internal void RegisterJs(Control target)
{
string clientId = target.ClientID;
RegisterJs(target, clientId);
}
}
}
出现的问题,一定要在!PostBack给Tip.xxx赋值,否则
出现viewstate不能正常加载的错误提示(如果给JTextBox的Text赋值的话),请问这个是什么原因 !
bug已经发现,原来System.Web.UI.WebControls.TextBox的SaveViewState()已经是Pair