How to hold UI from updating in WPF - c#

How can I suspend all UI elements from updating, then change anything I want and open multiple windows and ..., then resume UI and let it update at once?
I need it because I want to open multiple tabs with contents at once, but don't like to show user the flickers and multiple refreshes.

You don't have to supsend the UI. If you use data binding you can update on the data context what you want. Until you don't call the PropertyChanged event on the DataContext, nothing will be updated.

Use Win32 API functions LockwindowUpdate and UnLockWindowUpdate
refer below article for more details.
http://weblogs.asp.net/jdanforth/archive/2004/03/12/88458.aspx

Related

How to display MainWindow before the business logic execution has completed

So I am new to MVVM with WPF and am having trouble with a few concepts.
So as soon as you run, my MainViewModel will set up the model and will start pulling from the database in order to populate a list which is binded to one of my controls.
Now the problem is that, after it pulls data from the db it then needs to listen to another source which will fire an event and then add to this list. Now, this data source will fire events infinitely, so I would like to display the UI with the DB-pulled data and then update this list as events fire.
My initial idea was to only start this listen for updates once I have loaded and presented my control, but how is this done using the OnLoaded event in MVVM, I think this has been my biggest struggle.
Thanks
Decided to go with Clemens' comment.
I now, bind the datasource in code and run the viewModel's listen method when the window is loaded, using the Loaded event. I am still not sure if this is a good MVVM solution so will probably change it again soon if it isn't.

How to detect when a ListView or any control is finished rendering?

I am working on a Uno project using both UWP And WASM. The project contains a ListView or GridView with a source that is an ObservableCollection. Since it is getting data from an API. I am displaying a progress indicator, what I am finding is that the progress indicator stays active until the ObservableCollection is populated with data items.
The issue is that there is a lag between when the ObservableCollection has the data and when the control is finished rendering the view. In this case, it is an ObservableCollection of images and the GridView or ListView does not populate.
I have not been able to identify and event that will tell me when to inactivate the progress indicator.
There's no event defined in the WinUI API that tells you when the UI has completed rendering, so you have to create your own. This can be worthwhile when you have a complex, heavy UI that takes a visible delay to render after it's obtained data.
There are a few possible strategies:
hide the progress indicator after a fixed delay.
Schedule a callback using CoreDispatcher.RunIdleAsync(). This should only be raised when the UI thread is not 'busy', which is a rough proxy for it having completed rendering. Eg: Dispatcher.RunIdleAsync(_ => progressRing.IsActive = false);
Specific to ListView/ItemsControl: You could check that the list actually has item views with the ContainerFromIndex() method, eg listView.ContainerFromIndex(expectedIndex) != null. Be careful as the list only creates containers for items that are actually in view.
Specific to Image: Images sourced from remote urls are loaded asynchronously. You can subscribe to the ImageOpened event to get a callback when the image is ready.
You'll probably need to experiment to find what works best in your scenario; this is not an exact science. If possible, it's a good idea to test across multiple devices with a range of performances.

Formatting winforms UI on a background thread

I have a form, "StartForm" where the user makes a combo box choice, and then clicks a button. From there I would like to:
Generate an instance of another form, "MainForm"
Populate the DataGridView on that form with around 50 000 rows
Loop through each row in the datagrid view and perform some processing on it. In my case, it's formatting the row colour based on the value of a cell, and updating some columns.
Display the form.
This is straight forward, however because of the large number of rows and the processing time on each one, the UI freezes when constructing the MainForm. So I need the MainForm to be constructed on a background thread, and a progress bar to fill up on the StartForm while this is going on.
I keep getting cross thread exceptions or my formatting is lost when using backgroundWorker, and control.Invoke() also seems to throw exceptions. Maybe I'm just not using these correctly...
Could someone please explain how to accomplish the above? I have tried to keep the question basic to understand so it can be helpful to others but if you would like my code then please ask.
Thanks!
I think you need to implement virtual mode for your data grid. Take a look on msdn article about implementing Virtual Mode in the DataGridView Control. Small sample:
grid.VirtualMode = true; // enable virtual mode
grid.RowCount = source.Count; // 50000
grid.CellValueNeeded += grid_CellValueNeeded;
grid.CellPainting += grid_CellPainting;
Use CellValueNeeded event handler to provide value for cell (i.e. select value from source). Use CellPainting event handler to set cell color based on cell value (use e.CellStyle property). If it takes long time to fill your source with data, you can do that in BackgroundWorker. But do formatting and displaying data in virtual mode - this is the best option, when you have huge amount of rows (btw consider apply some filtering - rarely users need 50000 rows of data at once).
By default, all the work is done on the UI thread which gives the illusion of 'crashing' since the UI becomes inaccessible during the heavy work. Creating a form itself on a thread that's not the UI thread is possible but would mean that the form is only ever accessible by the thread that created it, so it's unlikely that's what you want. You're getting the cross-thread exception because you're trying to modify the UI thread from another thread.
What you could try is to load and process all the data on a background thread, but load it into a new, temporary DataTable. Then, use DataSet.Merge on the UI thread (possibly in the RunWorkerCompleted event if you're using a BackgroundWorker) and merge the temporary DataTable with your main DataSet which is bound to the DataGridView.
That should mean all the processing and heavy work is done on a background thread, but the DataGridView control is updated on the UI thread, thus bypassing any invalid cross-threading. One side note is that you should test the performance of the DataSet.Merge.

Form reloading in C#.net

I have a form i am fetching many records from database that are rendering dynamical on from. When i am clicking on Delete button this is deleting as well but I want to re-render my form so user will feel that records is deleted completely. when i am writing code this.refresh(); this is not fetching values from database and i am not seeing record is completely gone. It is windows C# form.
The methods Form.Refresh and Form.Invalidate have nothing to do with causing the application to retrieve new data from the database.
Form.Refresh - redraws the form synchronously (i.e. Form.OnPaintBackground and Form.OnPaint are called directly on the current thread).
Form.Invalidate - redraws the form asynchronously (i.e. WM_PAINT message is sent to the window, so Form.OnPaintBackground and Form.OnPaint will be called by the UI thread when it handles messages).
In order to update records, you need to make your control retrieve updated data.
If you specify more details on how the Form pulls that data, someone can help you figure out how to update it. For example, are you using data binding? Did you write your own code to retrieve the records?
Basically, without seeing your code, I would say you can take whatever it is you are doing in the constructor and put it in a separate method (let's call it RefreshData). Then, call RefreshData after you delete records from the database.
So, you're creating new controls that represent the data. In that case, there's no mechanism to automatically do the updating work for you, you'll have to code it. You generally have two options:
Keep a reference to the controls you created, and remove them from the Form before updating it.
Implementing something more efficient, like maintain a Dictionary that maps from the Data Rows to the controls that represent it, so that you can go over the updated data, and for each row in your dictionary that disappeared from the data, remove the controls.
You may also want to consider using some control that has a built-in Data Binding mechanism that would save you lots of work. For example, a GridView.
Could you be looking for Invalidate() ?
If you are filling the form in constructor you need to extract loading code to a method.
Then call it on DeleteButton.OnClick event after performing delete.

How to implement CRUD Master Details on the same screen under MVVM

I have a MVVM (Prism) application that I need to implement a master details screen wheer the master is a listview and the details is displayed next to it. Read-only seems easy enough (haven't done it yet but I've got my head around WPF binding) but edit/add confuses me.
How to I make it so the master is not updated until the details is saved?
How do I make it so you can't change the master's current selection while in edit/add mode?
I've been googling a plenty but have not found any meaty examples of this.
Thanks.
PS: This view is a child view on a larger screen. This is why I want both master and detail together.
You certainly can do this, though in my opinion such a UI design fails to harness the full power of WPF. Old WinForms UIs usually didn't update most of the application until data was saved to SQL Server (or wherever) because they didn't have real business objects and a powerful binding system like WPF. Trying to copy WinForms limitations within WPF seems like a step backward to me. Why not show the latest data everywhere it is visible in the UI, including in the master view? Also, why not allow the user to edit multiple items before saving, for example marking any edited but unsaved item with an animated marker in the master view? Combine these with a generalized undo and you have a better design and more intuitive for the user.
However if your business requirements make it absolutely necessary, here is how to do it:
Preventing changes to data from being visible outside the detail until it is saved
Upon entry into your "edit/add mode", make a copy of the data objects and set your detail view's DataContext to the copy instead of the live object. When the data is "saved", copy the data from the shadow copy back into the live object and set your detail view's DataContext back where it should be.
Preventing the master's current selection from changing while in edit/add mode
Two possibilities:
During edit/add mode, change the master view to disallow mouse hit testing or keyboard focus
When edit/add mode begins, capture the "current selection" then add an event handler that watches for "current selection" changes and immediately changes the selection back to what it was. When edit/add mode ends, remove the handler. This handler can be conveniently coded using a lambda expression and using a closure on a local variable to store the current selection.
Thanks for the answer. Now I've re-read my message, I see it is rather vague. I have a screen that edits an object which contains multiple lists of other child objects. I've implemented these as different tabs in a tab control. One of these tabs edits the comments, so I wanted to display a list of comments with an edit panel for the current selection next to the list. The user could then use add, edit or delete buttons to update the list. I wanted to do this in a pure(ish) MVVM way.
I came up with the following design which seems to work with minimal hacks.
The View includes a list of the child objects simply as a ListView bound to an observable collection within the ViewModel. I included a child object buffer – this is used to buffer changes until they are ready to be saved back to the list (or thrown away).
The View also includes an edit panel bound to the buffer object in the ViewModel. The buffer is updated whenever the list view’s current selection changes using a deep copy. I tried using data binding on the Selecteditem property but the set was never called, so a small code-behind method was added to force the property to be updated when the selection was changed.
The list view and edit view are mutually exclusive. In theory you could hide the disabled one, perhaps using a flip screen. As a general pattern, it is better for my app to have both visible at the same time as the edit panel may show extra information not shown in the list view. The choice as to which panel is enabled is controlled by binding IsEnabled to a ViewModel property like IsEditCommentMode.
Commands to manage the list have to be added, these are New, Editand Delete. Note that Add and Edit will set set up the buffer then set IsEditCommentMode to true. These list management commands are only available when IsEditCommentMode is false.
The edit panel implements Save and Cancel commands, they are only be enabled when IsEditCommentMode is true. When Save is executed, it should copy from the buffer to the list (either add or update) and fire the change notification. Finally, it should set IsEditCommentMode to false.
This all works well and does not seem to violate any MVVM tenents (in my humble but often flawed opinion).

Categories

Resources