Bug reports Found a bug? Post it here please.
Version 4.x > Bug reports > Gridview Viewstate in custom webpart not working View modes: 
User avatar
Member
Member
matyas.boros-gmail - 8/21/2009 12:00:10 PM
   
Gridview Viewstate in custom webpart not working
Hi,
im new to Kentico, and just intalled 4.0.
I need to move the functionality of my old site to kentico, and i decided to move all pages to usercontrols, and just add them as webparts in kentico. (create a portal engine template, and add the webpart in the cms desk => page=> design tab)
My probelm is that it seems that the viewstate for my gridviews is gone in these webparts. Is this a known issue, or did i just miss something obvious?

An exapmle code that works fine on a normal aspx page (change the base class to UserControl), but not when added as a webpart to kentico:

test.ascx:

<%@ Control Language="C#" AutoEventWireup="true" Inherits="testsite.UserControls.test"
CodeBehind="test.ascx.cs" %>
<asp:Label runat="server" ID="lab" />
<asp:GridView runat="server" ID="gv" AutoGenerateColumns="false" EnableViewState="true"
onrowcommand="gv_RowCommand">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" ID="btn" CommandName="print" CommandArgument='<%#Container.DataItem %>' Text='<%#Container.DataItem %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button runat="server" ID="btn" Text="hitme i dont do anything"
onclick="btn_Click" />

test.ascx.cs:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;

namespace testsite.UserControls
{
public partial class test : UserControlBase
{

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
gv.DataSource = new List<int> { 1, 2, 3 };
gv.DataBind();
ViewState["toto"] = "44";
}
else
{
}
lab.Text = ViewState["toto"] as string;

}

protected void gv_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "print")
lab.Text = e.CommandArgument as string;
else
lab.Text = "unknown command";
}

protected void btn_Click(object sender, EventArgs e)
{

}
}
}


the problem is only witrh the gridview viewstate, as the label does have the correct text on the button hitting.

Any help would be appreciated.

Kind regards

Matyas

User avatar
Kentico Support
Kentico Support
kentico_jurajo - 9/1/2009 2:08:29 AM
   
RE:Gridview Viewstate in custom webpart not working
Hi,

I know that Helena sent you a message already, I will post the solution here for other users as well:

You could save the datasource to the ViewState property. Please try this code:

using System;
using System.Web;
using System.Web.UI;

using CMS.PortalControls;
using CMS.GlobalHelper;
using CMS.Controls;
using CMS.ISearchEngine;
using System.Web.UI.WebControls;
using System.Collections.Generic;

public partial class CMSWebParts_my_User_Control : CMSAbstractWebPart
{

List<int> myValues = new List<int>();

//protected void Page_Load(object sender, EventArgs e)
//protected void Page_Load(object sender, EventArgs e)
//{ }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (IsPostBack)
{
gv.DataSource = ViewState["toto"];
gv.DataBind();
}

else
{
ViewState["toto"] = myValues;
}

}


protected void gv_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "print")
lab.Text = e.CommandArgument as string;
else
lab.Text = "unknown command";
}

protected void btn_Click(object sender, EventArgs e)
{
lab.Text = ViewState["toto"] as string;
}



protected void SetupControl()
{

myValues.Add(1);
myValues.Add(2);
myValues.Add(3);


gv.DataSource = myValues;
gv.DataBind();


}

protected override void OnInit(EventArgs e)
{
base.OnInit(e);

if (!IsPostBack)
{
SetupControl();
}

}

}


Best Regards,
Juraj Ondrus

User avatar
Member
Member
matyas.boros-gmail - 9/1/2009 5:18:15 AM
   
RE:Gridview Viewstate in custom webpart not working
Hi,

yes i got the reply from Helena, and she suggested the above code. Obviously it worked, but in some cases i bound the gridview to object lists, that are not serializable. In this case i would need to make it serializable first, and thensave it to the viewstate. This in a case of a usercontrol with multiple gridviews might be a lot of work, or if you diddnt write the original usercontrol, even too much.
The best workaround i found was to load the usercontrol dynamically from another one, as it is done in the general webpart (usercontrol.ascx). That one lacks the properties, but the code can be simply modified for this purpose. I used the below code:

In app_code:
WrapperBase.cs

using System;
using System.Web.UI;

using CMS.PortalControls;
using CMS.GlobalHelper;
using System.Collections.Generic;
using System.Reflection;
using System.Web.UI.WebControls;

public abstract class WrapperBase : CMSAbstractWebPart
{

// key is the public UC property name, value is the CMS property name
protected virtual Dictionary<string, string> StringProperties { get { return new Dictionary<string, string>(); } }

// key is the public UC property name, value is the CMS property name
protected virtual Dictionary<string, string> BoolProperties { get { return new Dictionary<string, string>(); } }

protected abstract string UserControlPath { get; }

protected abstract Label _lblError { get; }

/// <summary>
/// Content loaded event handler
/// </summary>
public override void OnContentLoaded()
{
base.OnContentLoaded();
SetupControl();
}


/// <summary>
/// Initializes the control properties
/// </summary>
protected void SetupControl()
{
if (this.StopProcessing)
{
// Do not process
}
else
{
}
}


/// <summary>
/// Loads the user control
/// </summary>
protected void LoadControl()
{
if (!string.IsNullOrEmpty(UserControlPath))
{
try
{
Control ctrl = Page.LoadControl(UserControlPath);
ctrl.ID = "userControlElem";
Controls.Add(ctrl);
string temp = "";
foreach (string key in StringProperties.Keys)
{
PropertyInfo pi = ctrl.GetType().GetProperty(key, BindingFlags.Instance | BindingFlags.Public);
if (pi != null)
pi.SetValue(ctrl, ValidationHelper.GetString(this.GetValue(StringProperties[key]), temp), null);
}
foreach (string key in BoolProperties.Keys)
{
PropertyInfo pi = ctrl.GetType().GetProperty(key, BindingFlags.Instance | BindingFlags.Public);
if (pi != null && PartInstance != null)
pi.SetValue(ctrl, Boolean.Parse((string)PartInstance.GetValue(BoolProperties[key])), null);
}

}
catch (Exception ex)
{
_lblError.Text = "[" + this.ID + "] " + ResHelper.GetString("WebPartUserControl.ErrorLoad") + ": " + ex.Message;
_lblError.ToolTip = ex.StackTrace;
_lblError.Visible = true;
}
}
}


protected override void OnInit(EventArgs e)
{
base.OnInit(e);

// The control must load after OnInit to properly load its viewstate
LoadControl();
}
}


This is the modification of the Usercontrol.ascx.cs file, that automatically sets the CMS properties to public usercontrol properties. I only used bool and string but you can easily add other ones if you need.

Then the code looks really simple:

testcontrol.ascx:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="~/CMSWebParts/Wrappers/testcontrol.ascx.cs"
Inherits="CMSWebParts_Wrapper_testcontrol" %>
<asp:Label ID="lblError" runat="server" EnableViewState="false" Visible="false" />

and testcontrol.ascx.cs

using System;
using System.Web.UI;

using CMS.PortalControls;
using CMS.GlobalHelper;
using System.Collections.Generic;
using System.Web.UI.WebControls;

public partial class CMSWebParts_Wrapper_testcontrol : WrapperBase
{
protected override Dictionary<string, string> StringProperties
{
get
{
return new Dictionary<string, string> {
{"publicUCProperty", "CMSProperty"}
};
}
}

protected override Dictionary<string, string> BoolProperties
{
get
{
return new Dictionary<string, string>
{
{"publicUCBoolProperty", "CMSBoolPropertyName"}
};
}
}

protected override string UserControlPath { get { return "~/CMSWebParts/originalcontrol/testcontrol.ascx"; } }

protected override Label _lblError
{
get { return lblError; }
}
}

This basically allows you to use any usercontrol without even changing the baseclass. I found it the least work, as you only have to change the usercontrolpath, and the property names for each control.
I wish they would work by default though, so i could save even this work.
Let me know if you have an easier solution.