Accessing a public property from with Repeater_ItemCommand - c#

I've got a user control which has a public property like so:
public string[] ImageIDArray {
get;
set;
}
Within my control I have a repeater, and inside that repeater I have a standard asp.net button. When the button is clicked I need to manipulate the ImageIDArray property. Im using the ItemCommand event handler.
The problem is when the ItemCommand handler runs the ImageIDArray is null. So, there's 2 questions here really.
1) At what point in the page lifecycle do properties get set (unless you programatically set them)
2) How do I get round this problem. Do I need to store the value in the viewstate?
Thanks in advance
Al

Please take a look at this article. Repeater.ItemCommand Event
I believe it contains the relevant infromation that you required.

The whole page life cycle is really stateless, apart from viewstate which gives a perception of state. The controls on the page get recreated every time you do a postback. This MSDN article does a good job explaining it. The data that you loaded previously to the rendered controls are kept in viewstate (for the vs enabled controls) and are loaded back to them, unless user has changed them before posting back. If the user has changed them and you do a postback, those values are loaded in the LoadPostBackData event, which runs after the viewstate has been populated.
I do not know where and when you are loading the data into your ImageIDArray but I'm assuming you are not loading it on every postback which is why the data is empty on your itemCommand event handler.
To answer your first question, just having a property does not do anything, it matters where you are setting the value to it. If you are assigning a value to the property in the ascx file, it stays with the control. Otherwise, you have to load it everytime.
Load the ImageIdArray on the Page_Load event of the user control. The page_Load event runs before your ItemCommand event is fired. If you don't want to load the ImageArray on every postback, you can consider storing that in the session or viewstate. Storing in the session means that the data is available for you for the session duration of the user. ViewState only lasts as long as you are on that page.
You can add data to the ViewState manually as ViewState["ImageIdArray"] = ImageIdArray. Or you can change your property as,
public string[] ImageIdArray
{
get { return (string[])(ViewState["ImageIdArray"] ?? null); }
set { ViewState["ImageIdArray"] = value; }
}
Here is an article that explains a little more about state management in asp.net.

Related

Databinding to object - How to cancel datasource changes

Here is the scenario:
I have an Edit Dialog form with a BindingSource and some data bound text boxes on it:
I pass an entity to the form constructor and it gets loaded into BindingSource.DataSource which causes the data bound controls to show the values of properties.
The problem is as the user edits the values in TextBox controls and Validating events get passed, the data source gets changed though it is not applying to DB but it still can confuses the user as he sees the edited values on the List Form, till next application restart.
so the question is: How to prevent binding source from reflecting changes instantly or how to roll them back?
I inherited the binding source and created a new binding source like this:
public class SuperBindingSource:BindingSource
{
#region Properties
public object DataSourceBeforeChange { get; private set; }
#endregion
#region Methods
public void ResetChanges()
{
this.DataSource = this.DataSourceBeforeChange;
}
#endregion
protected override void OnDataSourceChanged(EventArgs e)
{
base.OnDataSourceChanged(e);
DataSourceBeforeChange=this.DataSource.DeepClone();
}
}
Though I am not sure if it is a good approach.
As an option, when setting up data-bindings, you can set them to update data source never.
Then at the point that you want to apply changes, for example when pressing OK button, you can set data-bindings to update data source on property change and then call end edit method of the binding source.
For Cancel button, you don't need to do anything, because the data source is not updated.
Example
In form load event:
this.BindingContext[bindingSource].Bindings.Cast<Binding>().ToList()
.ForEach(b=>b.DataSourceUpdateMode= DataSourceUpdateMode.Never);
When pressing OK:
this.BindingContext[productBindingSource].Bindings.Cast<Binding>().ToList()
.ForEach(b => b.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged);
productBindingSource.EndEdit();
You can download/clone the full source code:
r-aghaei/SuspendDataBindingExample
You can use the SuspendBinding method after the values are loaded.
After that the values should not update the source until you call ResumeBinding:
SuspendBinding and ResumeBinding are two methods that allow the temporary suspension and resumption of data binding in a simple-binding scenario. You would typically suspend data binding if the user must be allowed to make several edits to data fields before validation occurs. For example, if one field must be changed in accordance with a second, but where validating the first field would cause the second field to be in error.
According to the documentation you should be able to use this with your textboxes. If the user clicks Ok to save the values you resume the binding and if he cancels you don't.

How to update the databound elements in the UI in a cached page?

In my app (GroupItemsPage) I have several groups with items (showing some news). If a user scrolls to the right and chooses an item, he is redirected to a details page. If he returns, the same group should still be displayed. I could achieve this behaviour by adding NavigationCacheMode="Enabled" to the XAML (see here). This works like a charm.
But unfortunately, after this, the page is not updated anymore. On this GroupItemsPage I display the number of unread items (with databinding). Unfortunately, this is not updated anymore since the page is cached.
How can I have both behaviors (update gui with databinding and persisting the chosen page)?
Edit: The GroupItemsPage looks the following. I marked the things I need to update (they are databound).
Events Loaded and Unloaded will always be called when navigating to and away from page regardless of cache settings.
If you want to load/update data in cached page, use Loaded event.
EDIT: There is another solution for your problem.
Define a private field _isLoaded which will tell you if data is loaded.
private bool _isLoaded;
Then in constructor set Loaded event:
Loaded += OnLoaded;
And then use this code in OnLoaded method to load or update your UI:
if (_isLoaded)
{
// Update your UI
}
else
{
_isLoaded = true;
// Initialize your UI (data loading, data binding, etc.)
}

User Control with Public Property in FormView

I have added a user control into a form view item template, but I have added a public property to the user control, and I can't seem to figure out how to set the property. I have tried the following:
<uc1:OfacControl id="OfacControl1" runat="server" AssetEvictionId='<%# Bind("AssetEvictionId") %>' />
But the value never gets set correctly.
I have also tried doing it in the codebehind of the form in the preRender method like so:
var assetEvictionIdHiddenField = (HiddenField)oFormView.FindControl("AssetEvictionIdHiddenField");
var OfacControl1 = (Ofac)oFormView.FindControl("OfacControl1");
if (OfacControl1 != null && assetEvictionIdHiddenField != null)
OfacControl1.AssetEvictionId = Convert.ToInt32(assetEvictionIdHiddenField.Value);
This doesn't work either.
It appears to be a timing issue. It looks like the control is being rendered at a different time than the value is being set. If the bind syntax doesn't work, and I am forced to use the code behind, which form view event should I be using to set the value. I have also tried the OnItemCreated event. This didn't work either.
It seems you are trying to set the control in the wrong place. You should be setting it in Init.
ASP.NET Page Life Cycle Overview states the following:
Init
Raised after all controls have been initialized and any skin settings
have been applied. The
Init
event of individual controls occurs before the
Init
event of the page. Use this event to read or initialize control
properties.

ASP.NET Modifying Control Tree Dynamically

I am trying to drop and add controls dynamically to my control tree. For example:
Under a specific condition, I am calling:
private void resetPanel()
{
Panel.Controls.Clear();
Panel.Controls.Add(Image);
Panel.Controls.Add(HiddenField);
}
My main objective is how do I get the added controls to persist across postbacks?
When I call another similar function using textboxes and titles, it persists perfectly. However, with the image it loses its URL and properties.
I understand that for dynamic controls to persist, you must add it on the Init, and you must be responsible for the control tree thus needing to add the dynamic control to the tree on every postback.
So why does it work for textboxes and labels persisting across post backs but you cannot do the control add for images and hiddenfields?
Thanks,
Brian
--Update and Solution--
I have found a mistake in my code, and the HiddenField values do persist across post backs. The solution I have opted for is to use the ViewState to save the values, then restore my dynamic controls on each post back.
--Edit--
Thank you for the replies, and since there may be a better solution to my problem, here is some code that will hopefully show how I am calling the method and why I would need to.
public void resetTitlePanel()
{
// Restylize the panel to initial state
TitlePanel.Controls.Clear();
TitlePanel.BorderColor = System.Drawing.Color.Maroon;
TitlePanel.BorderStyle = BorderStyle.Dashed;
TitlePanel.Enabled = true;
// Set the new control properties to initial state
Label TitleLabel = new Label();
TitleLabel.ID = "TitleLabel";
TextBox TitleTxtBox = new TextBox();
TitleTxtBox.ID = "TitleTxtBox";
// Add the new controls to the container
TitlePanel.Controls.Add(TitleLabel);
TitlePanel.Controls.Add(TitleTxtBox);
// Set the reference of this to the new dynamic control
this.TitleLabel = TitleLabel;
this.TitleTxtBox = TitleTxtBox;
}
public void resetImagePanel()
{
// Restylize the panel to initial state
ImagePanel.Controls.Clear();
ImagePanel.BorderColor = System.Drawing.Color.Blue;
ImagePanel.BorderStyle = BorderStyle.Dashed;
ImagePanel.HorizontalAlign = HorizontalAlign.NotSet;
// Set the new control properties to initial state
Image AddImage = new Image();
AddImage.ImageUrl = "~/Resources/Icons/picture_add.png";
AddImage.ID = "AddImage";
HiddenField HiddenImage = new HiddenField();
HiddenImage.ID = "HiddenImage";
// Add the new controls to the container
ImagePanel.Controls.Add(AddImage);
ImagePanel.Controls.Add(HiddenImage);
// Set the reference of this to the new dynamic control
this.AddImage = AddImage;
this.HiddenImage = HiddenImage;
}
The Calling Method:
private void copyFromSlide(TemplateControl destination, Template source)
{
// Reset the template
destination.resetTitlePanel();
destination.resetImagePanel();
destination.Title = source.Title;
// Find the path from the database and assign it to the control
destination.ImagePath = modData.getImagePath((int)source.ImageID);
}
So... I understand that the code is complex, perhaps more than it should be. Further, I am just a beginner so it may be of worse quality, and I apologize for that.
Key notes are:
There are 2 user controls that are interacting with each other.
This works completely fine on !IsPostback.
The ViewStateEnable is true on default, even if I assign it true explicitly, I get the same results.
This works completely for the title panel which consists of a label and textbox, both of which retains its value.
I know I am mixing static and dynamic controls together. I am used to C, so I am unsure if I could just move the object pointer to the new dynamic object.
The problem is, when assigning the image path, the value does not retain on postback.
I need to drop and re-add controls because under specific conditions I will drop the controls and add labels, which as noted, have no problem. The reason why I believe that I do not need to initialize the controls over again is because I am adding to a rooted panel as demonstrated by:
http://weblogs.asp.net/infinitiesloop/archive/2006/08/30/TRULY-Understanding-Dynamic-Controls-_2800_Part-3_2900_.aspx
I hope this adds some clarity.
Thanks once again,
-Brian
ViewState does not track changes until the InitComplete event fires. If you make changes and store them in ViewState before then (for example in Init) these changes will not generate a new viewstate key. Init should be used to reconstruct your control from viewstate. Events after the Init event is where you should set new values for these controls in view state so that they will be persisted back.
In response to your second question: The Image and HiddenField controls do not respond to any events (e.g. click event) and therefore do not need to be reconstructed. This is why you are seeing the different behavoir between the TextBox and Label controls vs the Image and HiddenField controls.
It may be easier to store the data that determines when to add and remove those controls in the page view state and just recreate the controls on every page load.
Have you enabled ViewState on these controls?
There are some things in the Remarks section of this document you may want to check out here
What you need to do is to always have all your controls added to the page (or user control) before OnLoad() happens (typically on OnInit()).
This is because ASP.NET loads control values from ViewState or Request.Form on OnLoad().
In fact between postbacks none of the "control"s are persisted, it's only control values which are preserved between postbacks either in ViewState or Request.Form. (No matter if they are added in Markup or Dynamically) as I said above, the only thing that is important is that the control is added to the page before OnLoad().
I should add even if you add your element after OnLoad you still can read control's value (in postback) like this:
Request.Form[txtName.UniqueID]
for more information about controls life cycle in ASP.NET see here;
http://msdn.microsoft.com/en-us/library/ms178472.aspx

Extender controls may not be registered after PreRender. Occurs on GridView DataBind()

On our ASP.Net 4.0 C# web app I get the error above in the subject line during debug.
I'm not really doing anything funky with dynamic controls on the page as some search results indicated could have been the source of the problem.
The page has 2 ajax:CalendarExtenders and has been working fine for a while.
Although its probably related, I can see how really but this is what I was currently working on when the error came up.
We have a control on the page that raises an event on an autorefresh feature. [Its a map control].
My page subscribes to that event and upon doing so DataBinds an asp:GridView. We need to bind the data in the grid every time to ensure the grid and the map control are in sync. [Its a vehicle tracking page]
The error occurs on the DataBind command.
I have removed the Extenders only to come up with the same error.
The databind is straightforward but I'm sure the error resides elsewhere. I'll include it anyway.
this.SearchGrid.DataSource = resultsWithMetrics;
this.SearchGrid.AllowPaging = true;
this.SearchGrid.PageIndex = this.SearchGridPager.CurrentPage;
this.SearchGrid.AllowPaging = this.SearchGridPager.PageSize > 0;
this.SearchGrid.PageSize = this.SearchGridPager.PageSize > 0 ? this.SearchGridPager.PageSize : this.SearchGrid.PageSize;
this.SearchGrid.DataBind();
Maybe its simply not understanding the page life cycle that has tripped me up, anyway would be glad of some help.
Use the following code in your usercontrol or page control. It will work if you are using any ajax control.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (this.DesignMode == true)
{
this.EnsureChildControls();
}
this.Page.RegisterRequiresControlState(this);
}
The issue was that on row databind the extender controls were added to each row and it was these extenders that caused the issue.
The event that triggered the the rebind came from a user control that used callbacks to communicate to the server and subsequently raise the event which probably arrived too late anyway in the page lifecycle.
Without fully understanding what was going on it all smelt a bit dodgy and I have changed the structure of the app a bit.

Categories