In my ViewModel I have an ObservableCollection of Person objects (that implement INotifyPropertyChanged) and a SelectedPerson property. These are bound to a ListBox in my view.
There is also the following Prism DelegateCommand in my ViewModel:
Private DelegateCommand _myCommand = New DelegateCommand(CanExecute)
Public DelegateCommand MyCommand {get {return _myCommand;}}
Private Bool CanExecute()
{
Return (SelectedPerson.Age > 40);
}
What is the most elegant way of calling MyCommand.RaiseCanExecuteChanged whenever the SelectedPerson changes and whenever the SelectedPerson's age changes?
Adding and removing property changed handlers in the SelectedPerson's setter seems a bit messy to me.
Adding and removing property changed handlers in the SelectedPerson's setter seems a bit messy to me.
That's how I do it, and I'm not sure what a cleaner alternative would be. If the command state depends on a sub-property, you need to observe the changes somehow. Be careful about unsubscribing, though, or you risk a memory leak if your Person outlives your view model. PropertyChangedEventManager and weak event handlers can help if you can't guarantee that you unsubscribe.
To keep things clean, I usually just have one handler that listens for any sub-property changes, which calls a RequeryCommands method (also called directly by view model methods), which in turn invokes RaiseCanExecuteChanged for all the commands in my view.
Related
I have a Control which inherits from NumericUpDown in WinForms.
I want to handle changes to the DecimalPlaces-Property of NumericUpDown, therefore I have both tried declaring a
void OnDecimalPlacesChanged()
{
MessageBox.Show("Moeeep");
}
and also manually subscribing the PropertyChanged-event in the ctor manually. It seems to just not fire when I update DecimalPlaces and I have no idea why.
Since Control does not implement INotifyPropertyChanged, only your class does. Because of that, the Control doesn't raise any event when the DecimalPlaces changes. No framework can inject that into their code.
For now, the best thing you have is to override the UpdateEditText method. It is called when the DecimalPlaces property changes. Note that this is of course not the only reason the method is called, so you might have a problem there...
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.
I have a Model which implements INotifyPropertyChanged and it may get updated by a background business thread. Its related ViewModel also implements INotifyPropertyChanged. And their View obviously binds to ViewModel. This View may be shown on multiple places, and my goal is that all of them get updated when the model changes.
I know that ViewModel should register for PropertyChanged event of Model. But I don't know when and where is the best place for this registering and deregistering. Specially about the deregistering, since I'm scared of having hundreds of VM event handlers on the Model for the VM/views that are not shown anymore.
Thanks in advance.
Is it an absolute necessity for you to limit the View not directly bind to the Model?
You can expose the Model as a property on the VM and then have your View directly bind to it thereby not having the VM subscribe to INPC from Model
something like:
public class MyViewModel: INotifyPropertyChanged {
...
private MyModel _model;
public MyModel Model {
get {
return _model;
}
set {
if (value == _model)
return;
value = _model;
RaisePropertyChanged(() => Model);
}
}
...
}
and in xaml (when MyViewModel is the DataContext):
<TextBlock Text="{Binding Model.ModelProperty}" />
Update:
Maybe this is of some help for tapping into the PropertyChanged events of Models in a "weak" fashion
IWeakEventListener
Using the central event dispatching of a WeakEventManager enables the handlers for listeners to be garbage collected (or manually purged) even if the source object lifetime extends beyond the listeners.
which is used in
Josh Smith's PropertyObserver
This should hopefully solve your issue of needing to explicitly un-register?
I've gotten around this issue by hooking in to model events on load and removing them on unloaded, the problem here is that the view model can miss events if it's off the screen. I usually just refresh the data quickly on load.
OnLoad - Refresh the VM data from the model and hook events.
OnUnLoad - remove any hooks that you've put in place.
I'm building a WPF app that connects to a SQL Server database using LINQ to SQL.
The main window of the app contains a ListView containing a series of detail views.
The ItemSource of the ListView is bound to a collection of detail view model objects exposed as a property on the root view model.
Each detail view model object composes several ICommand properties as well as a property exposing a detail model object, which in turn exposes the various data fields shown in the UI.
Analysis with the ANTS memory profiler shows that the objects being leaked are those contained in the detail model object, and some UI classes to which they are bound.
Instances of these objects from previous refreshes are not being garbage collected.
ANTS has a tool that allows the user to trace chains of reference to identify why unwanted memory is being retained. When I use it, I find that all of the chains that show up have an ICommand in them. Accordingly, I've removed the offending ICommand, and found that
the memory leak disappears.
Unfortunately, I need the ICommand to implement some important functionality. What is really confusing me is how it has a reference to the detail model object in the first place- they are two completely separate instance variables in the detail view model object.
Here is the constructor of the detail view model object (The reference to the RootViewModel is used for callbacks in some of the methods connected to the ICommands. I originally suspected that this might be causing a circular chain of references that might be the cause of the problem, but removing it doesn't seem to have any effect.)
public CarDataViewModel(CarData carDataItem, RootViewModel parentViewModel)
{
_parentViewModel = parentViewModel;
CarDataModel = carDataItem;
CompetingCheckboxStatus = CarDataModel.CurrentCar.Competing;
AcknowledgeAlarm = new ParameterlessCommand(AcknowledgeAlarmClicked);
Acknowledge = new ParameterlessCommand(AcknowledgeClicked);
ShowReport = new ParameterlessCommand(ShowReportClicked);
Cancel = new ParameterlessCommand(CancelClicked);
}
Here's the xaml where the bindings are set up - AcknowledgeAlarm is the ICommand, CarDataModel is the detail model object:
<ListView x:Name="itemGridView"Grid.Row="1"ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding CarDataViewModels}" IsSynchronizedWithCurrentItem="True" Margin="0,0,0,0">
<ListView.ItemTemplate>
<DataTemplate>
</DataTemplate.Resources>
<Button Command="{Binding AcknowledgeAlarm}">
<Border DataContext="{Binding CarDataModel}" BorderBrush="{StaticResource GrayFadeBrush}" Background="White" BorderThickness="5">
<Grid> . . .
The CanExecuteChanged event handler is likely implicated in the leak.
WPF expects ICommand implementations to use weak references to the event handlers. You're using a normal .NET event which uses strong references, which can cause this leak.
The way you are creating the ParameterlessCommand instance seems to imply that CanExecute will always be true, and you don't need the event at all.
Are you actually firing the event anywhere, or is OnCanExecuteChanged unused code?
If not, replace the event definition with:
public event EventHandler CanExecuteChanged { add {} remove {} }
This way the event does not store any handlers, and the view model avoids having a strong reference to the UI elements.
If you need to raise the event, the easiest solution is to use CommandManager.RequerySuggested, which matches the weak event semantics expected for ICommand:
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
}
remove {
CommandManager.RequerySuggested -= value;
}
}
Another thing you should do is implement INotifyPropertyChanged in your view model (if you haven't done so already), and use that instead of having individual NameChanged etc. events for each property.
This is because the logic in WPF dealing with the individual properties causes memory leaks when there is a reference from the view model back to the UI elements: http://support.microsoft.com/kb/938416
AFAIK you need to implement INotifyPropertyChanged even if you don't actually have any change events.
My guess is that fixing either of these two problems will make the leak disappear: the incorrectly implemented CanExecuteChanged causes a strong reference from view model to view, which is exactly the circumstance under which the lack of INotifyPropertyChanged causes a leak.
But it's a good idea to fix both issues; not just one of them.
I have the following property in my view model and the view is binding to this property.
All works fine except for a special case where the ActiveCategory (within the _catManager) can change from other events (outside of this view).
I don't want to expose the entire Category Manager in the view model so I'm only exposing what properties I need. What is the best way to do this so that the view gets notified of all changes, even those changes not triggered within this view model?
public ICategory SelectedCategory
{
get
{
return _catManager.ActiveCategory;
}
set
{
_catManager.ActiveCategory = value;
OnPropertyChanged("SelectedCategory");
}
}
Have your viewmodel hook into the _catManager's INotifyPropertyChanged event and have it relay the property change events through the viewmodel. When you see "ActiveCategory" come through, that means you need to raise an INPC for "SelectedCategory".
You need to delegate notification to whatever class _catManager is as well.
So a change to it's ActiveCategory property raises a notification.
One way would be to add a handler in the the class that has it as a property and then raise a notification that it's _catManager has changed somehow.