I try not to post questions like this, but i've really been struggling to find an answer or similar example. I have what I think is a really simple example I'd like to setup.
Basically I want to use commanding to add an item from a textbox to a listbox. I want to make sure there is something in the textbox via CanExecute and i want to make sure that its not already in the list.
I know this seems over complicated for what it is, but it hits on some points I've been struggling with.
<Window x:Class="Commanding_Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local ="clr-namespace:Commanding_Example"
Title="MainWindow" Width="200" Height="300">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<StackPanel>
<TextBlock>Name</TextBlock>
<TextBox></TextBox>
<Button>Add</Button>
<Button>Remove</Button>
<ListBox ItemsSource="{Binding People}" DisplayMemberPath="Name"></ListBox>
</StackPanel>
</Window>
I have a Person Class
class Person
{
public string Name { get; set; }
}
The only reason I have this is that the Add needs to create a new object, so slightly more complex than a simple string.
And then a basic view model
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
People = new ObservableCollection<Person>();
People.Add( new Person {Name = "jimmy"});
}
public ObservableCollection<Person> People { get; set; }
#region Default INotifyPropertyChanged implimentation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
So the question is, how would I implement commanding so that it uses the CanExecute to disable the Add button if the name is already there or the Name field is empty.
And then the same deal for Remove, only enabled if a name is selected in the list.
I'd like to make this as MVVM compliment as possible.
I've seen that you can do the Button.CommandBindings attached property to inject the methods you'd like to use for each, but that doesnt seem completely MVVM happy.
Also, i'd like to avoid the use of frameworks (Prism/Caliburn.Micro) since this is primarily for education reasons.
Also any references would be greatly appreciated. I've read many blogs etc but I always feel like they stray off before implementing a complete, simple example.
how would I implement commanding so that it uses the CanExecute to
disable the Add button if the name is already there or the Name field
is empty
I will show how to do the add, the remove is similar and I leave that for you to figure out. First I will show the xaml changes with the button using an AddPerson command:
<TextBox Text="{Binding CurrentPerson,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged
}"/>
<Button Command="{Binding AddPerson}">Add</Button>
We have bound the current edited text to a new property on the View Model named CurrentPerson. This is done because we want to access what the person enters, but also we need the binding updated as the user types. To accomplish that updating, we specify that the binding updates by setting the UpdateSourceTrigger attribute to be PropertyChanged. Otherwise our the CurrentPerson string and ultimately the command Can operation would only fire when the edit text box lost focus.
ViewModel
The viewmodel will subscribe to the AddPerson command. Execution of that will add the user, but also check a can method which returns a boolean whether to enable the button or not. The can will be excecuted when the CurrentPerson property changes where we ultimately call RaiseCanExecuteChanged on the commanding class to have the button check the can method.
(This VM is abbreviated for the example and based on your full VM)
public OperationCommand AddPerson { get; set; }
public string _currentPerson;
public MainViewModel()
{
People = new ObservableCollection<Person>();
People.Add(new Person { Name = "jimmy" });
// First Lamda is where we execute the command to add,
// The second lamda is the `Can` method to enable the button.
AddPerson = new OperationCommand((o) => People.Add(new Person { Name = CurrentPerson }),
(o) => (!string.IsNullOrWhiteSpace(CurrentPerson) &&
!People.Any(per => per.Name == CurrentPerson)));
// When the edit box text changes force a `Can` check.
this.PropertyChanged += MainViewModel_PropertyChanged ;
}
void MainViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "CurrentPerson")
AddPerson.RaiseCanExecuteChanged();
}
Finally here is the commanding class used which is based on my blog article Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding.:
public class OperationCommand : ICommand
{
#region Variables
Func<object, bool> canExecute;
Action<object> executeAction;
public event EventHandler CanExecuteChanged;
#endregion
#region Properties
#endregion
#region Construction/Initialization
public OperationCommand(Action<object> executeAction)
: this(executeAction, null)
{
}
public OperationCommand(Action<object> executeAction, Func<object, bool> canExecute)
{
if (executeAction == null)
{
throw new ArgumentNullException("Execute Action was null for ICommanding Operation.");
}
this.executeAction = executeAction;
this.canExecute = canExecute;
}
#endregion
#region Methods
public bool CanExecute(object parameter)
{
bool result = true;
Func<object, bool> canExecuteHandler = this.canExecute;
if (canExecuteHandler != null)
{
result = canExecuteHandler(parameter);
}
return result;
}
public void RaiseCanExecuteChanged()
{
EventHandler handler = this.CanExecuteChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
public void Execute(object parameter)
{
this.executeAction(parameter);
}
#endregion
}
Well, MVVM is just a pattern or philosophy, so I think your desire to avoid using a framework might be a little misguided. Even if you're not using one of those frameworks, you will essentially be writing your own framework in order to implement the MVVM pattern.
That being said, probably what you want to use is a DelegateCommand or one of the similar implementations. See: http://www.wpftutorial.net/DelegateCommand.html. The important part that I think you are looking for is that the command that the WPF button is binding to must raise the CanExecuteChanged event whenever there is a change made in the view model which affects whether the command can or cannot be executed.
So in your case, for example, you would want to add a call to the CanExecuteChanged of your AddPersonDelegateCommand to your OnPropertyChanged method (possibly filtered by the name of the property that was changed). This tells anything bound to the command to call CanExecute on the command, and then you would have your logic in that CanExecute that actually determines if a person with the entered name already exists.
So to add some sample code, it might look something like this:
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
People = new ObservableCollection<Person>();
People.Add( new Person {Name = "jimmy"});
AddPersonDelegateCommand = new DelegateCommand(AddPerson, CanAddPerson);
}
// Your existing code here
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if(propertyName == "NewNameTextBox") AddPersonDelegateCommand.RaiseCanExecuteChanged();
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public DelegateCommand AddPersonDelegateCommand { get; set; }
public void AddPerson()
{
// Code to add a person to the collection
}
public bool CanAddPerson()
{
return !People.Any(p=>p.Name == NewNameTextBox);
}
public string NewNameTextBox
{
get { return _newNameTextBox; }
set
{
_newNameTextBox = value;
OnPropertyChanged();
}
}
}
*Note: In this sample your <TextBox></TextBox> would need to be bound to the NewNameTextBox property on the view model.
Related
Good day, I'm new to WPF with MVVM, the problem in general is when filling a combox, but I'll give you some context:
I have a user control that contains different controls, including a combobox which I try to fill from the modelview but I am not successful. The user control is invoked from a main window in a few words the flow would be something like this
mainview.xaml->usercontrol.xaml->usercontrolmodelview.cs
this is where I define the source of the combobox that is inside the user control (usercontro.xaml):
<ComboBox x:Name="cbConcept" Width="150" ItemsSource="{Binding Path=Concepts}" DisplayMemberPath="textConcept" />
in this it is in usercontrolmodelview.cs linked to my user control, I define a list called Concepts that I fill it through a service (the service if it returns information and fills the list).
private IEnumerable<Concept> _concepts;
public IEnumerable<Concept> Concepts { get => _concepts; set { _concepts = value; } }
public usercontrolmodelview()
{
AddItemCommand = new ViewModelCommand(ExecuteAddCommand, ValidateAddCommand);
_api = new Api();
_memCache = new MemCache();
_ = LoadCatalogs();
}
private async Task LoadCatalogs()
{
_concepts = _memCache.GetCache<IEnumerable<Concept>>(KeysCache.CompanyCache);
if (_concepts == null)
{
_companys = await _api.GetConcept();
_memCache.SaveCache(_concepts, KeysCache.CompanyCache);
}
}
and this way I invoke the user control in my main window (xaml):
<ContentControl Content="{Binding currentChildView}"
Grid.Row="2"
Margin="20"/>
this the code in the main principal (cs):
public ICommand cmdControl { get; }
private ViewModelBase _curretnChildView;
public ViewModelBase currentChildView
{
get { return _curretnChildView; }
set
{
_curretnChildView = value;
OnPropertyChanged(nameof(currentChildView));
}
}
public MainViewModel()
{
cmdControl = new ViewModelCommand(ExecuteShowAddUserControl);
}
private void ExecuteShowAddUserControl(object obj)
{
currentChildView = new usercontrolmodelview();
}
xaml code where the command that shows the user control is linked:
<RadioButton Style="{StaticResource menuButton}"
Tag="{StaticResource colorClosed}"
Command="{Binding cmdControl}">
</RadioButton>
as extra data the user control if it is displayed in the main window.
I have tried to change the type of source by datacontext and even so the combobox is not filled
Your usercontrolmodelview should implement INotifyPropertyChanged to tell your view when a property changed and the view needs to refresh that proprety. This is espacially important when dealing with async operations.
A sample implementation could look like this:
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
I also like to create a ViewModelBase class which implements this interface and make all my viewmodels inherit from it, but this is personal preference.
In the Setter of your public IEnumerable Concepts, you call NotifyPropertyChanged(nameof(Concepts));
Please note, that if you collection of concepts might change later, you should use a ObservableCollaction and call the PropertyChangedEvent on the CollectionChanged Event handler.
Hope this helps
~BrainyXS
I'm having some difficulty with Context Menu commands on my View Model.
I'm implementing the ICommand interface for each command within the View Model, then creating a ContextMenu within the resources of the View (MainWindow), and using a CommandReference from the MVVMToolkit to access the current DataContext (ViewModel) Commands.
When I debug the application, it appears that the CanExecute method on the command is not being called except at the creation of the window, therefore my Context MenuItems are not being enabled or disabled as I would have expected.
I've cooked up a simple sample (attached here) which is indicative of my actual application and summarised below. Any help would be greatly appreciated!
This is the ViewModel
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List<string> data = new List<string>{ "One", "Two", "Three" };
// This is to simplify this example - normally we would link to
// Domain Model properties
public List<string> TestData
{
get { return data; }
set { data = value; }
}
// Bound Property for listview
public string SelectedItem { get; set; }
// Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
The DisplayValueCommand is such:
public class DisplayValueCommand : ICommand
{
private MainWindowViewModel viewModel;
public DisplayValueCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (viewModel.SelectedItem != null)
{
return viewModel.SelectedItem.Length == 3;
}
else return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(viewModel.SelectedItem);
}
#endregion
}
And finally, the view is defined in Xaml:
<Window x:Class="WpfCommandTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCommandTest"
xmlns:mvvmtk="clr-namespace:MVVMToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<mvvmtk:CommandReference x:Key="showMessageCommandReference" Command="{Binding DisplayValue}" />
<ContextMenu x:Key="listContextMenu">
<MenuItem Header="Show MessageBox" Command="{StaticResource showMessageCommandReference}"/>
</ContextMenu>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TestData}" ContextMenu="{StaticResource listContextMenu}"
SelectedItem="{Binding SelectedItem}" />
</Grid>
</Window>
To complete Will's answer, here's a "standard" implementation of the CanExecuteChanged event :
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
(from Josh Smith's RelayCommand class)
By the way, you should probably consider using RelayCommand or DelegateCommand : you'll quickly get tired of creating new command classes for each and every command of you ViewModels...
You have to keep track of when the status of CanExecute has changed and fire the ICommand.CanExecuteChanged event.
Also, you might find that it doesn't always work, and in these cases a call to CommandManager.InvalidateRequerySuggested() is required to kick the command manager in the ass.
If you find that this takes too long, check out the answer to this question.
Thank you for the speedy replies. This approach does work if you are binding the commands to a standard Button in the Window (which has access to the View Model via its DataContext), for example; CanExecute is shown to be called quite frequently when using the CommandManager as you suggest on ICommand implementing classes or by using RelayCommand and DelegateCommand.
However, binding the same commands via a CommandReference in the ContextMenu
do not act in the same way.
In order for the same behaviour, I must also include the EventHandler from Josh Smith's RelayCommand, within CommandReference, but in doing so I must comment out some code from within the OnCommandChanged Method. I'm not entirely sure why it is there, perhaps it is preventing event memory leaks (at a guess!)?
public class CommandReference : Freezable, ICommand
{
public CommandReference()
{
// Blank
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute(parameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
//if (oldCommand != null)
//{
// oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
//}
//if (newCommand != null)
//{
// newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
//}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
#endregion
}
However, binding the same commands via a CommandReference in the
ContextMenu do not act in the same way.
That's a bug in CommandReference implementation. It follows from these two points:
It is recommended that the implementers of ICommand.CanExecuteChanged hold only weak references to the handlers (see this answer).
Consumers of ICommand.CanExecuteChanged should expect (1) and hence should hold strong references to the handlers they register with ICommand.CanExecuteChanged
The common implementations of RelayCommand and DelegateCommand abide by (1). The CommandReference implementation doesn't abide by (2) when it subscribes to newCommand.CanExecuteChanged. So the handler object is collected and after that CommandReference no longer gets any notifications that it was counting on.
The fix is to hold a strong ref to the handler in CommandReference:
private EventHandler _commandCanExecuteChangedHandler;
public event EventHandler CanExecuteChanged;
...
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
}
if (newCommand != null)
{
commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
}
...
private void Command_CanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, e);
}
In order for the same behaviour, I must also include the EventHandler
from Josh Smith's RelayCommand, within CommandReference, but in doing
so I must comment out some code from within the OnCommandChanged
Method. I'm not entirely sure why it is there, perhaps it is
preventing event memory leaks (at a guess!)?
Note that your approach of forwarding subscription to CommandManager.RequerySuggested also eliminates the bug (there's no more unreferenced handler to begin with), but it handicaps the CommandReference functionality. The command with which CommandReference is associated is free to raise CanExecuteChanged directly (instead of relying on CommandManager to issue a requery request), but this event would be swallowed and never reach the command source bound to the CommandReference. This should also answer your question as to why CommandReference is implemented by subscribing to newCommand.CanExecuteChanged.
UPDATE: submitted an issue on CodePlex
An easier solution for me, was to set the CommandTarget on the MenuItem.
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
More info: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html
I'm having difficulties with getting a bound textbox to update. I'm still new to WPF development and I must be missing a fundamental concept as I've read nearly everything available on the internet at this point and I'm still confused. Below is my code. First, an overview of what I'm doing to better set the context for my question.
Mainwindow is a Window that contains tabs that load various pages using frame source tags. I believe this might be causing me issues as I'm not sure where the actual object is getting instantiated for each tab, just that the XAML is being loaded.
Scratchpad is a class that contains a textbox, which is going to be updated and used by almost all classes that perform any type of operation to report status and any errors.
Textbox XAML (this is in "ScratchPad_View.xaml" for context)
<TextBox x:Name="scratchMessage"
Text="{Binding Path=ScratchMessage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Right"
Height="300"
Width ="500"
TextWrapping="Wrap"
VerticalAlignment="Top"/>
Code behind XAML
public partial class ScratchPad : Page
{
public ScratchPad()
{
InitializeComponent();
ScratchPad_Model ScratchPad_Model = new ScratchPad_Model();
this.DataContext = ScratchPad_Model;
}
}
Model Implementation
class ScratchPad_Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string _scratchMessage;
public string ScratchMessage;
{
get
{
return _scratchMessage;
}
set
{
if (value != _scratchMessage)
{
_scratchMessage = value;
OnPropertyChanged("ScratchMessage");
}
}
}
// Create the OnPropertyChanged method to raise the event
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Most of this I have cobbled together via responses to other questions on StackOverflow and reading numerous databinding tutorials however it's still not clicking. I'm not sure how to update the contents of the textbox and since I'm loading the page that contains the textbox in the XAML of my mainwindow I'm not sure I'm even referencing the correct object. The mainwindow loads this page in a frame tag, copied below.
<Frame Source="Common/View/ScratchPad_View.xaml" ></Frame>
In the code behind for this XAML, I have the following.
public partial class MainWindow
{
// Create scratchpad object for logging and status display
ScratchPad scratchPad = new ScratchPad();
public MainWindow()
{
InitializeComponent();
}
private void StartVault(object sender, RoutedEventArgs e)
{
// Creates the authentication prompt view object and pass the scratchPad reference for reporting
authPrompt_View _authPrompt_View = new authPrompt_View(scratchPad);
}
}
I pass the reference to the ScratchPad object that I created in the initialization of the mainwindow to all classes so that they can update the contents of the textbox, however I haven't had much luck in getting the binding to work. Once it works, I'm still not quite sure how I'm supposed to append text to the textbox. There's probably a great deal of problems here but I'm hoping to fix some of my conceptual problems and get a better understanding of what I'm doing wrong, thanks in advance!
You can use Application.Properties to set global properties for your project. So probably in SETTER method of textbox bound variable (in your case ScratchMessage), you need to set property in global application properties collection.
Below links explains it very well:
https://msdn.microsoft.com/en-us/library/aa348545(v=vs.100).aspx
http://www.c-sharpcorner.com/Resources/842/application-properties-in-wpf-and-c-sharp.aspx
My understanding is that , You have created the ViewModel for ScratchPad inside the constructor and assigning the DataContext in the same.
So, other windows will not have access to the DataContext.
My suggestion is that Maintain a base ViewModel class and inherit the base Viewmodel in all other ViewModel's.
Add ScratchMessage property inside base viewModel.
So you can access the ScratchMessage property from other viewModel's too.
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _scratchMessage;
public string ScratchMessage
{
get { return _scratchMessage; }
set
{
_scratchMessage = value;
this.OnPropertyChanged("ScratchMessage");
}
}
// Create the OnPropertyChanged method to raise the event
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ViewModel1 : BaseViewModel
{
ViewModel1()
{
this.ScratchMessage = "Message";
}
}
I have a scenario in wpf +mvvm i.e if my particular property changes in viewmodel1 then i wan to notify viewmodel2 having observable collection that property "A" has bee changed
1)I want to fire it for particular property not for all.
i have tried below code but not working .please let me know how cam i do this.
public class Model1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// Create custom event
public event EventHandler NotifyChange;
private string testProperty;
public string TestProperty
{
get
{
return testProperty;
}
set
{
testProperty = value;
// If changing properties, fire your OnPropertyChanged to update UI
OnPropertyChanged("TestProperty");
}
}
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
// Fire your custom event if a property changed
NotifyChange(this, null);
}
}
}
public class Model2 : INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public Model2()
{
// Assuming there is an accessible instance of model1
Model1 m1Instance = new Model1();
// Hook up your NotifyChange event from model1
m1Instance.NotifyChange += Model1Changed;
}
private void Model1Changed(object sender, EventArgs e)
{
// this will be triggered on change in model1
}
private void OnCollectionChanged(object singleObject)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, singleObject));
}
}
Use PubSub Events
My suggestion would be to look into PubSub events.
My recommendation of doing this, is to use Prism. Here's some more information: http://www.c-sharpcorner.com/UploadFile/5ffb84/prism-event-aggregator-in-wpf-with-mvvm/
You will be sticking to proper MVVM practices in this case.
Here's MSDN's ever-useful guide: https://msdn.microsoft.com/en-us/library/ff649664.aspx
Have a really good read-up on how this works, and how to use/implement it.
Alternatively
This will work, but I would still defer to using PubSub events if possible.
You could try this:
public class Model1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string Property
{
get { return _property; }
set
{
_property = value;
OnPropertyChanged("Property");
}
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Model2
{
public Model2()
{
// You might be storing your Model1 as a property in the Model2?
// I don't know, but I've put it in the constructor just for example.
var model1 = new Model1();
model1.PropertyChanged += OnModel1PropertyChanged;
}
private void OnModel1PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Property")
{
// Do stuff here when the right property has changed in model 1
}
}
}
I have only new'd up a Model1 in the constructor of Model2 as an example - you might be assigning it and storing as a field or property elsewhere in the Model2 ViewModel.
This might be particularly useful if you have ViewModels within ViewModels (parent VM > child VMs).
I use parent > child VMs quite regularly, and I don't think it's against MVVM best practices, but I still use the EventAggregator, instead of events.
As a side note, if you are using C#6:
Use nameof(Property) instead of "magic strings" (e.g. "Property"). This makes for much easier refactoring and compiler can tell you about errors - but essentially does the same job. Use this in the OnPropertyChanged() call in your setter
You can also use the nameof keyword when checking the property name, with the same principle as above. Like this: if (e.PropertyName == nameof(Model1.Property)) ...
Use null propagation: PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));. Changes the method body of your OnPropertyChanged to a nice one-liner, whilst still doing the same job
I digress
I would always look at sticking to proper MVVM practices, where possible.
I use Prism's EventAggregator on a daily basis and will swear by it.
Have a good read up on PubSub Events (you can use any type of event aggregator, but I would say to use Prism's, preferably)
Hope this helps! :)
If this is just to notify Model2 I think you dont need all these implementations. You can do something like
public string TestProperty
{
get
{
return testProperty;
}
set
{
testProperty = value;
// If changing properties, fire your OnPropertyChanged to update UI
OnPropertyChanged("TestProperty");
//Here you can call a method of Model2 sating that its changed
Model2 m2Instance = new Model2();
m2Instance.ValueChanged();
}
}
Add the method ValueChanged in you model 2.
That's because you're not registering to PropertyChanged. You're registering your event handler on NotifyChange so that PropertyChanged in Model1 is NULL and so NotifyChange is not fired.
So, you need to implement your OnPropertyChanged as the following:
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
if (NotifyChange != null)
NotifyChange(this, null);
}
Finally, if you want your NotifyChange to be fired for a specific property, then adjust the code above to check for the propName before you fire the event.
I am a beginner to use MVVM in WPF and found that it seem impossible to change the value of a textbox or a label. Here is an example.
In Xaml:
The original value of Name is "Peter".
But after I press a button which invoke a command in the ViewModel and change the value of Name to be
"John". So, suppose the value of the text box will be changed to John as well. However, it doesn't change.
I have found a lot of example in the net and found that none of them implemented this kind of functions. What I have learnt from them is to use Command and ItemsSource of ListView.
The value of ListView will change when I use button to raise command to change the ItemsSource of the view. Its value will change automatically when the Binding to ItemsSource changed.
However, I cannot make the value of TextBox or Label change even the value of the bindings to them are changed already.
Actually, I am really quite young in MVVM. I think I still have so much that I don't know.
Could you give me an example of how exactly I should do to make change to textbox after a button click? By the way, I am not quite sure how to make command for button. It seem to involve so much codes that I found in the sample from the net. Is there any simplier way?
Thank you very much.
Your ViewModel needs to implement INotifyPropertyChanged .
Documentation see here
public class Bar : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string foo;
public string Foo
{
get { return this.foo; }
set
{
if(value==this.foo)
return;
this.foo = value;
this.OnPropertyChanged("Foo");
}
}
private void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged!=null)
this.PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
Your view model should implement INotifyPropertyChanged so that WPF knows that you've altered a value of a property.
Here is an example from
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private string customerNameValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
var listeners = PropertyChanged;
if (listeners != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged("CustomerName");
}
}
}
}