I have the following class
public class LanguagingBindingSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Dummy
{
get { return String.Empty; }
set
{
PropertyChanged(this, new PropertyChangedEventArgs("Dummy"));
}
}
}
that is bound to elements in XAML like this
Text="{Binding Dummy,Source={StaticResource languageSource},Converter={StaticResource languageConverter},ConverterParameter=labelColor}"
The sole purpose of the LanguageBindingSource class and its Dummy method is to allow property notifications to update the bindings when one or more resources change. The actual bound values are provided by the converter, looking up resources by the names passed as parameters. See the comments on this answer for more background.
My problem is that the resources are changed by a process external to the XAML pages containing the bindings and I need a single static method that I can call to trigger property change notification for all instances of the binding. I'm struggling to figure out just how I might do that. All ideas will be most appreciated.
Modify your class as follows:-
public class LanguagingBindingSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate {};
public static void FirePropertyChanged(string key)
{
((LanguagingBindingSource)Application.Resources[key]).NotifyPropertyChanged("Dummy");
}
private void NotifyPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name);
}
public string Dummy
{
get { return String.Empty; }
set
{
NotifyPropertyChanged("Dummy"));
}
}
}
Now are any point where you need to fire off this change use:-
LanguagingBindingSource.FirePropertyChanged("languageBindingSource");
Where "languageBindingSource" is the resource key that you are also using in your binding Source property.
Related
I am using Aspect Oriented Programming in my WPF project. I have used it to decorate my viewModels with INotifyPropertyChanged interface and an implementation behavioral class as seen below:
Container.RegisterType<SomeViewModel>(
new Interceptor<VirtualMethodInterceptor>(),
new InterceptionBehavior(new LoggingBehavior(TraceEventType.Verbose)),
new AdditionalInterface<INotifyPropertyChanged>(),
new InterceptionBehavior<NotifyPropertyChangedBehavior>());
This works fine.
But when I have only get only properties like :
public bool IsDummy
{
get { return _isDummy; }
}
How do I call the OnPropertyChanged method from the ViewModel, which does the job of updating the View(UI). So if I update variable _isDummy to true, I should be able to call something which does the same job as OnPropertyChanged("IsDummY");
To notify the view that a value has changed you must raise the OnPropertyChanged event with the property which has changed. This is usually done by the standard implementation:
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Then in the setter of the property
public bool IsDummy
{
set
{
if (_isDummy!= value)
{
_isDummy= value;
OnPropertyChanged();
}
}
}
This syntax only works if it is called from within the property setter, alternatively you can call it when the property is changed elsewhere in the class
OnPropertyChanged(nameof(IsDummy));
Here I added a model to my viewmodel:
public dal.UserAccount User {
get
{
return _user;
}
set
{
_user = value;
RaisePropertyChanged(String.Empty);
}
}
I handle property change event...
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
This is the binding I use:
<TextBox Text="{Binding User.firstname, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
Why the propertychange event is not triggered on updating view?
PropertyChanged is used to notify the UI that something has been changed in the Model.
Since you're changing an inner property of the User object - the User property itself is not changed and therefore the PropertyChanged event isn't raised.
Second - your Model should implement the INotifyPropertyChanged interface. - In other words make sure UserAccount implements INotifyPropertyChanged, otherwise changing the firstname will not affect the view either.
Another thing:
The parameter RaisePropertyChanged should receive is the Name of the property that has changed. So in your case:
Change:
RaisePropertyChanged(String.Empty);
To
RaisePropertyChanged("User");
From MSDN:
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
(No need to refresh all the Properties in this case)
You can read more on the concept of PropertyChanged here
You can invoke a property changed event from another class. Not particularly useful if you have all the sources. For closed source it might be. Though I consider it experimental and not production ready.
See this console copy paste example:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
public class Program
{
static void Main(string[] args)
{
var a = new A();
a.PropertyChanged += A_PropertyChanged;
var excpl = new Excpl();
excpl.Victim = a;
excpl.Ghost.Do();
}
private static void A_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Event triggered");
}
}
[StructLayout(LayoutKind.Explicit)]
public struct Excpl
{
[FieldOffset(0)]
public A Victim;
[FieldOffset(0)]
public C Ghost;
}
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public class C : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void Do()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
}
}
}
I have a datagrid that is bound to a observableCollection of Employees
The user is allowed to do in line editing by double clicking the datagridRow.
When binding the property in question I also use UpdateSourceTrigger.
When I user presses the save button ,the saveCommand is triggered in my MVVM and I want to create a list of only the employees that I have had property modified.
All my ViewModels implements INotifyPropertyChanged.
Despite lots of links on google I cannot seem to find an example that takes you through or explain how to track the items that have changed in a observablecollection.
Can you help?
Create base class for your Employee, for example, EntityBase and enum describing its states:
public enum EntityState
{
NotChanged,
Changed,
Added,
Deleted
}
public abstract class EntityBase : INotifyPropertyChanging, INotifyPropertyChanged
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
private EntityState state = EntityState.NotChanged;
public EntityState State
{
get { return state; }
set { state = value; }
}
public EntityBase()
{
state = EntityState.NotChanged;
}
protected virtual void SendPropertyChanging(string propertyName)
{
if ((this.PropertyChanging != null))
{
this.PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
protected virtual void SendPropertyChanged(string propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
When one of your employes in collection changed - mark it with EntityState.Changed, and then you can request only changed entities from collection and process them as you want.
I'm not aware of any built in way to do what you want.
What I've done in the past is to implement a boolean IsDirty property on the objects in the collection. Then set the IsDirty property to true anytime you raise PropertyChanged.
I have a Silverlight application in which I implemented MVVM pattern.
In my application there is a child window on which I have ComboBox. I bound ItemsSource and SelectedItem of my combobox to a property (typeof ObservableCollection) and property of MyType appropriately. MyType is a "MODEL" derived from INotifyPropertyChanged. When my window is loaded I set values to this properties. But my combobox doesn't display selected item.
I found that when I set property which is bound to selected item (in ViewModel), the PropertyChanged event is null.
Can anyone help me. Thanks.
From the way you've described it the only thing being bound to is the ViewModel yet the only thing that implements INotifyPropertyChanged is MyType. Nothing is actually binding to the instance of my type to listen to its PropertyChanged event which is why its null.
It sounds like you haven't implemented INotifyPropertyChanged on your ViewModel.
PropertyChanged works fine, so it must be in your implementation of it. Simply implementing INotifyProperty changed isn't good enough, you have to explicity call the event.
For example, this will not work:
public class Model : INotifyPropertyChanged
{
public string Title { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
In order for it to work, you must raise the property changed. Easiest way is to encapsulate the logic in a method, like this:
public class Model : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Of course you can put the event and the method in a base class to inherit from so multiple models can take advantage of it.
I have a listbox defined in XAML as:
<ListBox x:Name="directoryList"
MinHeight="100"
Grid.Row="0"
ItemsSource="{Binding Path=SelectedDirectories}"/>
The SelectedDirectories is a property on the lists DataContext of type List<DirectoryInfo>
The class which is the datacontext for the listbox implements INotifyPropertyChanged. When the collection changes the items are added successfully to the list however the display does not update until I force the listbox to redraw by resizing it.
Any ideas why?
EDIT: INotifyPropertyChanged implementation
public class FileScannerPresenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private FileScanner _FileScanner;
public FileScannerPresenter()
{
this._FileScanner = new FileScanner();
}
public List<DirectoryInfo> SelectedDirectories
{
get
{
return _FileScanner.Directories;
}
}
public void AddDirectory(string path)
{
this._FileScanner.AddDirectory(path);
OnPropertyChanged("SelectedDirectories");
}
public void OnPropertyChanged(string property)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Try
ObservableCollection<DirectoryInfo>
instead - you're triggering a refresh of the entire ListBox for no reason, and you don't need to make your hosting class implement INotifyPropertyChanged - it could easily just be a property of the window. The key is to never set the property to a new instance. So:
class SomeWindow : Window {
public ObservableCollection<DirectoryInfo> SelectedDirectories {get; private set;}
SomeWindow() { SelectedDirectories = new ObservableCollection<DirectoryInfo>(); }
public void AddDirectory(string path) {
SelectedDirectories.Add(new DirectoryInfo(path));
}
}
If you end up using that FileScanner class, you need to implement INotifyCollectionChanged instead - that way, the ListBox knows what to add/remove dynamically.
(See Update below). WPF seems to be working alright. I put your code into a new project. The listbox updates whenever I click the button to invoke AddDirectory. You should not need any more code changes.
The problem seems to be something else.. Are there multiple threads in your UI?
I didnt have the FileScanner type. So I created a dummy as follows.
public class FileScanner
{
string _path;
public FileScanner()
{ _path = #"c:\"; }
public List<DirectoryInfo> Directories
{
get
{
return Directory.GetDirectories(_path).Select(path => new DirectoryInfo(path)).ToList();
}
}
internal void AddDirectory(string path)
{ _path = path; }
}
No changes to your FileScannerPresenter class. Or your listbox XAML. I created a Window with a DockPanel containing your listbox, a textbox and a button.
Update: Paul Betts is right. It works because I return a new list each time from the Bound property. Data binding with lists always messes me up.
With more tinkering, the easy way to do this is:
Make FileScanner#Directories return an ObservableCollection<DirectoryInfo> (which implements INotifyCollectionChanged for you). Change all signatures all the way up to return this type instead of a List<DirectoryInfo>
FileScanner and FileScannerPresenter themselves do not have to implement any INotifyXXX interface.
// in FileScanner class def
public ObservableCollection<DirectoryInfo> Directories
{
get
{ return _DirList; }
}
internal void AddDirectory(string path)
{
_path = path;
//var newItems = Directory.GetDirectories(_path).Select(thePath => new DirectoryInfo(thePath)).ToList();
//_DirList.Concat( newItems ); -- doesn't work for some reason.
foreach (var info in Directory.GetDirectories(_path).Select(thePath => new DirectoryInfo(thePath)).ToList())
{
_DirList.Add(info);
}
}