Asp.Net Repeater loses data on postback - asp.net

My problem is a bit more complicated than the title says:
I made a user control (I called it editor) for editing Data base data into another user control I also made(I called it GridView).
The editor is use for each row (a row is also a usercontrol and the editor is only inside row even for insert) inside my gridview and work perfectly but not when I try to use it to insert.
The only difference between insert and edit is this Field:
#region Field
/// <summary>
///
/// </summary>
//public Field Field { get { return DataItem as Field; } }
private Field _field;
[Bindable(true)]
public Field Field
{
get
{
if (IsInsert && _field == null)
{
_field = SubscriptionController.CreateField();
}
return _field;
}
set { _field = value; }
}
#endregion
Inside this Field I've the collection I bind to the repeater
the SubscriptionController.CreateField(); method just create an instance of Field class and all collection inside here is the code:
public Field CreateField()
{
Field field = new Field();
field.Type = GetFieldTypes().First();
field.Label = new LocalizedStringCollection();
field.Values = new FieldValueCollection();
field.Selections = new FieldSelectionCollection();
foreach (Models.TrainingGroup trainingGroup in GetTrainingGroup())
{
foreach (Models.Division division in GetDivisions())
{
foreach (Models.ProfilStatusGroup profilStatusGroup in GetProfilStatusGroup())
{
field.Selections.Add(new Models.FieldSelection() { Selected = false, DivisionId = division.Id, ProfilStatusGroupId = profilStatusGroup.Id, TrainingGroupId = trainingGroup.Id });
}
}
}
}
the collection I bind is stored in viewstate :
#region FieldValues
/// <summary>
/// Get/Set FieldValues from Viewstate
/// </summary>
public FieldValueCollection FieldValues
{
get
{
if (ViewState["FieldValues"] == null)
{
if (Field != null && Field.Values != null)
ViewState.Add("FieldValues", Field.Values);
else
ViewState.Add("FieldValues", new FieldValueCollection());
}
if (ViewState["FieldValues"] != null)
{
return (FieldValueCollection)ViewState["FieldValues"];
}
return null;
}
set
{
if (ViewState["FieldValues"] == null)
{
ViewState.Add("FieldValues", value);
}
else
{
ViewState["FieldValues"] = value;
}
}
}
#endregion
but when I get on postback all textboxes inside my repeater are empty.

Related

Inspite of checking for null with usercontrol checkboxlist is returning object reference not set to an instance of an object

I have a group of 7 checkboxes in checkboxlist user control. I build a string in the selectedIndexchanged event for the boxes checked, pass to ViewState and then pass ViewState to the Property. I do this because in the instance when no checkboxes are selected I want to handle null. The problem is no matter how I check for null, the system is throwing object reference error. This current setup works fine if at least one checkbox is checked but if none are checked it fails. How do I check for null? Should I be checking for null in the property or the host aspx page?
I have researched difference ways to do this and I have tried many. My thought is using IsNullOrEmpty or IsNullOrWhiteSpace would be the correct way to go but neither are working.
User Control class - global variable
private string _daysOffInputString = string.Empty;
User Control Property
public string DaysOffSelectedValues
{
get
{
if (string.IsNullOrEmpty(ViewState["DaysOff"].ToString()))
{
_daysOffInputString = string.Empty;
}
else
{
_daysOffInputString = ViewState["DaysOff"].ToString();
}
return _daysOffInputString;
}
set { _daysOffInputString = value; }
User Control event
protected void CbDaysOff_SelectedIndexChanged(object sender, EventArgs e)
{
CheckBoxList chkbx = (CheckBoxList)sender;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < chkbx.Items.Count; i++)
{
if (chkbx.Items[i].Selected)
{
sb.Append(chkbx.Items[i].Text + ", ");
}
if (!String.IsNullOrEmpty(sb.ToString()))
{
//Remove last comma & space from string
_daysOffInputString = sb.ToString().Substring(0, sb.ToString().Length - 2);
}
else
{
_daysOffInputString = string.Empty;
}
}
ViewState["DaysOff"] = _daysOffInputString;
}
aspx page - snippet where I retrieve uc property value:
case 2:
blnFlag = false;
ucDaysOff uc3 = row.Cells[3].FindControl("ucDaysOff3") as ucDaysOff;
strAnswer = uc3.DaysOffSelectedValues; //e.g. "Sat, Sun"
break;
SOLUTION: In the user control property DaysOffSelectedValues I was casting ViewState["DaysOff"] to string before checking for null which was the problem. Here's the code that works:
public string DaysOffSelectedValues
{
get
{
if (ViewState["DaysOff"] == null)
{
//_daysOffInputString = string.Empty; }
_daysOffInputString = "Nothing to see here.";
}
else
{
_daysOffInputString = ViewState["DaysOff"].ToString();
}
return _daysOffInputString;
}
set { _daysOffInputString = value; }
}
You should always check if the object, in this case ViewState, is null before using it. Lets say ViewState["DaysOff"] has not been created or has been removed.
Then this will throw a nullreference:
string str = String.IsNullOrEmpty(ViewState["DaysOff"].ToString());
Because you are not checking the ViewState object for null, but the string it is supposed to hold.
So do this
if (ViewState["DaysOff"] != null)
{
string str = ViewState["DaysOff"].ToString();
}

User Control's child controls not getting instantiated

public partial class ChatUserControl : System.Web.UI.UserControl
{
UserChatClass ucc = new UserChatClass();
public ChatUserControl()
{
lblChatFriend = new Label();
txtChatMessage = new TextBox();
imgFriend = new Image();
rpChatMessages = new Repeater();
}
public string ChatFriend { get { return this.lblChatFriend.Text; } set { this.lblChatFriend.Text = value; } }
public string imgFriendUrl { get { return this.imgFriend.ImageUrl; } set { this.imgFriend.ImageUrl = value; } }
public object rpChatDataSource { get { return this.rpChatMessages.DataSource; } set { this.rpChatMessages.DataSource = value; } }
public Repeater rpChatMessagesToBind { get { return this.rpChatMessages; } set { this.rpChatMessages = value; } }
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ChatUserControl user1 = new ChatUserControl();
divChatUserControlCover.Controls.Add(user1);
}
}
private void BindUserControls()
{ ChatUserControl user1 = divChatUserControlCover.Controls[1] as ChatUserControl;
user1.ChatFriend = row["username"].ToString();
user1.imgFriendUrl = "../../HttpImageHandler.jpg?username=" + row["username"].ToString();
DataSet dsCM = ucc.GetChatMessages(Session["username"].ToString(), row["username"].ToString());
user1.rpChatDataSource = dsCM;
user1.DataBindForRpChatMessagesToBind();
user1.Visible = true;
}
Master.aspx
<div id="divChatUserControlCover" runat="server">
</div>
Ok I have edited the code and now I have created properties. How do I call the DataBind method for rpChatMessages? I also cant see my usercontrol on page. Why
I'm not sure if your trying to reference the first label or second label. If its the second lable you can't just do chatMessage. you would have to do
((Label)rpChatMessages.FindControl("chatMessage")) due to scope of controls.
When you reference a component inside another component (ie Repeater) the child component no longer belongs to the document (implied this) but rather belongs to the control, ie
this.rpChatMessages { chatMessage }
I think you are just trying to pass a value to one control inside a UserControl if this is correct, declare a public property like this:
ASCX code behind
public string MyProperty
{
get
{
return this.lbl.Text;
}
set
{
this.lbl.Text = value;
}
}
Setting the value to the UserControl
private void BindUserControls()
{
ChatUserControl user1 = divChatUserControlCover.Controls[1] as ChatUserControl;
user1.MyProperty = row["username"].ToString();
Setting the value in the page markup
<uc1:ChatUserControl MyProperty='<%# Eval("some field") %>' ...
Edit 1
Remove that line
public object rpChatDataSource { get { return this.rpChatMessages.DataSource; } set { this.rpChatMessages.DataSource = value; }
And instead add a method
public void BindMyRepeaterOrWhatever(IEnumerable<Yourentity> data)
{
this.myDataBoundControl.DataSource = data;
this.myDataBoundControl.DataBind();
}
You can change the IEnumerable<Yourentity> data for object data but if you can pass a strongly typed enumeration would be better
To my surprise I found why my user control's child controls dont get instantiated. Its because ChatUserControl user1 = new ChatUserControl() doesnt get its child controls initialized.
The proper way to create a new intance of user control is this way....
ChatUserControl user1 = (ChatUserControl)Page.LoadControl("~/ChatUserControl.ascx");

ASP.NET custom controls - custom property doesn't hold the assigned value on postaback

I have a custom asp-net control that inherits from another one and its works as expected, though the properties are only set properly if i code them in the markup directly, so for instance if i need set a property at runtime that is some dynamic value, this value is never set or somehow lost.
Here's the markup code:
<!--related form-->
<fw:advancedformdisplay id="formDisp" runat="server" captchaenabled="true" EmailEnabled="true" EnableViewState="true" captchaprivatekey="xxxxxxxxxxxxxxxxxxxx" captchapublickey="xxxxxxxxxxxxx" captchatheme="white" SourceType="MenuItem" SourceMainId="Auto">
</fw:advancedformdisplay>
This is the code of the control:
[DefaultProperty("CaptchaEnabled"),ToolboxData("<{0}:AdvancedFormDisplay runat=server></{0}:AdvancedFormDisplay>"), Description("This is an enhanced FormDisplay control that inlcudes Googles Captcha control is enabled")]
public class AdvancedFormDisplay :SiteBuilder.WebControls.FormDisplay
{
bool _CaptchaEnabled = false, sendEmail = false;
string captchaErrorMessage = "The verification code entered is not valid. Please try again!";
RecaptchaControl captchaControl = null;
string captchaPrivateKey = "", captchaPublicKey = "", captchaTheme = "clean";
string originalFormHtml = string.Empty;
string afterText = string.Empty, beforeText = string.Empty;
Literal litHtmlForm = null;
string captchaErrorClass = "errorCaptcha";
public string EmailBeforeText
{
get { return beforeText; }
set { beforeText = value; }
}
public string EmailAfterText
{
get { return afterText; }
set { afterText = value; }
}
public string CaptchaErrorClass
{
get { return captchaErrorClass; }
set { captchaErrorClass = value; }
}
public bool CaptchaEnabled
{
get { return _CaptchaEnabled; }
set { _CaptchaEnabled = value; }
}
public bool EmailEnabled
{
get { return sendEmail; }
set { sendEmail = value; }
}
public string CaptchaErrorMessage
{
get { return captchaErrorMessage; }
set { captchaErrorMessage = value; }
}
/// <summary>
/// red,white,blackglass,clean
/// </summary>
public string CaptchaTheme
{
get { return captchaTheme; }
set { captchaTheme = value; }
}
public string CaptchaPrivateKey
{
get { return captchaPrivateKey; }
set { captchaPrivateKey = value; }
}
public string CaptchaPublicKey
{
get { return captchaPublicKey; }
set { captchaPublicKey = value; }
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
}
public override void OnSaved(FormDisplayEventArgs e)
{
//If captcha control is enabled we need to adda bit of code to redirect form properly
if (CaptchaEnabled && e.Redirect && !e.SendMail)
{
//Do Stuff
}
if(sendEmail)
{
//Send email
}
base.OnSaved(e);
}
public override void OnSaving(FormDisplayEventArgs e)
{
if (CaptchaEnabled)
{
//Validate and do stuff
}
base.OnSaving(e);
}
}
And then in my asp.net page that is using control, created by markup code, in the Page_Load() i try to assign some values to some properties and and the values aren't set properly, meaning that if i have set for isntance, the property EmailBeforeText = "somthing" this value will not be assigned..
protected void Page_Load(object sender, EventArgs e)
{
//2: Get the language of menuitem - Based on current culture setting (for by dropdownbox - change logic)
try
{
currentCulture = Thread.CurrentThread.CurrentCulture.ToString();
// Redirect if domain does not match rootnode.
DomainChecker.CheckURL(this.Request, this.Response, currentCulture);
if (footerArticle != null)
footerArticle.SourceMenuId = Digimaker.Config.Custom.Get("FooterID_" + currentCulture).ToString();
}
catch
{
currentCulture = "en-GB";
if( footerArticle != null )
footerArticle.SourceMenuId = Digimaker.Config.Custom.Get("FooterID_" + currentCulture).ToString();
}
Any ideas what i'm missing here?
Thanks a lot for your reading!
Regards,
byte_slave
short answer: use viewstate to persist your custom values!
Understanding ASP.NET ViewState whitepaper (see example with NavigateUrl)
edit: as reading the white-paper is obviously a really hard thing:
Each control is responsible for storing its own state, which is
accomplished by adding its changed state to its ViewState property.
The ViewState property is defined in the System.Web.UI.Control class,
meaning that all ASP.NET server controls have this property available.
(When talking about view state in general I'll use lower case letters
with a space between view and state; when discussing the ViewState
property, I'll use the correct casing and code-formatted text.)
If you examine the simple properties of any ASP.NET server control
you'll see that the properties read and write directly to the view
state. (You can view the decompiled source code for a .NET assembly by
using a tool like Reflector.) For example, consider the HyperLink Web
control's NavigateUrl property. The code for this property looks like
so:
public string NavigateUrl
{
get
{
string text = (string) ViewState["NavigateUrl"];
if (text != null)
return text;
else
return string.Empty;
}
set
{
ViewState["NavigateUrl"] = value;
}
}
As this code sample illustrates, whenever a control's property is
read, the control's ViewState is consulted. If there is not an entry
in the ViewState, then the default value for the property is returned.
When the property is assigned, the assigned value is written directly
to the ViewState.

ViewState as Attribute

Instead of this ..
public string Text
{
get { return ViewState["Text"] as string; }
set { ViewState["Text"] = value; }
}
I would like this ..
[ViewState]
public String Text { get; set; }
Can it be done?
Like this:
public class BasePage: Page {
protected override Object SaveViewState() {
object baseState = base.SaveViewState();
IDictionary<string, object> pageState = new Dictionary<string, object>();
pageState.Add("base", baseState);
// Use reflection to iterate attributed properties, add
// each to pageState with the property name as the key
return pageState;
}
protected override void LoadViewState(Object savedState) {
if (savedState != null) {
var pageState = (IDictionary<string, object>)savedState;
if (pageState.Contains("base")) {
base.LoadViewState(pageState["base"]);
}
// Iterate attributed properties. If pageState contains an
// item with the appropriate key, set the property value.
}
}
}
Pages that inherit from this class could use the attribute-driven syntax you've proposed.
Well, this is what i got so far, TY Jeff for pointing me in the right direction:
TestPage:
public partial class Pages_Test : BasePage {
[ViewState]
public String Name { get; set; }
BasePage:
#region Support ViewState Attribute
BindingFlags _flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
protected override Object SaveViewState()
{
object _baseState = base.SaveViewState();
IDictionary<string, object> _pageState = new Dictionary<string, object> { { "base", _baseState } };
//Use reflection to get properties marked for viewstate
foreach (PropertyInfo _property in GetType().GetProperties(_flags))
{
if (_property.HasAttribute<ViewState>())
{
object _value = _property.GetValue(this, _flags , null, null, null);
_pageState.Add(new KeyValuePair<string, object>(_property.Name, _value));
}
}
return _pageState;
}
protected override void LoadViewState(Object savedState)
{
if (savedState != null)
{
var _pageState = (IDictionary<string, object>)savedState;
if (_pageState.ContainsKey("base"))
{
base.LoadViewState(_pageState["base"]);
}
//use reflection to set properties
foreach (PropertyInfo _property in GetType().GetProperties(_flags ))
{
if (_property.HasAttribute<ViewState>() && _pageState.ContainsKey(_property.Name))
{
object _value = _pageState[_property.Name];
_property.SetValue(this, _value, _flags , null, null, null);
}
}
}
}
#endregion
Attribute:
/// <summary>
/// This attribute is used by the BasePage to identify properties that should be persisted to ViewState
/// Note: Private properties are not supported
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ViewState : Attribute
{
//Marker
}
Helpers:
public static class PropertyExtension
{
public static Boolean HasAttribute<T>(this PropertyInfo property)
{
object[] attrs = property.GetCustomAttributes(typeof(T), false);
return attrs != null && attrs.Length == 1;
}
}
EDIT
Jan has a valid point about performance, I did some profiling with the following results:
Without Attribute With Attribute Increase Slower %
One Property
First Load 0,004897899 0,010734255 0,005836356 219
Save, postback 0,002353861 0,010478008 0,008124147 445
Load, Postback 0,001488807 0,00627482 0,004786013 421
10 properties
First Load 0,006184096 0,015288675 0,009104579 247
Save, postback 0,004061759 0,015052262 0,010990503 371
Load, Postback 0,0015708 0,005833074 0,004262274 371
% increase
Avg Page. 0,902215714567075 0,00648
On a Empty page the increase is considerable, but on an average page with a load of 1s this increase amounts to 0,01%.
Update : Using PostSharp, PostSharp4ViewState
Step 1 : Make sure your website is precompiled
Step 2 : Install PostSharp and PostSharp4ViewState
Step 3 : Reference PostSharp.Public And PostSharp4ViewState
Step 4 : Following is Code is now valid.
[Persist(Mode=PersistMode.ViewState)]
private string _name;
public String Name {
get { return _name; }
set { _name = value; }
}
BBorg's solution is actually incredibly slow because of the heavy use of reflection.
Using PostSharp.Laos, by letting your attribute inherit from OnMethodBoundaryAspect, you can easily override public override void OnInvocation(MethodInvocationEventArgs eventArgs) and do all the magic in there. This will be way faster. Check for example the CacheAttribute example on the PostSharp homepage.
If you are really wanting bare speed, you can write a PostSharp plugin that weaves MSIL (GetFromViewState, SetInViewState methods or something) into your properties, that won't even have a performance penalty.
This functionality is built into NHibernate Burrow. If you don't happen to use NHibernate in your application, the source code for NHibernate Burrow is available here. Feel free to dig in, see how they did it, and rip out any parts that our useful to you (as long as you comply with the LGPL license).
The most relevant code seems to be in StatefulFieldProcessor.cs lines 51 - 72.
/// <summary>
/// Get the FieldInfo - Attribute pairs that have the customer attribute of type <typeparamref name="AT"/>
/// </summary>
/// <typeparam name="AT"></typeparam>
/// <returns></returns>
protected IDictionary<FieldInfo, AT> GetFieldInfo<AT>() where AT : Attribute {
IDictionary<FieldInfo, AT> retVal = new Dictionary<FieldInfo, AT>();
foreach (FieldInfo fi in GetFields())
foreach (AT a in Attribute.GetCustomAttributes(fi, typeof (AT)))
retVal.Add(fi, a);
return retVal;
}
protected IDictionary<FieldInfo, StatefulField> GetStatefulFields() {
IDictionary<FieldInfo, StatefulField> retVal;
Type controlType = Control.GetType();
if (controlType.Assembly == webAssembly)
return null;
if (!fieldInfoCache.TryGetValue(controlType, out retVal))
fieldInfoCache[controlType] = retVal = GetFieldInfo<StatefulField>();
return retVal;
}

Converting List in Comma Separated List with linq?

I am having a info class with following field like id,name,address and i have created list of that info like List.
I want to have all the values of list into comma seperated strings using linq. Is there any way for this.
simple way... may not be the fastest so it depends on how many records you are processing
var myObjects = new[] {
new {
id=1,
name="Matt",
address="1234 no chance ln\r\nnowhere, OH 12345"
},
new {
id=1,
name="Jim",
address="4321 no chance ln\r\nnowhere, OH 12345"
}
};
var myList = (from o in myObjects
select string.Format("{0},\"{1}\",\"{2}\"",
o.id,
o.name,
(o.address ?? string.Empty).Replace("\r\n", ";")
)).ToList();
Have a look at this example by Mike Hadlow. It needs some improvement (escaping commas, new line support etc) but gives you the basic idea.
Taken from the LinQExtensions.cs found at Batch Updates and Deletes with LINQ to SQL
/// <summary>
/// Creates a *.csv file from an IQueryable query, dumping out the 'simple' properties/fields.
/// </summary>
/// <param name="query">Represents a SELECT query to execute.</param>
/// <param name="fileName">The name of the file to create.</param>
/// <remarks>
/// <para>If the file specified by <paramref name="fileName"/> exists, it will be deleted.</para>
/// <para>If the <paramref name="query"/> contains any properties that are entity sets (i.e. rows from a FK relationship) the values will not be dumped to the file.</para>
/// <para>This method is useful for debugging purposes or when used in other utilities such as LINQPad.</para>
/// </remarks>
public static void DumpCSV(this IQueryable query, string fileName)
{
query.DumpCSV(fileName, true);
}
/// <summary>
/// Creates a *.csv file from an IQueryable query, dumping out the 'simple' properties/fields.
/// </summary>
/// <param name="query">Represents a SELECT query to execute.</param>
/// <param name="fileName">The name of the file to create.</param>
/// <param name="deleteFile">Whether or not to delete the file specified by <paramref name="fileName"/> if it exists.</param>
/// <remarks>
/// <para>If the <paramref name="query"/> contains any properties that are entity sets (i.e. rows from a FK relationship) the values will not be dumped to the file.</para>
/// <para>This method is useful for debugging purposes or when used in other utilities such as LINQPad.</para>
/// </remarks>
public static void DumpCSV(this IQueryable query, string fileName, bool deleteFile)
{
if (File.Exists(fileName) && deleteFile)
{
File.Delete(fileName);
}
using (var output = new FileStream(fileName, FileMode.CreateNew))
{
using (var writer = new StreamWriter(output))
{
var firstRow = true;
PropertyInfo[] properties = null;
FieldInfo[] fields = null;
Type type = null;
bool typeIsAnonymous = false;
foreach (var r in query)
{
if (type == null)
{
type = r.GetType();
typeIsAnonymous = type.IsAnonymous();
properties = type.GetProperties();
fields = type.GetFields();
}
var firstCol = true;
if (typeIsAnonymous)
{
if (firstRow)
{
foreach (var p in properties)
{
if (!firstCol) writer.Write(",");
else { firstCol = false; }
writer.Write(p.Name);
}
writer.WriteLine();
}
firstRow = false;
firstCol = true;
foreach (var p in properties)
{
if (!firstCol) writer.Write(",");
else { firstCol = false; }
DumpValue(p.GetValue(r, null), writer);
}
}
else
{
if (firstRow)
{
foreach (var p in fields)
{
if (!firstCol) writer.Write(",");
else { firstCol = false; }
writer.Write(p.Name);
}
writer.WriteLine();
}
firstRow = false;
firstCol = true;
foreach (var p in fields)
{
if (!firstCol) writer.Write(",");
else { firstCol = false; }
DumpValue(p.GetValue(r), writer);
}
}
writer.WriteLine();
}
}
}
}
private static void DumpValue(object v, StreamWriter writer)
{
if (v != null)
{
switch (Type.GetTypeCode(v.GetType()))
{
// csv encode the value
case TypeCode.String:
string value = (string)v;
if (value.Contains(",") || value.Contains('"') || value.Contains("\n"))
{
value = value.Replace("\"", "\"\"");
if (value.Length > 31735)
{
value = value.Substring(0, 31732) + "...";
}
writer.Write("\"" + value + "\"");
}
else
{
writer.Write(value);
}
break;
default: writer.Write(v); break;
}
}
}
private static bool IsAnonymous(this Type type)
{
if (type == null)
throw new ArgumentNullException("type");
// HACK: The only way to detect anonymous types right now.
return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
&& type.IsGenericType && type.Name.Contains("AnonymousType")
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
}

Resources