What would be the cleanest way to have a Save state for an application so that whenever a property or content was updated the "Save" options would become enabled.
For example, there is a menu and toolbar "Save" buttons. When the WPF app first opens both buttons are disabled. When the user updates the properties or document, the buttons become enabled until a "Save" is done, at which point they go back to disabled.
Bind IsEnabled to a ViewModel that exposes an "IsDirty" or "HasBeenModified" boolean property, or something of similar ilk. The ViewModel would watch for changes to the Model and set IsDirty to true if the Model is modified for any reason. When saved, the ViewModel can be told to set IsDirty to false, disabling the button.
You are using the Model-View-ViewModel pattern, right? Here are a few links on the pattern to help you on your way, just in case:
http://en.wikipedia.org/wiki/Model_View_ViewModel
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
http://www.wintellect.com/CS/blogs/jlikness/archive/2010/04/14/model-view-viewmodel-mvvm-explained.aspx
Are all the properties you wish to watch for in the same class? If so, something like this will work:
Have the class derive from INotifyPropertyChanged;
Watch for property changes from the class and set an IsDirty flag when there are changes
Set IsEnabled for the Save button based on the IsDirty flag
When the Save command is executed, set IsDirty=false
Notifying Class Example
public class NotifyingClass : INotifyPropertyChanged
{
private string Property1Field;
public string Property1
{
get { return this.Property1Field; }
set { this.Property1Field = value; OnPropertyChanged("Property1"); }
}
private string Property2Field;
public string Property2
{
get { return this.Property2Field; }
set { this.Property2Field = value; OnPropertyChanged("Property2"); }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Watching for Property Changes
public partial class MainWindow : Window
{
private bool isDirty;
NotifyingClass MyProperties = new NotifyingClass();
public MainWindow()
{
InitializeComponent();
this.MyProperties.PropertyChanged += (s, e) =>
{
this.isDirty = true;
};
}
}
How you set the disabled/enabled state depends on what type of button/command implementation you are doing. If you would like further help just let me know how you are doing it (event handler, RoutedCommand, RelayCommand, other) and I'll check it out.
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));
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.
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'm trying to bind my window title to a property in my view model, like so:
Title="{Binding WindowTitle}"
The property looks like this:
/// <summary>
/// The window title (based on profile name)
/// </summary>
public string WindowTitle
{
get { return CurrentProfileName + " - Backup"; }
}
The CurrentProfileName property is derived from another property (CurrentProfilePath) that is set whenever someone opens or saves profile. On initial startup, the window title is set properly, but when ever the CurrentProfilePath property changes, the change doesn't bubble up to the window title like I expected it would.
I don't think I can use a dependency property here because the property is a derived one. The base property from which it is derived is a dependency property, but that doesn't seem to have any effect.
How can I make the form title self-updating based on this property?
That's because WPF has no way of knowing that WindowTitle depends on CurrentProfileName. Your class needs to implement INotifyPropertyChanged, and when you change the value of CurrentProfileName, you need to raise the PropertyChanged event for CurrentProfileName and WindowTitle
private string _currentProfileName;
public string CurrentProfileName
{
get { return __currentProfileName; }
set
{
_currentProfileName = value;
OnPropertyChanged("CurrentProfileName");
OnPropertyChanged("WindowTitle");
}
}
UPDATE
Here's a typical implementation of INotifyPropertyChanged :
public class MyClass : INotifyPropertyChanged
{
// The event declared in the interface
public event PropertyChangedEventHandler PropertyChanged;
// Helper method to raise the event
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName);
}
...
}