I just wanted to confirm couple of things.
I) Code snippet:
cmb1.Datasource= dt;
cmb1.Valuemember = "value";
Does the control rendering happens 2 time for the control, 1 more time extra because of the value member getting changed after data source assigned. Is this so?
II) How can I trace these re-populations in C#? I just wanted to debug and see and confirm? Example please?
Thanks
Karthik
I) It depends. If this is for an asp.net site, you have to also call the DataBind() method before anything actually happens. But otherwise, yes - databinding will likely happen twice.
II) You can trace it by building a method that returns a datatable, and binding to that method as the data source. Then you can set a breakpoint inside the method and watch for when the breakpoint is hit.
This depends on the actual implementation of the data bound control. I'd expect a "good" control to only retrieve the data when it needs it, and therefore changing these properties would not do anything but clear out existing bindings (if there were any) and create the new ones without actually retrieving data.
You can use a profiler to trace such things, or bind to a class of yours and set a breakpoint or increase a counter.
Related
I have a datagridView on Form1. When I double-click on an item in that Datagridview, I go to a detailform where I get to see the details of the line in the datagridview. (I get them based on the id of the record I keep in the datagridview). Now I want to make prev/next buttons on the detailform so that the user doesn't have to go back to form1.
I can access the datagridview (which is public), but I can't see the row info.
In the debugger I can see the row info if I start from
this.Owner.Controls[0].Controls[2].Controls[0] and then click to open the Rows properties. When I enter this.Owner.Controls[0].Controls[2].Controls[0].Rows[0], I get error CS1061.
How can I fix this?
This is a data type issue. Looking at this expression:
this.Owner.Controls[0].Controls[2].Controls[0].Rows[0]
We can follow the data type of each part of the expression from beginning to end to see the problem.
First, the type of this is the current Form, which will be something inherited from System.Windows.Forms.Form. From there, we check that type's .Owner property, and see it is also a (nullable) System.Windows.Forms.Form.
Now we start looking at the Form type's .Controls member. We find the datatype here by understanding that Form inherits from System.Windows.Forms.Control, which leads us to the property definition. Now we learn we're looking at a System.Windows.Forms.ControlCollection. This type has an indexer property that allows you to make the [0] and [2] lookups (probably better to do this by name, fwiw). The documentation for this property tells us the result of the indexer expression is back to System.Windows.Forms.Control.
Now we know enough to understand that when we take this expression:
this.Owner.Controls[0].Controls[2].Controls[0]
the data type chain for the expression looks like this:
this .Owner .Conrols [0] .Controls [2] .Controls [0]
Form > Form > ControlCollection > Control > ControlCollection > Control > ControlCollection > Control
It shows the final result is a System.Windows.Forms.Control. From there we go look for the .Rows property and find... nothing. The basic Control type does not provide this property.
So why does it work in the debugger? At run time, we aren't dealing with base Control objects. We are dealing with types that inherit from Control, like Panel, TextBox, or DataGridView. It just so happens the DataGridView control does provide a Rows property. The debugger allows you to resolve this to use the run time type, but the compiler doesn't know what's going to happen once the program starts running; it has to be more strict. For example, you might have a button to remove the gridview and put something else there instead, and now the Rows expression would no longer be valid.
You can smooth over the compiler issue with a cast:
var dgv = (DataGridView)this.Owner.Controls[0].Controls[2].Controls[0];
var row = dgv.Rows[0];
But this is tightly-coupled to the form layout. Move something around in the designer and suddenly you're tracing through a lot of code to find out what went wrong. You will be better off setting a reference directly to the DataGridView in the child form before even showing it on the screen.
var child = new detailForm(this);
child.ParentGrid = this.DataGridView1; // assuming you declare a public property named "ParentGrid" and your control is named DataGridView1
child.ShowDialog();
Finally, you may be wondering whether you're expected to work through a data type expression chain like that for every line of code you use. I'd say you are expected to understand what data types you're working with at every level of every expression. Fortunately, this doesn't mean you need to have it all memorized. It doesn't take much experience to understand how the Windows Forms types fit together generally, so you can understand these data types without having to go chase through the documentation on every single thing. And speaking of "chasing through the documentation", when you really don't know, you are expected to know how to read the docs to find the answer.
Assuming this is a read-only operation, I would pass the DataGridView to the detail form. You could create a constructor for the detail form that takes a DataGridView as a parameter, or create some kind of public void Populate(DataGridView dgv) { } function that you call after the form constructor.
If you intend on changing data in the DataGridView from the detail page, and expect it to reflect on the main form, then you can either create a event on the detail form that the main form listens for, or after the detail form closes, retrieve the stored DataGridView and look for changes (or just overwrite).
Ok, I'm utterly confused by this situation, so bear with me.
In my application, if you click on a button I have an editor form open
dgEditor = new fmDataGridFieldEditor();
dgEditor.ShowDialog();
This works fine, and my form shows up and operates correctly. The form has a data grid in it with some specified fields. Now, if I then change data in one of the columns of the datagrid (a column that is just meant for numbers) and then change the sorting order by clicking on the column header, my form crashes. with an ArgumentException error that says "Object must be of type Int32" on the dgEditor.ShowDialog(); line.
I don't understand what is going on or even how to start debugging this. This doesn't happen when I modify existing rows, or if the rows I enter are already sorted (e.g. 0,1,2 is fine but 0,1, 0 causes the crash).
Furthermore, I have visual studio 2010 setup to break on all exceptions, not just unhandled ones, but I'm getting an exception in the same place.
Finally, I tied the data grid's ColumnSortModeChanged event to show a message box, but even when the sorts don't crash the form, the message box doesn't show.
I'm at a loss on how to proceed with this.
The debugger shows the last line of code that you wrote. Which is the ShowDialog() call. If you look at Debug + Windows + Call stack then you see the methods in the .NET framework that are involved. Scroll the window up if necessary to see them all. DataGridView has a lot of built-in functionality, the source code isn't readily available although you can get it from the Reference Source. Not that this will help much, there's rather a lot of it.
Clearly there's some invalid data in one or more rows. Looks like a leading space, only guessing here without sitting in front of your machine. Implement the CellValidating event so the user cannot enter an improperly formatted number.
I just had this happen to me in VB. What I discovered was that when I copied the values from a textbox into the grid I didn't do a cast to int. Being VB I assumed it would cast implicitly, but the cell value is, I guess, an object, so it happily took a string. Everything looked right and worked right, until I happened to sort on that column. Maybe this will help someone else.
ShowDialog will throw an error if you are trying to create a PictureBox dynamically on the TableLayoutPanel. It does not allow two PictureBox elements to add a similar index on the Table or you could have a error if your are using MemorySTream and not closing it properly.
I have application full of various controls databound to my classes. I would like to ask user "You are closing application and you made some changes. Do you want to save your changes?". For this I need to recognize that user made any changes.
How to catch user made changes in databound controls? Is textBoxXXX_TextChanged the only way to do this?
Thanks in advance for all your answers.
It depends on the datasource; for example DataTable and DataSet sources contain the GetChanges() methods which allow you to easily see if rows have been added/removed/modified. Other data sources will have their own implementations, if any. If there is no implementation then it's up to you to determine how to check for those changes.
In any event this is something you should do at the data-level, not the UI (by watching for "changed" events). Watching events doesn't scale beyond a couple controls and maintenance can be iffy.
Update: Not sure why I didn't think of it, but a second option is to add a BindingSource to your UI object and use it as a databinding proxy (your UI controls databind to the BindingSource and the BindingSource binds to the real datasource). It provides a better approach than handling all your individual "Control_Changed" events, and requiring rework of your other layers (esp. if they aren't custom data-types).
You need to provide custom logic for that, there's not really an automatic way of doing this. As I see it there are several options:
At the start of the editing, save a copy of the original data object, and when you need to check, compare the current object with the saved one. The comparison can be custom (field by field) or semi-automatic by use of serialization (compare the serialized forms) - if it is serializable.
In each of your data object's property set accessors, test for a change in value and mark the object as 'dirty'.
As been discussed, there are many ways to do this depending on how granular you want to get.
A relatively easy way using client side javascript would be to do something like the following:
Hook into the onchange events of the form elements. You could do this dynamically on page load using javascript/DOM.
When the onchange error handler is called, you could set a page level variable: pageHasChanged = true;
Hook into the page's beforeonunload event (occurs when the user tries to navigate away from the page) and check the pageHasChanged variable to see if any changes were made. If changes were made you could alert the user.
This doesn't give you the detail of what changed, but would be fairly easy to modify to track which form elements changed.
I'm trying to use the selected value of a ListBox control to populate a TextBox with its Text property, and a HiddenField with its value property. It sounds simple enough, and I went with this:
currentGroupTextBox.Text = currentSiteGroupList.SelectedItem.Text;
currentGroupHiddenField.Value = currentSiteGroupList.SelectedValue;
But upon execution, ASP.NET returns an error:
Object reference not set to an instance of an object.
And highlights the first line. currentGroupTextBox and currentGroupHiddenField are two controls which are enabled in the ASPX file so I'm not too sure why ASP.NET would complain about instancing them.
I'm going to try and pull together answers to all of your questions, including those in the comments.
Even if SelectionMode="Single", the listbox starts out without anything being selected, unless you specify which item should be selected in your code.
To test if the SelectedItem is null, use the following code:
if (currentGroupSiteList.SelectedItem != null) {
currentGroupTextBox.Text = currentSiteGroupList.SelectedItem.Text;
currentGroupHiddenField.Value = currentSiteGroupList.SelectedValue;
}
What does your Page_Load code that loads the listbox look like? Is it wrapped with a if (!Page.IsPostBack) check? If not, pressing the button and initiating a postback will reload the listbox, thus losing the SelectedItem that the user selected.
I'd be willing to bet that your first line is choking on referencing currentSiteGroupList.SelectedItem, as that seems the most likely candidate for being a null reference. Make sure your code is executing in the right place within the ASP.NET page lifecycle, such that the SelectedItem is set properly behind the scenes.
SelectedItem from your currentSiteGroupList.SelectedItem is likely to be null (what represents no selection). You need to test it before assigning it to currentGroupTextBox.Text
Is it currentGroupTextBox or currentGroupTextBox that is null?
In debug if stop on that line..is it one or both that are not existing?
A common issue I find is that controls are placed on an asp formview or similar and so the reference to that control is not actually its id/name, but more likely you need to do;
TextBox myTextBoxReference = (TextBox) formName.FindControl("currentGroupTextBox")
string theValue = myTextBoxReference.Text
Another often issue is the page life cycle. So if your object is not on a form but maybe you are referencing it before it exists in the postback.
hth
I'm trying to use FindControl() to SET the values of some DropDownLists on a page.
for some reason, it appears that FindControl() is returning a copy of the control object as opposed to a reference, I was under the impression that this would not be the case due to the lack of a copy constructor for Controls?
<EDIT>
Ok, it apparears that it is not in fact returning a copy, it's simply not letting me set the visibilty of a control, the other properties I'm setting work fine.
Does anyone have any insight as to why this might be the case?
I've tried setting it in quick-watch mode then looking at the value straight away, and that isn't actually changing the value either!
</EDIT>
<EDIT> (two)
Ok, I'm doing this in Page_Load, and it's not in a gridview (I like how you guys assumed that one cus I was using FindControl()).
I'm doing this as there is a set of operations I have to perform on a dynamic number of similarly named lists, and it's MUCH better to do it in a loop than to hard code it.
</EDIT>
Can anyone help?
Cheers, Ed
Edit:
Whoa, wait... are you setting the Visible property to true rather than false?
In your comment, you mention it changes back instantly. The Visible property will evaluate to false if it's parent is set to be invisible, no matter how often you tell it the value should be true.
Where are you calling FindControl() and setting the Visible property?
For info, FindControl() gets a reference to a control in the current naming container, based on a string id and does not copy the control. In order to work with the control, you should cast it to the type of control that you expect -
DropDownList ddl = (DropDownList)e.Row.FindControl("myDropDownList");
this example would allow you to work with a DropDownList control in the OnRowDataBound event of a GridView.