wpf how to refresh bound viewmodel - c#

I'm a newbie to WPF and C# and am building my first app mostly by using code examples. I'm sure there might be some better ways to do this, that I'm not understanding yet, so I'm coming to you guys for some advice.
I have a treeview control with of a bunch of nested objects that is downloaded into an ObservableCollection viewmodel from a WCF Service that I also built. I have the viewmodel declared in the Windows.Resources of the XAML.
My treeview then binds to that StaticResource by its key name.
Items="{Binding Source={StaticResource MyCatalogModel},Path=Items, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
The data in the tree is saved locally to a file. When the viewmodel is instantiated it reads the file, or it creates it if it doesn't exist.
I have 2 related problems right now.
Sometimes the data object that is imported is rather large with lots of nested objects (children). This is taking a long time to update the tree. How can I speed this up? Can I "turn off" the Notify changed stuff of the ObservableCollection, and just reload (rebind?) the viewmodel when it's finished?
I'd like to give the user the ability to basically clear out all the items from the tree and start from scratch. I have code that dumps the underlying file and as I said, it will be recreated when a new viewmodel is instantiated, but I don't know how to "reset" the binding of the resource and the tree. How do I do this?
Thanks to all who respond and any code snippets will be greatly appreciated!!

I had a similar problem, where I had a very large amount of data in a collection - and the event for OnPropertyChanged was firing for each item in the collection. I added an extension with a method to add a range to an ObservableCollection. Here is the code for the extension.
public class SmartCollection<T> : ObservableCollection<T>
{
public SmartCollection()
: base()
{
_suspendCollectionChangeNotification = false;
}
bool _suspendCollectionChangeNotification;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suspendCollectionChangeNotification)
{
base.OnCollectionChanged(e);
}
}
public void SuspendCollectionChangeNotification()
{
_suspendCollectionChangeNotification = true;
}
public void ResumeCollectionChangeNotification()
{
_suspendCollectionChangeNotification = false;
}
public void AddRange(IEnumerable<T> items)
{
this.SuspendCollectionChangeNotification();
int index = base.Count;
try
{
foreach (var i in items)
{
base.InsertItem(base.Count, i);
}
}
finally
{
this.ResumeCollectionChangeNotification();
var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
this.OnCollectionChanged(arg);
}
}
}
So instead of an ObservableCollection its a SmartCollection. What I did was build my collection of objects into a List then you call the AddRange method and pass in your List of objects. This greatly improved the performance.
As far as recreating the tree - if its based on the viewmodel. Just new up the viewmodel that its bound to.

It's amazing how many times the same old questions come up again and again. Right, I have no idea why you are Binding to a StaticResource, but that is a bad idea.
Just create a public property of type ObservableCollection<T> in your view model, set an instance of it as the DataContext of your view in whichever way you prefer or know. Make sure you implement the INotifyPropertyChanged Interface correctly in the code behind, or declare a DependencyProperty instead. Then you can Bind directly to this property, let's call it Items:
<TreeView ItemsSource="{Binding Items}" ... />
When you have it set up this way, then all you need to do to empty or reset the TreeView is this (in the view model):
Items = new ObservableCollection<YourItemDataType>();
As for speed, it's hard to know what you're doing, but WPF is known for being slow when rendering large collections. I can't help with that, sorry.

Related

Extend an external object with an IsSelected Property for MVVM?

I have to create a small WPF/MVVM based GUI that shows the user a list of objects that I get from an external library. The user cannot directly edit those objects, but only select them for further usage.
At first I though I could directly use the given objects in a regular collection as I did not see any need for an INotifyPropertyChanged implementation, but then I noticed that I would need an IsSelected property so that the view model would know which objects are selected by the user and furthermore there is also one case where I have to select specific objects from the view model. This means I have to somehow add the said IsSelected property to make this scenario work in MVVM.
What options do I have?
Do I have to write a wrapper class that inherits from the external class and only extends it by the said IsSelected Property? This would mean I would also have to convert the list of objects that I get from the external library before I can use them.
Or is there maybe a more convenient way to extend the external object so that I can handle the selection in an MVVM based way?
You can define a collection of the selected objects on your viewmodel, like:
public class YourViewModel
{
public List<Thing> SelectedThings { get; } = new List<Thing>();
}
Because the SelectedItems property of the built-in WPF ListBox is not a DependencyProperty so it cannot be bound, you can manage your collection with a simple event handler like
<ListBox SelectionChanged="ListBox_SelectionChanged" />
in codebehind:
private YourViewModel vm;
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems)
{
vm.SelectedThings.Add(item);
}
foreach (var item in e.RemovedItems)
{
vm.SelectedThings.Remove(item);
}
}
If you want to set the selected items from the view model, too, I have found a good solution instead of event handler here: https://www.tyrrrz.me/Blog/WPF-ListBox-SelectedItems-TwoWay-binding

UWP app: insert / remove user control MVVM way

I'm learning UWP at the moment in an attempt to port an old Win32 to the new platform. I'm using Template10 and everything runs fine so far, except I'm bit confused on how to implement the problem below.
Problem: In a page, I have to constantly remove and insert user controls depending on a view model property. The user controls are fairly complex and they all look and behave differently. Imagine a wizard with back and next buttons. On every click I have to remove the old content and insert a new one, with completely different view model.
Question: What would be the recommended way of implementing this in a MVVM way?
At the moment, my only idea is to send a message from the page's view model and subscribe for the message in page's code behind where I can create the required component and insert it dynamically in the page (after removing the old one).
In MyPageViewModel:
public IComponentViewModel CurrentComponent {get; set;}
...
public DelegateCommand NextItemCommand = new DelegateCommand(() =>
{
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Publish(CurrentComponent);
});
In MyPage.xaml.cs code behind
public MyPage()
{
InitializeComponent();
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Subscribe(OnItemChanged);
}
private void OnItemChanged(IComponentViewModel viewModel)
{
switch (viewModel.Type)
{
case 1:
// create the new user control and insert it in the container
var component = new TypeOneComponent();
component.DataContext = (TypeOneCompoentViewModel)viewModel;
// ...
case 2:
...
}
}
Not sure this is the best approach tho.
I've been thinking about a Wizard approach lately myself. It seems to me that a FlipView with re-templated left/right buttons is the easiest approach. My WizardViewModel would have several children view-models; something like Page1ViewModel, Page2ViewModel, and so on. I strongly feel that each page view-model would have a dedicated UserControl so the UI can be unique but not dynamic - I think it makes sense to design against dynamic UI, while embracing an adaptive UI - which is a different concept altogether.
The pseudo code might look like this:
public interface IWizardPage { }
public class Page1ViewModel : ViewModelBase, IWizardPage { }
public class Page2ViewModel : ViewModelBase, IWizardPage { }
public class Page3ViewModel : ViewModelBase, IWizardPage { }
public class MainPageViewModel : ViewModelBase
{
public IWizardPage CurrentPage { get; set; }
public IWizardPage Page1ViewModel { get; set; }
public IWizardPage Page2ViewModel { get; set; }
public IWizardPage Page3ViewModel { get; set; }
}
And this:
<FlipView Template="{StaticResource WizardFlipView}"
SelectedItem="{Binding CurrentPage, Mode=TwoWay}">
<Page1UserControl DataContext="{Binding Page1ViewModel}" />
<Page2UserControl DataContext="{Binding Page2ViewModel}" />
<Page3UserControl DataContext="{Binding Page3ViewModel}" />
</FlipView>
This is just a recommendation. But to answer your question, this would be very amenable to the MVVM pattern. I also think this would allow for you to be very flexible without getting so dynamic that maintenance is affordably time consuming. There are lots of ways to do wizards. I think this would be a fine solution, good with the MVVM pattern and fine with Template 10.
Best of luck.
I typically use an ItemsControl. This allows you to have a generic item template and a item specific template if you want and you can add / remove items at will by binding to the ItemsSource.
In your example of a wizard, you might make the main wizard container an ItemsControl that only shows one item at a time and a page would be an "item". The distinction with MVVM is that you don't add child controls, you add data and then specify a template to render it. So your items are going to be simple databound poco objects.
For your actual example, I guess you can add child controls to the ItemsControl and they would render automatically without even using a template since a ContentPresenter knows how to render controls. I would still use data only classes though since one of the tenants of MVVM is to separate the data from the UI. So your child item would be a model and your item specific template would be the UI layout bound to the item data.

WPF gets "old" value when data in ListView change

I have the TreeView with objects. When I select item of these tree, than other control - ListView displays as its items properties of selected object. I want to save values of properties when in TreeView selection is change to other object.
So, is there a good way in WPF to gets values of "just before changing" items in ListView control? My idea for now is to override the PreviewMouseDown to check if user click tree node. By god way I mean better than mine. Maybe something in ListView template?
Indication that there is no need to change my idea with the PreviewMouseDown will be also good answer.
Could you please provide the relevant code snippets? I try to answer your question, but I'm not sure I understood it correctly.
If you bind the SelectedItem of you TreeView to a property (a.e. using MVVM pattern), you can save the values before actually setting the item.
Doing so in the setter is not so good though, because it becomes quite large then. I would have a setter like this:
private Foo bar;
public Foo Bar
{
get { return bar; }
set
{
OnPropertyChanging("Bar");
bar=value;
OnPropertyChanged("Bar");
}
}
Then you can listen to your own PropertyChanging events and do your stuff there:
private void this_PropertyChanging(object param, PropertyChangingEventArgs e)
{
switch(e.PropertyName)
{
case "Bar":
//Do you stuff
break,
}
}

Multiple ComboBoxEdit controls with same items

I am working on a user control which contains a SpinEdit (numeric up-down) control and a ComboBoxEdit. The option selected in the combo-box provides a factor by which number in the SpinEdit is multiplied. At the moment, I have something like this:
public class MyUserControl : DevExpress.XtraEditors.XtraUserControl
{
private static List<String> listItems;
static MyUserControl() // Populate the list of options with the default options
{
listItems = new List<String?();
listItems.Add("Option1");
listItems.Add("Option2");
listItems.Add("Option3");
}
public MyUserControl()
{
InitializeComponent();
// Add default options to the combo box
foreach (String item in listItems)
{
this.cboBox.Properties.Items.Add(item);
}
}
}
That works fine (note that the above is simplified, in reality the static list is a static Dictionary which maps the strings to the multiplication factor) except that I want to allow the user to add custom options to listItems and have these appear on every instance of this user control in my application. That is why listItems is static, as my hope was to do this.cboBox.Properties.Items = listItems; so that any additions to listItems would appear on every control. However, the Items property is read-only so I cannot do that.
How can I go about ensuring every instance of my user control has the same set of options, even if these are changed? Having the static members fire an event when the user changes the option list might do the trick, but that seems a bit overkill for something that looks as simple as this. Does anyone have any other ideas?
In your case is better to use LookUpEdit control instead of ComboBoxEdit. Just make some adjustments to it:
lookUpEdit1.Properties.ShowHeader = false;
You can use your listItems in that way:
lookUpEdit1.Properties.DataSource = listItems;
But there is some problem with Dictionary as DataSource. For DataSource you must use an collection that implements IList, ITypedList or IBindingList interface. So, you can convert your Dictionary to List or you can use this trick:
private static Dictionary<int, string> items;
//...
lookUpEdit1.QueryPopUp += lookUpEdit_QueryPopUp;
lookUpEdit2.QueryPopUp += lookUpEdit_QueryPopUp;
//...
private void lookUpEdit_QueryPopUp(object sender, CancelEventArgs e)
{
var lookUpEdit = (LookUpEdit)sender;
lookUpEdit.Properties.DataSource = null;
lookUpEdit.Properties.DataSource = items;
}
But I think is better to use List instead of Dictionary.
I don't know about the DevExpress controls (you might want to ask on their support forums), but in classic Windows comboboxes, the list is maintained inside the control. The Items property is a .Net wrapper that makes the internal structure easier to work with. So, it's not possible to assign the same data structure to each combobox. They have their own copy.
Your idea to have a publish/subscribe mechanism to what are now static lists seems like a reasonable solution.
If it were me, instead of having static stuff on the control class, I'd make that master list container a separate class and have a property on the control that takes an instance of that class. It'd make your overall architecture a bit more flexible and testable.

Strategy, State, Singleton... or a Composition thereof?

I am new on stackoverflow and relatively new to WPF.
I've wrapped my head around a half dozen weighty tomes of Patterns and Best Practices (as well as numerous posts here) but cannot seem to find the solution I am looking for.
My Problem: WPF / .Net 4 / C#
I have a text processor (of type Editor E) that can load one Document (of type Document D) at a time (strored as Editor.CurrentDocument). Several UI controls bind to the Document's properties (all Dependency Properties) such as Document.Title, Document.DateLastModification.
Now I want to be able to switch the actual Document instance without having to unhook and re-hook all event handlers. So I guess the Editor.CurrentDocument property must somehow remain its instance while switching its implementation.
I have tried to create a SingleInstanceDocument class that inherits directly from Document and uses the Singleton pattern. But then I cannot find a way to inject any Document instance into the SingleInstanceDocument without having to internally re-map all properties.
Am I somehow being misguided or missing the point here? If the SingleInstanceDocument approach is a viable solution, is there any way I can use reflection to re-map all available dependency properties from the inner Document to the outer SingleInstanceDocument shell automatically?
Thank you very much!
Addendum:
It turned out that the functionality required here was already provided by WPF/.NET out of the box by implementing INotifyPropertyChanged on the CurrentDocument host object. Thus changing the current document caused the UI to update its bound controls appropriately. I'm sorry for all the confusion.
first, learn some basic MVVM pattern. basically in WPF-MVVM just use ObservableCollection and INotifyPropertyChanged interface.
this type of collection implements the observer pattern that notify update to UI(View) when you add/remove or "select" the current item.
//in main ViewModel
private Ducument _currentDocument;
public Document CurrentDocument
{
get { return _currentDocument; }
set
{
_currentDocument = value;
NotifyPropertyChanged("CurrentDocument");
}
}
//stored all loaded documents as collection.
public ObservableCollection<Document> Documents { get; set; }
binding selected - current item.
<ListBox ItemsSource="{Binding Path=Documents}" SelectedItem="{Binding Path=CurrentDocument}" DisplayMemberPath="Title">
<!-- //all Document.Title as listitem -->
</ListBox>
<!--// Editor's View -->
<ContentControl DataContext="{Binding Path=CurrentDocument}"></ContentControl>

Categories