I was wondering about some best practices for data binding in web forms.
For example we have a control:
public partial class MyUserControl : UserControl
{
public override void DataBind()
{
//#1
base.DataBind();
//#2
}
}
If we wanted that control to bind its data automatically we would do something like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.DataBind();
}
}
In the first section of code if we do our data binding before the call to base.DataBind() (#1) then that means that, if for example we databind a ListView, we don't have to call ListView.DataBind() because when we call base.DataBind() on our control it recursively calls DataBind on all child controls. Also, we won't have access to any of the properties of the control that were assigned to it using the data binding code blocks <%# %>
If we do our binding on our controls after base.DataBind() (#2) then this means that DataBind is called on these controls twice. Once when base.DataBind() is called, and the second time when we are forced to call control.DataBind().
Does anyone know of some sort of pattern I can follow here that I don't know about?
Am I making any sense here?
What am I doing wrong?
EDIT:
Looking at this page:
http://msdn.microsoft.com/en-us/library/w5e5992d.aspx
Use this method to bind data from a
source to a server control. This
method is commonly used after
retrieving a data set through a
database query. The method is
primarily used by control developers;
most controls perform data binding
automatically.
It appears that best practice is to bind controls data automatically. Databinding is for when we explicitly set a data source.
I've encountered problems previously, when pages become complex and you are relying on user controls binding their own data in the page load event. This has resulted in some long debugging sessions, as you can't be guaranteed exactly when this page load event in the control will fire.
Now, that said, these pages weren't too well designed, and we shouldn't have run into this problem, but I prefer to stick with the explicit and have my page tell the child controls when to do their thing - you may wish to avoid calling base.DataBind and do it explicitly instead - you may also wish to not override the DataBind method in this way and instead call the method on your control by another name.
Related
I am new to the .net and I am working on the some functionality in office that is as below
Jobs.aspx.cs
protected void gvActionItems_RowEditing(object sender, GridViewEditEventArgs e)
{
//setting the value of the user control property
}
JobUserControl.ascx.cs
public int _usrcontrolproperty
{
get{return _usrcontrolproperty;}
set{
//depending on the value of the property fetch the data from the database and binding those data on the user controls FormView
}
}
protected void fvJob_DataBound(object sender, EventArgs e)
{
//Making the dynamic UI changes that is setting properties of controls depending upon the values of binding data
}
This is how i did the required UI changes in databound event of form view, but one of senior says 'It's bad architectural code design it has extra memory issue and make the UI changes in the _usrcontrolproperty set method after the data bind.'. So i want to know
1) Is this really bad architectural code ? If bad then why ?
2) And if my seniors way is bad then also Why ?
Because i thought the UI changes should done at the the time of the binding the data
If your senior cannot backup his/her claims.. then he/she isn't really someone you should try learning from. I'm not sure what the "memory issue" is that he/she is referring to, however it's hard to tell with your stripped down code.
That being said, I would reconsider databinding in a property set purely because you open yourself up to "gotcha's" later down the track when people start setting this property.
Instead, I would have a Refresh() method. Therefore, the calling code would be:
UserControl.Property = value;
UserControl.RefreshData();
This gives the calling API the option of refreshing at that point or deferring the decision.
I am with the #Simon on this one but I would have a RefreshData(value) method. Therefore, the calling code would be:
UserControl.RefreshData(value);
This gives the calling API the option of refreshing at that point or deferring the decision.
An in this method you can use as ,
Public static RefreshData(<datatype>data)
{
//Assign the value to the property
//Get the data from database
//Bind the data
}
And you can also make your property private or protected if you don't want to expose the property to other classes.
I have composite web server control, which at the moment doesn't perform any actions. My aim is to place inside it child controls beginning with checkbox. I try to do it in the following way:
[DefaultProperty("Text")]
[ToolboxData("<{0}:SubmitImageControl runat=\"server\"></{0}:SubmitImageControl>")]
public class SubmitImageControl : CompositeControl
{
private CheckBox _checkBox;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
}
protected override void CreateChildControls()
{
_checkBox = new CheckBox();
Controls.Add(_checkBox);
base.CreateChildControls();
}
protected override void RenderContents(HtmlTextWriter output)
{
_checkBox.RenderControl(output);
}
}
Registering and placing on the page:
<%# Register TagPrefix="uc" Namespace="PostBackHandlerApp.Controls" Assembly="PostBackHandlerApp" %>
<uc:SubmitImageControl runat="server" />
Checkbox appears on the page and everything seems fine until we look at the view state. Its value is
/wEPDwULLTExMTg2MzM0NjJkGAEFHl9fQ29udHJvbHNSZXF1aXJlUG9zdEJhY2tLZXlfXxYBBR1jdGwwMCRNYWluQ29udGVudCRjdGwwMCRjdGwwMD+PWeqrbtVyQSNMxvfjcmJkKAwpIuEPWJd+m5W6eJtQ
Then, if we simply remove the code Controls.Add(_checkBox);, view state size reduces greatly:
/wEPDwULLTExMTg2MzM0NjJkZLrri0oSGPS9ZiOTsRtSageoskXzCME4KCdRZxOiJyR9
If I move the code of child initialization and adding to OnInit method of my control (where, as far as I know, view state tracing is still disabled), result stays the same. Also, this MSDN article recommends to perform initialization only in CreateChildControls method:
You should create the child controls in the CreateChildControls method and not in OnInit or another life cycle phase. The server control architecture relies on calls to CreateChildControls whenever the Controls collection is needed, such as during data binding (if applicable).
Could anyone explain me why view state becomes larger? Thanks in advance.
Have you tried disabling viewstate for the checkbox in the control. I preusme the viewstate has to account for this control unless you specify otherwise? If you want to easily use this control within the lifecycle though you would want to leave the viewstate enabled.
The reason why viewstate is populated is implementing IPostBackDataHandler interface by large part of data controls, including checkbox. Interface's method LoadPostData is called automatically after the LoadViewState event and viewstate gets populated from the posted data.
Here is nice article about it.
OK, weird one. I have many usercontrols with a repeater, the layout of the repeater is the same in all controls, and they all have a bindData() method publically available.
I'm wondering, can I setup another usercontrol for paging without having to specify the parent control?
I'm able to do the following:
((controls.specificuserControlClass)Parent).bindData();
Which is all fine - however I'd need to specify the specificuserControlClass into the pager and then would need it "per repeater" if you see what I mean?
So can I call Parent.bindData() blindly from the child control? I "know" that method exists (or would build checks to make sure), however Visual Studio isn't happy as it doesn't know of the method.
Why not make your controls all implement a specific interface?
public interface IBindData
{
void bindData();
}
Then, you would simply do:
((IBindData)Parent).bindData()
And it should invoke each control's method as appropriate.
Ok I'm trying to understand how best to handle ViewState, for the programmatic setting of default values using C#. I understand that the construction of the ViewState hidden field is based on every value that is set after the OnInit event is triggered. What I'm not clear about is if there is a difference between using the control's constructor or the OnInit event to set default values.
public MyControl(){
this.Text = "SomeDefaultValue";
}
versus
protected override void OnInit(EventArgs e){
this.Text = "SomeDefaultValue";
}
I've seen some places that suggest testing the ViewState value for null in the get of the given property, like so:
public string Text {
get {
return this.ViewState["Text"] == null ?
"SomeDefaultValue" :
this.ViewState["Text"] as string;
}
set { this.ViewState["Text"] = value; }
}
I don't like that because it makes clearing the value confusing.
So, Is there any functional difference between using the constructor vs OnInit to set default ViewState values?
In terms of minimizing ViewState, there is no difference, as ViewState starts tracking after the OnInit method is run.
There are some functional differences, however: until the control is initialized, you cannot access other properties like the Page. For this reason, I usually prefer to use either OnInit or some handler tied to the Init event.
Also, be careful about overriding OnInit: you should call base.OnInit() to make sure that other event handlers for the Init event still get called.
I highly recommend that you read this excellent article on the topic: http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx
Edit
To clarify, the ViewState starts tracking for a given control after the OnInit method is run for that control. So in the given example, you are safe to override OnInit like this:
protected override void OnInit(EventArgs e){
this.Text = "SomeDefaultValue"; // Make sure this happens before base.OnInit
base.OnInit();
}
This works because the Text property is saving the value to the ViewState of this control. However, let's say you have another child control (I'll use a Label as an example). That Label's OnInit will already have been run by the time your control's OnInit method is called. So if you want to change the Label's Text value, you'll need to do it during that label's OnInit phase (or sooner).
You could do it in the constructor of the current control:
public MyControl(){
this.Label.Text = "SomeDefaultValue";
}
... but as mentioned earlier you won't have access to the external control structure, which may be necessary in some cases. A good alternative in these cases is to use an Init event handler on the label itself. You can hook up the event handler itself in your constructor:
public MyControl(){
this.Label.Init +=
(sender, e) => this.Label.Text =
((TextBox)Page.FindControl("SomeControl")).Text;
}
... but this will only work if the control is declared directly as a member of your class. If the label is inside a template (like in a Repeater), you'll need to use markup to hook it up:
<asp:Label runat="server" OnInit="Label_Init" />
with the code-behind:
public void Label_Init(object sender, EventArgs e)
{
var label = (Label)sender;
label.Text = ((TextBox)Page.FindControl("SomeControl")).Text;
}
This latter example has the advantage of working in just about every circumstance I can think of, but it requires more boilerplate code, as well as a change in markup. So pick your poison based on your specific situation.
There is quite a detailed document on the ViewState over at MSDN:
...server controls don't begin tracking
view state changes until right at the
end of the initialization stage.
Second, when adding dynamic controls
that need to utilize view state, these
controls will need to be added during
the Page's Init event as opposed to
the Load event.
Just from this alone, I would say, if you're utilising the ViewState, use OnInit.
I have an UpdatePanel.
and I have a PlaceHolder inside this UpdatePanel.
There is a number of UserControls. One of them will be loaded dynamically,
according to some selections.
Control mycontrol = this.Page.LoadControl("myusercontrol.ascx");
myplaceholder.Controls.Add(mycontrol);
after loading a specific UserControl, I wanted to get the text written in
a TextBox that is in the loaded UserControl from the Parent page.
TextBox mytextbox = (TextBox) Page.FindControl("myusercontrol")
.FindControl("mytextbox");
The problem was the text is always empty !
What am I missing ?
I appreciate your help.
You should load your UserControl overriding OnInit as mentioned before. And why were you looking entire page to find the UserControl? You can use PlaceHolder.Controls...
This how I got it work
protected override void OnInit(EventArgs e)
{
Control userControl = this.Page.LoadControl("WebUserControl.ascx");
testPlaceHolder.Controls.Add(userControl);
userControl.ID="id";
base.OnInit(e);
}
protected void testButton_Click(object sender, EventArgs e)
{
Control testUserControl = (Control)testPlaceHolder.Controls[0];
//Control testUserControl=(Control)testPlaceHolder.FindControl("id");
TextBox mytextbox = (TextBox)testUserControl.FindControl("testTextBox");
testButton.Text = mytextbox.Text;
}
When you say that the text is always empty, do mean the TextBox object is null or literally the .Text of the textbox is empty?
Remember that in web applications you have to post back to the server to refresh results and update controls among other things.
Try posting back to the server and seeing if that helps.
Have you considered adding a property to your user control to return the text?
eg:
public class YourControl : UserControl
{
public string Text
{
get
{
return this.TextBox1.Text;
}
}
}
Usually, User Controls are used for encapsulation - you wrap up all the details of controls, behaviour etc in a UC so other code doesn't have to deal with it.
By referring to controls within the UC directly - by name or ID - you're breaking the model. Can I suggest you don't do this, instead if you need to get information from the UC you add a property, event or method to it that the container can call.
That way if you need to change the UC - control names, types, styles, or additional logic is used later - you only need to change that property/event/method in the UC, not in the (for example) 100 places it might be used in the code.
If you could let us know why you need this information or more specific details about the example, perhaps we can suggest some code to implement this.
So, what should I do ?
Just get the posted values manually.
Request.Form[yourcondeol.UniqueID]
by debugging this you can see all the posted data.
Request.Form