I have a C# program that has two elements, a TreeView and a DataGridView and two EventHandelrs. When an item is selected in the TreeView, the DataGridView with a table that displays some information and can be edited by the user. Each item in the TreeView has its own unique table. When the user unfocuses on the DataGridView, the software underlying software model is updated (saving the changes the user made):
aDataGridView_Leave(object sender, EventArgs e)
{
//Update the software model (save the current DataGridView)
}
aTreeView_AfterSelect(object sender, EventArgs e)
{
//Update the table that is shown in aDataGridView
}
My problem is that if the user de-focuses on the DataGridView by selecting a different item in the TreeView I essentially have a race condition between which of these events happens first (even though this is all on one thread). Obviously, in this case I want the DataGridView_Leave Event to fire first or the changes the user made to the table get blown away by a new table being loaded before the changes are saved.
Is there anyway graceful way to enforce what event happens first?
It sounds like you can resolve this by keeping an isDirty bool that is true when there are changes that must be saved. You need to track this yourself, for example in property setters. Check isDirty in aTreeView_AfterSelect and save if necessary, and in aDataGridView_Leave so that you don't save if there are no changes.
This should work, but it sounds like your set up is a little hacky, and it may be counter-intuitive to users that changes are saved when an element loses focus.
What comes immediately to mind is some sort of counter. Assign each event call a number and store the last one processed. That way you can check if it is an old event and ignore it. I haven't seen it used in C# exactly, but plenty of times with AJAX when network delay often causes this situation.
I created a simple windows form application with a treeview, gridview and text box to log events that occur. When you leave the gridview its leave event always fires before the treeviews after select.
Are you saving the data in the gridview asynchronously? I could see if you are saving asynchronously then the treeviews after select could fire before your async method grabbed the data to save from the grid.
If this is the case, push the gridviews data to a stack and pop it off in the method that does the save.
Related
I have a listbox, datagridview and textbox in winforms.
Here is what I am trying to do
When the user selects an item in listbox, datagridview is loaded with some data regarding to the item selected and also there is a autocomplete textbox with a custom source which changes dynamically based on the item selected.
datasource for datagridview and autocomplete textbox is from the rdbms (MYSQL).
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
string item=listBox1.SelectedValue.Tostring();
UpdateDataGridView(item);
UpdateACTextBox(item);
}
Until this operation is completed, user cannot able to select another item in listbox, it takes almost 10 seconds to finish.
To resolve this, I came across some solutions like asynchronous method.
Which is the best way to resolve this problem?
Backgroundworker,
async and await,
threadpool,
multithreading.
Please suggest something if I have not researched properly.
What is the conventional way developers follow to these types of problems?
UPDATE:
I forgot to mention that I am using VS2010. Since async and await is not supported in vs2010. I think I need to drop that option.
Thanks in advance
It seems to me that the problem is that the amount of data you are loading to the listView is to big. So i would suggest using a background task in combination with the VirtualMode of the listView. In virtual mode the datas are not stored inside of the listView you have to store the values outside in a list of your own. If needed the listView will call a event to ask you for the value. Simply return a dummy or use old values if the data is not available. This will let the listview simply blanke or showing the old values. After the values are finished copied in a list simply exchange the pointer (variable) of the list with a atomic operation. From that point on the new datas are used to update the list and not the old one. Call update function to load the new valuses. This methode allowes it although to update the data in the background and update it from time to time. Only drawback you need to store the values of your list twice. Inside of the old shown buffer and inside of the new currently updated one. I hope that helps.
So I have a website coded in c#, and when I load a certain form, it loads with it a several records from a database and populates a grid view. One event is a button click for a button marked resend. This method takes all items in the grid that are selected and will resend them. If the selected items don't follow a particular set of rules another form pops up and says that the records must be moved to resend. The user has the option to hit yes and move the records or no and continue on with their business.
I need to pass the list of selected items from the resend click to the yes click on the separate form, the only issue is the popup and the grid are technically the same form. I can load it and all that jazz, but the load event and all of the popup's code is located in the same form.cs as the grid. I haven't been able to make event handlers work. I normally create a custom EventArgs class and store the values in there, but since the two forms are technically the same, I'm not sure how to transfer the information properly. I keep getting a null reference exception when I try to access the list of selected values from my yes click event.
Any suggestions as to what I should do? Properties and variables haven't worked, which leads me to believe they are technically two different forms, I just don't know how to work with them in this context.
If you need code or want anything clarified let me know.
Store the list of selected items in Session cache, like this:
protected void buttonResend_Click(object sender, EventArgs e)
{
List<Items> listOfSelectedItems = new List<Items>();
// Put logic here for populating listOfSelectedItems
// Store listOfSelectedItems in Session cache
Session["SelectedItems"] = listOfSelectedItems;
}
Note: I made up the Items class that is stored in List<T>; obviously whatever class you have for holding the selected items will work or even just a list of strings.
Now in the "other" page, you can retrieve the listOfSelectedItems in Session, like this:
if(Session["SelectedItems"] != null)
{
List<Items> listOfItems = Session["SelectedItems"] as List<Items>;
// Put logic here to work with list of selected items
}
I'm pretty new to c# and Winforms and I'm wondering what is the best approach to the following screen design.
I have a window that contain a Datagrid wich would be read-only. Beneath the grid, I have the detail of the records in differents fields (textbox, combobox, checkbox).
What I want is that when the user click on an item in the datagrid, the data will be shown in the detail fields.
That part is pretty easy, but I want to be able to update the fields automatically, wich means, I would prefer to not have to press a Save button.
Let's say that I click an item in the datagrid, change some value in the detail fields and the I click on another item in the datagrid, then I also want to perform some validation and calculation before the record get updated.
What I was thinking at first was to get the button for "new", "edit", "save" action and lock and unlock the fields accordingly and keep a flag to know if i need to insert or update the data, but then I realized that I would prefer to not have thoses button and have the save performed automatically.
Is there any sample somewhere that does what I want?
Also, would you guys using the built-in databinding functunality or just use a dataset object in code?
Pretty common scenario.
On selected row change of grid you know which datarow you shoul bind to the other controls. when same event happens again you validate and save or cancel in case of errors.
You can make use of DataGridView.CellEndEdit Event to get the new value and DataGridView.CellBeginEdit event to get the old value and update your data if there is any change
I have a bit of an issue with something I'm working on in ASP.NET/C#. I am using a GridView to display all 'items' linked to an account, with 3 fields relating to the link, but also all files not linked to it. Next to all records is a checkbox so they can link/unlink the two, and modify the 3 fields relating to that link.
I am currently using the 'CheckedChanged' and 'TextChanged' events to update the entries, as opposed to looping through each row in the Grid, checking its current state and updating accordingly etc which would provide a lot of overhead.
Problem is,the events fire if the user clicks a 'Cancel' button. Is there a way to detect if this certain button has been Clicked, and stop the events from firing? Or do you guys have better ideas?
First of all, it sounds like your Cancel button's Clicked handler is assigned to your custom function somewhere. By default it shouldn't do anything.
However, you can use the Sender object to check the type of object that fired the event:
if (sender is Checkbox) {
// Do something useful
}
I have not tested this code.
I'm using the DevExpress Xtra TreeList control to display a hierarchical set of questions and responses - think of a complex survey form, containing sections, subsections and a variety of questions.
The form is working in unbound mode, with no dataset nor any databinding.
As a part of the information displayed for each question, some background is obtained by calling a webservice on a background thread; results from these webservice calls are then used to populate the TreeList via calls to TreeListNode.SetValue().
Presently, these calls to SetValue() are causing any active editors to close, discarding the user's current input - a very user unfriendly experience.
How can I ensure the user's editing process is unaffected by these background updates?
The only similar questions I've found have been on the DevExpress forums, where the suggestion is a forced commit of the user's entries, which avoids loss of data but otherwise does nothing to fix a poor user experience. Since these all dated from 2007, I'm hoping the situation has now changed. Is it possible to update nodes without altering the state of the users own activity?
Background: A typical screen would have 500+ rows, with the webservice call for for each row taking around 0.6s to return. Forcibly committing or cancelling the user's actions every 0.6s is simply not acceptable, and forcing users to wait for processing to complete (>5minutes) before they can make any changes is equally poor.
Short answer: You can't
Changing a value in the TreeList will result in any current user editing being cancelled, reguardless of the use of Binding or not.
Official response from DevExpress:
Unfortunately, there is no way to prevent an active editor from being closed when the data source values are changed. It is impossible to achieve because the TreeList should be always synchronized with the underlying data. This functionality is implemented via the IBindingList interface in a usual manner. In response to the "change" notification the treeList must refresh itself and, as a result, reload data. This causes the active editing state to be reset.
However, there are several different ways to introduce required functionality. For example, you can create a separate form that will contain a set of editors that will provide the ability to edit a specific object directly. Another possible way to achieve this goal is to create some intermediate storage that will cache all changes. The synchronization with the TreeList's data source should be performed by user request.
Well one way i achieved to do it is by ShowEditor() after a small time delay after an event which does an update (say 100 miliseconds). I would get a problem because of an notifypropertychanged update and i would hook on FocusedNodeChanged. For instance:
FocusedNodeChanged += OnNodeChanged;
private void OnNodeChanged(object s, FocusedNodeChangedEventArgs e)
{
_delayer.Start();
}
private void _delayer_Tick(object sender, EventArgs e)
{
ShowEditor();
_delayer.Stop();
}
_delayer is a Timer class with tick event. A bit crude but it does the trick.