I should start by saying I'm relatively new to the MVVM pattern. Having said that, I've got two tabs I'm trying to load with large data sets. I would like for the first tab to load when the page is loaded, but when you click the other tab for the first to be unloaded and the second to be loaded (aka Lazy Loaded). Any ideas as to how to do that?
It's important to keep the UI responsive to the user and loading large amounts of data can have a negative effect on that experience. That's why in situations like yours I have the view-model call a method that uses a BackgroundWorker(s) to load the data. The earlier you do this in the app's lifecycle, the better your user's experience.
So, like mathieu suggested, bind the control's property to your view-model and have your view-model load the data in a non-blocking fashion.
You can bind the SelectedValue property of the TabControl to a property of your viewmodel.
This way you can unload/load the correct data when the property value changes.
Related
I started a project using TemplateStudio for WinUI (C#) 5.2. It uses the CommunityToolkit ListDetailsView Control for a "List Details" page.
What is the best way to load additional data when a user selects something from the list of results? I thought I would add two more properties to my ViewModel (one to handle the data, one to show a busy animation while the data is loading, both using ObservableObjects SetProperty), when the selected gets changed I fetch the data async for the selected item and update the ViewModel properties accordingly.
I can't seem to find a way how to access this data from the DetailsViewControl, and I'm not sure I do it the correct way anyway.
If I add a property to my model and change the value on changing the selected the data gets added, but the UI doesn't update itself (I tried _selected and Selected). Only when selecting another item from the results and going back to the previous shows the value.
What would be the best/correct way to lazy load additional data with this control? There would be about 4-5 parallel requests going on in the first release.
Later on I need a few buttons in the DetailsViewControl as well, where and how am I supposed to handle the commands?
Or am I better of not using this control and do it manually?
Ok, this is going to be a 1000ft long question, but there's a lot to cover so here goes:
I am creating a paged items control, the purpose of which is to display very large collections in a paged format. I've created a repository on GitHub which can be found here. I have removed any styling for simplicity.
Upon starting the application, it looks like this:
This is pretty straightforward really, there's navigation buttons, an items per page selector but that isn't really important. The problem here is when you click the button "Open New Window".
This will open a new MainWindow, but on the first window, the collection disappears, as shown below:
The image above shows the old window in front, as you can see, there is no list of content as there is on the new window.
So, after smashing my head against a wall for a couple of hours, I am in need of assistance. I'll provide an overview of how the project is structured.
AnagramPagedItemsControl
The control being used for displaying the content is a custom control called AnagramPagedItemsControl, it is responsible for handling navigation between pages. I think the key property here is the PagedCollection.
The PagedCollection dependency property holds the collection which is bound to the Models property in the TestItemsViewModel.
TestItemsViewModel
This is the DataContext of the MainWindow, each window instance should create it's own view model. The CreateTestItems() method is responsible for creating the list of test items.
LazyPagedCollection
The purpose of this collection is to encapsulate the logic of a paged observable collection, it only loads pages when they are needed, hence the laziness.
It exposes methods like NextPage which are called in the AnagramPagedItemsControl when the user clicks on the various navigation buttons. The view model can also call navigation on the LazyPagedCollection, this allows the view model to call navigation without having to go through the view to do it.
TL;DR
When I create a new Window, the content of the previous window disappears. The problem is almost certainly with the control however I am stuck as to how to fix the problem.
This is quite a large problem to look at so I'd be very grateful for anyone who can look into it. Again, the source code is here, please feel free to suggest alternatives or pick out anything that I may have overlooked.
Had some time to spare, so:
The problem is the setter for the CollectionView property in the style for AnagramPagedItemsControl in generic.xaml.
This does not instantiate a new ListBox every time the style is applied; it will just create the one ListBox, the first time the style is created, and use that value over, and over again. So in effect, every instance of MainWindow shares the same ListBox.
You can see this by setting the Tag property of PART_CollectionView to (for instance) "1" in SetupBindings(ItemsControl PART_CollectionView). When you open a new window, you'll see that PART_CollectionView.Tag contains the same value you previously assigned.
I was wondering if there is any difference between adding some controls/usercontrols (that take time to initialize ) in XAML or constructor and in Loaded event of some window.
I have a window where I use same usercontrol thrice. This is a usercontrol and takes some time to initialize itself. This window takes around 5 - 7 seconds to initialize itself with all three usercontrols initialized.
How should I approach adding them in window?
( Someone suggested that adding control after window is loaded will allow window to load in faster way than adding them in XAML/Constructor of the window.)
I would like to have some suggestion from experts on this topic as I don't know much about this.
Thanks.
There is nothing inherently wrong with deferring the loading of a window's content until after the window has been shown. The event you probably want is ContentRendered, which tells you the window's handle has been created, and the window shown.
I would suggest wrapping your window's content in a single "view", say a UserControl, such that you only need to set the window's Content and be done with it, as opposed to imperatively injecting multiple controls. It just makes things simpler. In essence, make your view a user control, and treat the window as the host of your view.
None of this is likely to reduce the time required to populate your window, but at least you'll be able to show something while the content is processed, even if it's just an empty window with a wait cursor.
Also, look for any places where you perform expensive operations on the UI therad (e.g., in your views or view models), and consider replacing those with asynchronous operations. You might find that you're able to load the UI faster than the underlying data, in which case you can display a more complete view with the appropriate wait/progress indicators until your data comes in.
I'm a bit confused as to what a viewmodel's role is beyond databinding. I have a menu built in silverlight. The menu has x number of menu items which is determined at runtime. One of the features I would like to add to this is that each menuitem has a different text colour when hovered over.
Is it the role of the view to have a colour selector method or should the view handle this in it's code behind?
Normally I would keep the coloring/styling in XAML if possible - My view of the ViewModel is that it is responsible for providing all the data (ie. not graphical stuff) from the Model in a manner the View can consume.
If it was complex logic that determined the color and it was to be reused - I might be tempted to put it in the ViewModel tho.
The view model is used by the data binding process as a "safe" way to allow you to sort/filter/group the records as seen by a specific control without (necessarily) making changes to the actual bound data set (that is, unless/until you tell it to). (FMI read Bea's article here.)
I agree with Goblin here, in that the presentation aspects like color might be best kept separate in the XAML, for example in the DataTemplate used by that control.
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).