Databound controls shouldn't update if they're not visible - wpf

I have a WPF application, and the design follows the standard MVVM model.
When the underlying data changes, my view model fires the PropertyChanged event so that the controls can update.
My main view is a tab control, so the majority of the controls are invisible at any one time. There are performance problems, and I've realised that much of the CPU time is dedicated to fetching data to update invisible controls. (My view model uses lazy evaluation, so it fires the PropertyChanged events, but doesn't actually calculate the final displayable properties until asked).
Does WPF have a standard way to deal with this problem?
Idealy, if an invisible control receives a relevant PropertyChanged event, it should just think "I must requery that property once I'm visible again".

I don't think there is any infrastructure to handle deactivating bindings associated with non-visible controls. Unfortunately there are many situations in which you would want a control that is not visible to participate in databinding. Most importantly, you often have a control whose visibility itself depends on a binding. Also, you might have a binding between properties of a visible control and a non-visible control. Or someone might want the exact opposite of what you want: the control to populate itself while non-visible and then jump out fully populated once visible.
I think the only good solution for your situation is to avoid having heavyweight non-visible controls, if that is possible. Specifically for your tab control, I would have thought that would be the default behavior, but perhaps it depends on your situation. Ironically some people complain that the TabControl destroys its children when switching between tabs and would like to know how to prevent that because keeping all the background tabs in memory takes some work. But you seem to have the opposite problem.
For reference, here is the source I mentioned for TabControl children:
Keeping the WPF Tab Control from destroying its children
You might be able to do some experiments in a small project to "turn on" the recycling behavior they are trying to turn off. If your control were loaded on demand then tab switching might be little slower but the performance on a tab would improve.

We did something along these lines in our base ViewModel..
Note, You have to freeze/thaw corresponding with the View's visibility.
It basically traps all the PropertyChanged events while frozen, and pushes them out when thawed. While also not keeping dupes, as they don't matter in our case.
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly HashSet<string> hashSet = new HashSet<string>();
private bool isFrozen;
protected void RaisePropertyChanged(string propertyName)
{
if (isFrozen)
{
lock (hashSet)
{
hashSet.Add(propertyName);
return;
}
}
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void Freeze()
{
isFrozen = true;
}
/// <summary>
/// Enable PropertyChanged Events to fire again
/// </summary>
protected void Thaw(bool fireQueued)
{
isFrozen = false;
if (fireQueued)
{
lock (hashSet)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
foreach (string propertyName in hashSet)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
hashSet.Clear();
}
}
else
{
hashSet.Clear();
}
}
}

My base view model has a IsVisible property. When the view model is invisible just suppress property changed notifications. When it becomes visible fire off a property changed event for each property (pr pass in null to the property name)

Related

WPF Is there a simple way to update GUI from main thread [duplicate]

I'm a web and backend programmer by nature. Normally I try to avaoid making windows programs. Now I have to make a WPF client.
I have a background task that raises an event every often time. (It is working like a poller and when the criteria are met an event is raised). Noob as I am I wrote this code that was attached to the event to update the UI.
private void IsDisconnectedEvent()
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
}
This gives an exception because I am not on the same thread. After some googling I found that I should change the code with:
private void IsDisconnectedEvent()
{
Dispatcher.Invoke(() =>
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
});
}
This works, but this is not the only event and thus makes my code horrible ugly. Are there better ways to do this?
Regarding this:
This works, but this is not the only event and thus makes my code
horrible ugly
Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.
Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.
This means that there should be nothing like this:
UserWindow.Visibility = Visibility.Hidden;
anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.
Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>
Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.
Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.
When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.
Therefore our first step is to have all our ViewModels inherit from a class like this:
(taken from this answer):
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
//Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:
public class UserViewModel: PropertyChangedBase
{
private bool _showUserWindow;
public bool ShowUserWindow
{
get {return _showUserWindow; }
set
{
_showUserWindow = value;
OnPropertyChanged("ShowUserWindow"); //This is important!!!
}
}
}
Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:
public UserWindow() //Window's Constructor
{
InitializeComponent(); //this is required.
DataContext = new UserViewModel(); //here we set the DataContext
}
As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.
This same concept applies to EVERYTHING in WPF.
One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.
You can read more about that in the link and also the MSDN DataBinding page linked above.
Let me know if you need further details.

WPF ObservableCollection and Listbox.itemsource exception

Editing this entire post to clarify... I cannot seem to nail this:
BackgroundWorker receives data from a WCF service that is a list of objects. The service reference is configured to be ObservableCollection.
I pass the ObservableCollection via a delegate into my main UI thread and set it equal to the UI threads Local Collection.
A listbox is bound to this local collection and does not update. I've added the following to my collection:
public ObservableCollection<EmployeeData> _empData { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<EmployeeData> EmpData
{
get { return _empData ; }
set
{
_empData = value;
OnPropertyChanged("EmpData");
}
}
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(p));
}
This even fires but the PropertyChanged is always null. My XAML listbox has a binding declared as:
ItemsSource="{Binding Path=EmpData}"
No matter what I do EmpData updates but the ListBox does not, I've tried several other methods but nothing ever changes in the listbox, its always just null.
I've been working on this for over a day now, I cannot seem to get this whole automatic updating thing to 'click'.
I'm not sure that I understand exactly what you are doing, but here are a couple of suggestions.
Have a single ObservableCollection
Bind your itemcollection (or listbox, or whatever) to this
Depending on the user, clear and fill that observablecollection with list data
Have the background worker update the list and refresh the observable collection if anything has changed.
Ideally your EmployeeData class will implement the INotifyPropertyChanged interface, so that property changes will get automatically updated in your view.

How do I make a UserControl that can bind to an ItemsSource or DataContext?

I'm trying to write a simple 2D map editor. Here's my code so far. How do I code the UserControl class that binds to a map? I can't seem to find an example of a UserControl that handles the ItemsSource like the built in ListBox and DataGrid do. I'm thinking I need to find out when ItemsSource gets set and then write code that subscribes to CollectionChanged and PropertyChanged and creates/deletes/positions Images? Should I even be trying to do this when I have 3 ObservableCollections to bind to?
public class Map
{
public ObservableCollection<ObservableCollection<MapSquare>> Squares
= new ObservableCollection<ObservableCollection<MapSquare>>();
}
public class MapSquare
{
public ObservableCollection<MapTile> Items = new ObservableCollection<MapTile>();
}
public class MapTile : INotifyPropertyChanged
{
private CroppedBitmap bmp;
public CroppedBitmap Bitmap {
get{return bmp;}
set{ bmp = value; OnPropertyChanged("Bitmap");}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
When you say "like the built in ListBox and DataGrid", how similar do you mean? Do you just mean the broad ability to deal with a list-like source, or do you mean all the stuff that they can do - item generation, virtualization, item templating, container styling and so on?
The mechanism in ItemsControl that underpins all of the built in list binding in WPF is surprisingly complicated, so it would be a huge amount of effort to reproduce. Worse, there are lots of places where other bits of WPF know about ItemsControl and have special handling for it. (E.g., ScrollViewer and certain panels.) So it's not even possible to produce your own implementation that does exactly the same thing unless you also write your own replacements for all those other parts too.
ItemsControl is one of the most powerful features of WPF, but it's also one of the least well-factored. Basically, if you want its functionality, you pretty much have to use it.
So if you want your UserControl to include ItemsControl functionality, you'll need to put some sort of ItemsControl inside your UserControl and just wire your collection properties through to their ItemsSource properties. That may well be the best approach because you're getting the built-in ItemsControl implementation to do all the work for you.
But if you're only looking to reproduce some specific features, you could handle the collection change events. But I'd only go down that path if you've exhausted the option of getting ItemsControl to do it for you.

Changing the UI language dynamically

I have a multi-language silverlight application where the resources are stored in resx files for different languages and bound to the buttons' and labels' Content Properties inside the xaml.
When setting the thread's UI culture in the constructor of the silverlight's page every thing works fine, but when changing it based on user's selection (through combobox selection)
the interface doesn't change. I need the page to redraw the controls and rebind to the resource files based on the new thread's UI culture.
I am not sure what mechanism you are using to bind your view to your localization repository, but I am guessing that the problem lies with notification.
The view will only update the data in a binding when it gets a notification event. Most likely, the object with the localization data that you are binding to is not sending notifications when the culture changes.
You might consider adding an INotifyPropertyChanged to the object that holds your localization strings. Then, add a method to that class called "NotifyThatEverythingChanged". In that method, just send that the property string.Empty has changed, which tells the UI to update everything in the data context.
In my case, I have the object that the RESX auto-generated for me called MyUIStrings. It has a bunch of static strings in it. I derive from that class, and add the functionality to notify that everything has changed. The UI will act accordingly:
public class NotifyableUIStrings : MyUIStrings, INotifyPropertyChanged
{
public void NotifyThatEverythingChanged()
{
OnPropertyChanged(string.Empty);
}
protected void OnPropertyChanged(string propertyName)
{
var handlers = PropertyChanged;
if(handlers != null)
handlers(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}

Can't get a DataGrid to update

I have a WPF Toolkit DataGrid bound to an ObservableCollection of Car in my view model. Car has a PropertyChanged event and the setters of each of its two string properties fire the event. I also have the grid's SelectedItem property bound to a property in the view model of type Car, also called SelectedItem.
On the same window as the grid, I have buttons for add, modify and delete. Add and modify open a dialog window with two textboxes, one for each Car property. Delete just shows a confirm dialog then does the delete.
For add and delete, I add or delete an item from the ObservableCollection and the grid updates itself as expected. However, for modify it does not. At first, my Car did not use PropertyChanged and after some searching I found that it needed to for the grid to update when an individual item's properties changed. But now that I am using PropertyChanged, the grid still doesn't update.
I've tried changing the values of the SelectedItem in my view model as well as directly changing the item on the collection.
What am I doing wrong?
Make sure you're implementing INotifyPropertyChanged and not just raising a PropertyChanged event. Also, when raising PropertyChanged, you must pass "this" as the sender, otherwise WPF will ignore the event.
Below is a simple base class that implements INotifyPropertyChanged.
public class Person : INotifyPropertyChanged {
private string name;
public string Name {
get { return name; }
set {
if (name != value) {
name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Sounds like the classic problem with ObservableCollection. ObservableCollection only notifies of additions, deletions, etc. on it's self. It will NOT notify of changes to properties of whatever you have stored in it. This is why your add/delete operations work as expected.
What you should do is use a CollectionView and bind to that:
ObservableCollection<MyObject> myCollection = new ObservableCollection<MyObject>();
ICollectionView view = CollectionViewSource.GetDefaultView(myCollection);
using this method also has the benifit that grouping and sorting are built into the view.

Resources