I am learning to create a WPF application following the MVVM patern. I'm try change data in viewmodel from service class but it can work, here is example code:
In MainWindow.xaml:
<Grid Grid.Row="6">
<TextBox materialDesign:HintAssist.Hint="Status"
Text="{Binding Status, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
VerticalAlignment="Center" />
</Grid>
In MainViewModel.cs:
public class MainViewModel : BaseViewModel
{
private static MainViewModel _instance = new MainViewModel();
public static MainViewModel Instance { get { return _instance; } }
//...
// Status
private string _Status = "Status";
public string Status { get => _Status; set { _Status = value; OnPropertyChanged(); } }
public MainViewModel()
{
////
// => This command can change status
// Start
StartCommand = new RelayCommand<object>((p) => { return true; }, (p) =>
{
OutStatus("Task success!");
});
}
public void OutStatus(string status)
{
Status = status;
}
}
In UtilitiesService.cs
public static class UtilitiesService{
public static void SetStatus(){
// => Here i can't change Status and can't binding to MainWindow.xaml
MainViewModel.Instance.OutStatus("Change Status in service");
}
}
So how can I change a property in manviewmodel in service file.Sorry Im so noob :))
You are using different instances of MainViewModel for data binding and for updating.
Also don't use public static instances or members across the application. Instead directly pass around the instance (in your case MainViewModel and UtilitiesService).
Generally static class members like properties or fields introduce a potential memory leak, because the garbage collector can't collect them to free memory. It also makes unit testing difficult and defies the concept of object oriented language key features like encapsulation. It will make code hard to modify.
In the simplest scenario, you can create the MainViewModel instance in your MainWindow. You can also create a shared instance of UtilitiesService at this point as well.
It's unclear what purpose UtilitiesService has. If it is meant to update MainViewModel by other View Model classes you can do it your way. If it is meant to be used in the Model, then you shouldn't do it your way. In this case your MainVoewModel would listen to the UtilitiesService events to update itself. Because in MVVM the Model does never talk to the View Model.
The recommended C# naming convention suggests to name fields using the camelCase pattern (starting with a lower case letter). Microsoft Docs: Naming Guidelines
A TextBox.Text binding that is configured to bind OneWay is pretty useless. In this case the TextBox only serves as display. You should then use TextBlock instead.
MainViewModel.cs
public class MainViewModel : BaseViewModel
{
//...
// Status
private string _status = "Status";
public string Status { get => _status; set { _status = value; OnPropertyChanged(); } }
public MainViewModel()
{
////
// => This command can change status
// Start
StartCommand = new RelayCommand<object>((p) => { return true; }, (p) =>
{
SetStatus("Task success!");
});
}
public void SetStatus(string status)
{
Status = status;
}
}
UtilitiesService.cs
public class UtilitiesService
{
private MainViewModel MainViewModel { get; }
public void UtilitiesService(MainViewModel mainViewModel)
{
this.MainViewModel = mainViewModel;
}
public void SetStatus()
{
// Change MainViewModel.Status and update bindings in MainWindow.xaml
this.MainViewModel.SetStatus("Change Status in service");
}
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var mainViewModel = new MainViewModel();
this.DataContext = mainViewModel;
var sharedUtilitiesService = new UtilitiesService(mainViewModel);
// Pass the shared UtilitiesService instance to other view model classes
// to allow them to update the MainViewModel anonymously.
var otherViewModel = new OtherViewModel(sharedUtilitiesService);
}
}
MainWindow.xaml
<Window>
<TextBlock Text="{Binding Status}" />
</Window>
Related
I am struggling with Text binding in my WPF app.
Lets imagine that I have another working app (ex. windows service) with some data in it.
In my WPF app I would like to have folder "DATA" with class where data are introduced and in same folder another class which would include a void which will query my windows service
I would like to show this data in my WPF window.
To make it simpler - one class with data, one class with data changing and WPF window with showing this data.
Unfortunately I can not achieve this... When I am executing below code, my window is showing 0 instead 123.
I would like to achive that my window will show value 123.
file "Database.cs" in folder "Data" in project "example"
namespace example.Data
{
public class Database
{
private int _testInt = 0;
public int testInt
{
get { return _testInt; }
set { _testInt = value; }
}
}
}
file "Query.cs" in folder "Data" in project "example"
namespace example.Data
{
public class Query
{
public Database _database;
public void execute()
{
_database = new Database();
_database.testInt = 123;
}
}
}
file "MainWindow.xaml.cs" in project "example"
namespace example
{
public partial class MainWindow : Window
{
public Data.Database _database;
public Data.Query _query;
public int testInt
{
get { return _database.testInt; }
set { _database.testInt = value; OnPropertyChanged(); }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
_database = new Data.Database();
_query = new Data.Query();
_query.execute();
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
}
}
File MainWindow.xaml
<Window>
<TextBlock Text="{Binding testInt}"
Foreground="White"
FontSize="15"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="20,10,10,0" />
</Window>
P.S. If I will put
_database.testInt = 987;
to MainWindow.xaml.cs it is working properly - window is showing value 987 in textblock.
You have multiple instances of the Database object, a new one each time Query.execute is called and one in MainWindow constructor.
It's the data in the later that is displayed.
You should modify the content of this instance to see any change, for that, you must inject it in the Query object:
_query = new Data.Query(_database);
// ...
public class Query
{
private readonly Database _database;
public Query(Database database)
{
_database = database;
}
public void Execute()
{
_database.testInt = 123;
}
}
Finally you need a way to notify the view that the content as changed, that why Database should implement INotifyPropertyChanged.
But at this point it's badly named, because it's a model in the MVVM pattern.
you need to implement INotifyPropertyChanged
public partial class MainWindow : Window, INotifyPropertyChanged
from the MVVM view, I think these answers from Orace and Jason are on a good way, both do not solve the problem completely.
Let the Mainwindow implement INotifyPropertyChanged
Let the query accept the new value:
public void execute(int value)
{
//_database = new Database();
// inject _database like in the answer above
_database.testInt = value;
}
When your testInt changes, let the _query deliver the change down to the "database" (btw: you do it vice versa) See code below:
`public int testInt
{get { return _database.testInt; }
`set { _query.execute(value); OnPropertyChanged(); }`
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
_database = new Data.Database();
// the property change will change both the view and the model
testInt = 987;
}
Well, you have changed both model and view with one property change then, Good or not?!
Just for future users. There is small bug in Orace's answer: (It should be without "readonly" parameter, because below You are writing to it.
private Database _database;
public Query(Database database)
{
_database = database;
}
I currently facing the issue that my DataGrid binding is not refreshing the UI.
My ViewModel and Object inherit from INotifyPropertyChanged.
Here is my code:
XAML:
<DataGrid Grid.Row="2" DataContext="{StaticResource MainViewModel}" ItemsSource="{Binding TestCollection, Mode=OneWay}" AutoGenerateColumns="True"/>
ViewModel:
public class MainViewModel: ViewModelBase
{
private ObservableCollection<ProductDisplayItem> _testCollection;
public ObservableCollection<ProductDisplayItem> TestCollection
{
get => _testCollection;
set => SetProperty(ref _testCollection, value);
}
private async void SendSearch()
{
//MyCode
.....
IEnumerable<ProductDisplayItem> displayItems = DisplayItemHelper.ConvertToDisplayItems(products);
TestCollection = new ObservableCollection<ProductDisplayItem>(displayItems);
}
}
My Object:
public class ProductDisplayItem: ViewModelBase
{
private string _mfrPartNumber;
private double _unitPrice;
private int _stock;
public string MfrPartNumber
{
get => _mfrPartNumber;
set => SetProperty(ref _mfrPartNumber, value);
}
public double UnitPrice
{
get => _unitPrice;
set => SetProperty(ref _unitPrice, value);
}
public int Stock
{
get => _stock;
set => SetProperty(ref _stock , value);
}
public ProductDisplayItem()
{
}
public ProductDisplayItem(string mfrp, double unitPrice, int stock)
{
MfrPartNumber = mfrp;
UnitPrice = unitPrice;
Stock = stock;
}
}
And my ViewModelBase:
public abstract class ViewModelBase: IDisposable, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public void Dispose()
{
}
}
I also tried to add the items to the ObservableCollection instead of creating a new one, but with the same result.
I hope anyone can help me with that.
Thanks in advance
The most common cause of such errors is confusion about ViewModel instances: UI elements are bound to one instance, and you are modifying a collection in another instance.
Since WPF MVVM usually provides for using the main ViewModel in only one instance, try using Singleton.
Fresh topic with a similar question: Is it a correct approach to create static viewModel in MVVM?
First implementation option from there:
1) If:
in general, in principle, under no circumstances is it assumed that a ViewModel can have several instances at the assembly level in which it is created;
if this does not create any security problems, since the static instance can be accessed by everyone;
if static values are sufficient to create a single instance. In most cases, this means that the ViewModel has only one non-parameterized constructor.
Then in this case it is worth using Singleton.
Example:
public class MainWindowViewModel : ViewModelBase
{
// The only instance available outside of this class.
public static MainWindowViewModel Instanse { get; }
= new MainWindowViewModel();
// All constructors must be MANDATORY HIDDEN.
private MainWindowViewModel()
{
// Some code
}
// Some code
}
To get this instance in XAML, x: Static is used.
You can get the entire instance, or create a binding to a separate property.
<SomeElement
DataContext="{x:Static vm:MainWindowViewModel.Instance}"/>
<SomeElement
Command="{Binding ButtonCommandEvent,
Source={x:Static vm:MainWindowViewModel.Instance}}"/>
Ok I figured it out. It's the DataContext...
Works fine after removing it from xaml.
EDIT: Added concrete example to clarify what I trying to achieve.
Here is application scheme:
To make code simpler, I will use trivial Messenger class instead of event aggregator from Prism. Tuple contains Id and string payload.
public static class Messenger
{
public static event EventHandler<Tuple<int, string>> DoWork;
public static void RaiseDoWork(int id, string path)
{
DoWork?.Invoke(null, new Tuple<int, string>(id, path));
}
}
Model instance subscribe to messenger for knowing when to start work (if Id correct), and notify view-model when work finished.
public class Model
{
public int id;
public Model(int id)
{
this.id = id;
Messenger.DoWork += (sender, tuple) =>
{
if (tuple.Item1 != this.Id)
{
return;
}
var result = tuple.Item2 + " processed with id " + this.id;
this.OnWorkCompleted(result);
};
}
public event EventHandler<string> WorkCompleted;
private void OnWorkCompleted(string path)
{
this.WorkCompleted?.Invoke(null, path);
}
}
UserControlResult is responsible for payload processing and result output. To make code simpler, lets just trace output instead of putting it on UI. So XAML will be default.
Code-behind:
public partial class UserControlResult : UserControl
{
private ResultViewModel viewModel;
public UserControlResult()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ResultViewModel(id);
this.DataContext = this.viewModel;
}
}
View-model:
public class ResultViewModel
{
private Model model;
public ResultViewModel(int id)
{
this.model = new Model(id);
this.model.WorkCompleted += path =>
{
Trace.WriteLine(path);
};
}
}
UserControlButtons contains buttons, one of them should start processing of model in UserControlResult via messenger. To make code simpler, lets omit command implementation and just show its handler.
Code-behind:
public partial class UserControlButtons : UserControl
{
private ButtonsViewModel viewModel;
public UserControlButtons()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ButtonsViewModel(id);
this.DataContext = this.viewModel;
}
}
View-model:
public class ButtonsViewModel
{
private int id;
public ButtonsViewModel(int id)
{
this.id = id;
}
// DelegateCommand implementation...
private void StartWorkingCommandHandler()
{
Messenger.RaiseDoWork(this.id, "test path");
}
}
UserControlParent contains both UserControlResult and UserControlButtons. His only role is to pass Id to them, so he doesn't even need view-model.
Xaml:
<StackPanel>
<uc:UserControlResult x:Name="UserControlResult" />
<uc:UserControlButtons x:Name="UserControlButtons" />
</StackPanel>
Code-behind:
public partial class UserControlParent : UserControl
{
public UserControlParent()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.UserControlResult.Init(id);
this.UserControlButtons.Init(id);
}
}
And finally MainWindow contains two instances of UserControlParent. Its role to assign them different Ids.
Xaml:
<StackPanel>
<uc:UserControlParent x:Name="UserControlParent1" />
<uc:UserControlParent x:Name="UserControlParent2" />
</StackPanel>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.UserControlParent1.Init(111);
this.UserControlParent2.Init(222);
}
}
This will work: pressing button in UserControlButtons will start working in UserControlResult model, and both UserControlParent will working correct and independend thanks to Id.
But I believe that this chain of invoking Init methods is violates MVVM because code-behind (which is View in MVVM) should not know anything about Id value (which is relative to Model in MVVM). Talking that, I'm sure that Id is not part of view-model, because it doesn't have any presentation in UI.
How can I pass Id value from top window to "deepest" view-models without violating MVVM?
Original Question
Here is WPF application consisting from 3 UserControls:
UserControl3 is a part of UserControl2 content. I keep MVVM during developing and using Prism.
I need to invoke method of custom class (which is model in terms of MVVM) in UserControl3 from view-model of UserControl1. The restriction that custom class can't be singleton. I suppose to do it one of the following way:
Using event aggregator from Prism. UserControl1 view-model is publisher and UserControl3 model is subscriber. For this I'll need to create unique Id in Window and pass it to UserControl1 and UserControl3.
Creating service instance in Window and pass it to UserControl1 and UserControl3. Then UserControl1 will just invoke method of this instance.
Window pass UserControl2 instance to UserControl1. View-model in UserControl1 will just invoke method of UserControl2, which will invoke method of UserControl3 and so on.
It seems like 2 and 3 approaches violates MVVM. How would you resolve this situation?
I would use option 1. I use MVVM Light to send a message and whoever receives that specific message will fire off the service method. Loosely coupled.
I think I achieved truly MVVM implementation shown in simplified example below. Special thanks to Ed Plunkett's comment and Nikita's answer.
First, I don't need to pass unique Ids anymore. For identification of different ParentViewModel instances, I just pass them different Messenger instances (which replaces Prism's EventAggregator for the sake of simplicity):
internal class Messenger
{
public event EventHandler<string> DoWork;
public void RaiseDoWork(string path)
{
this.DoWork?.Invoke(this, path);
}
}
Second, it seems like in my particular case Model should not worry about Messenger's DoWork event. As soon as this event raised in one view-model (ButtonsViewModel), it is more appropriate for this event to be consumed by another view-model (ResultViewModel) rather than by Model itself. So Model simplified too:
internal class Model
{
public string Process(string input)
{
return input + " processed!";
}
}
Below demonstrated all view-models "from top to bottom".
internal class MainViewModel
{
private readonly Messenger eventAggregator1 = new Messenger();
private readonly Messenger eventAggregator2 = new Messenger();
public MainViewModel()
{
this.ParentViewModel1 = new ParentViewModel(this.eventAggregator1);
this.ParentViewModel2 = new ParentViewModel(this.eventAggregator2);
}
public ParentViewModel ParentViewModel1 { get; }
public ParentViewModel ParentViewModel2 { get; }
}
internal class ParentViewModel
{
public ParentViewModel(Messenger eventAggregator)
{
this.ButtonsViewModel = new ButtonsViewModel(eventAggregator);
this.ResultViewModel = new ResultViewModel(eventAggregator);
}
public ButtonsViewModel ButtonsViewModel { get; }
public ResultViewModel ResultViewModel { get; }
}
internal class ButtonsViewModel
{
private readonly Messenger eventAggregator;
public ButtonsViewModel(Messenger eventAggregator)
{
this.eventAggregator = eventAggregator;
this.StartCommand = new DelegateCommand(this.StartProcessing);
}
public DelegateCommand StartCommand { get; }
private void StartProcessing()
{
this.eventAggregator.RaiseDoWork("test path");
}
}
internal class ResultViewModel : ViewModelBase
{
private readonly Model model = new Model();
private string textValue;
public ResultViewModel(Messenger eventAggregator)
{
eventAggregator.DoWork += (sender, s) => this.DoWorkHandler(s);
}
public string TextValue
{
get { return this.textValue; }
set { this.SetProperty(ref this.textValue, value); }
}
private void DoWorkHandler(string s)
{
var result = this.model.Process(s);
this.TextValue = result;
}
}
Note that in ResultViewModel I replaced Trace.WriteLine with actual screen output (because now strings are without Id, so trace output the same). ViewModelBase just implements INotifyPropertyChanged.
Below demonstrated content part of all views "from top to bottom".
<!-- MainWindow.xaml -->
<StackPanel Orientation="Horizontal">
<views:UserControlParent DataContext="{Binding ParentViewModel1}" />
<views:UserControlParent DataContext="{Binding ParentViewModel2}" />
</StackPanel>
<!-- UserControlParent.xaml -->
<StackPanel>
<local:UserControlResult DataContext="{Binding ResultViewModel}" />
<local:UserControlButtons DataContext="{Binding ButtonsViewModel}" />
</StackPanel>
<!-- UserControlButtons.xaml -->
<Grid>
<Button Content="Test" Command="{Binding StartCommand}" />
</Grid>
<!-- UserControlResult.xaml -->
<Grid>
<TextBlock Text="{Binding TextValue}" />
</Grid>
And finally this two worlds are connected in App.xaml.cs:
private void App_OnStartup(object sender, StartupEventArgs e)
{
new MainWindow { DataContext = new MainViewModel() }.Show();
}
Seems like MVVM, but any remarks are welcome.
Often when I’m designing an MVVM application, the following scenario comes up. A Window, having multiple children, sharing the same data source.
I’m trying to decide on the best way to implement a single datasource for all children. There are 3 options I can think of, all with their own advantages and disadvantages.
Example
The Window has two child UserControls, each in their own tab.
UI is linked up like this.
In order to keep modularity and provide them with data, the same design is reflected in ViewModels.
The MainViewModel is set up like this.
public class MainViewModel : ViewModelBase
{
private readonly ChildViewModelA _childViewModelA = new ChildViewModelA();
private readonly ChildViewModelB _childViewModelB = new ChildViewModelB();
public ChildViewModelA ChildViewModelA { get { return this._childViewModelA; } }
public ChildViewModelB ChildViewModelB { get { return this._childViewModelB; } }
}
The MainWindow instantiates the MainViewModel and sets DataContext of the children. Child controls are bound to data properties.
public partial class MainWindow : Window
{
private readonly MainViewModel _viewModel = new MainViewModel();
public MainWindow()
{
InitializeComponent();
}
public MainViewModel ViewModel { get { return this._viewModel; } }
}
<Window x:Class="WpfApplication3.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfApplication3.View.Controls"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TabControl>
<TabItem Header="Tab 1">
<controls:ChildViewA DataContext="{Binding ViewModel.ChildViewModelA}"/>
</TabItem>
<TabItem Header="Tab 2">
<controls:ChildViewB DataContext="{Binding ViewModel.ChildViewModelB}"/>
</TabItem>
</TabControl>
</Grid>
</Window>
To prevent every ViewModel from retrieving the same data from the database, I want to load data in the MainViewModel and provide the children. However, there are multiple ways of doing this.
Example 1: Using a setter method on the children
public class MainViewModel : ViewModelBase
{
...
private readonly FakeDataManager _fakeDataManager = new FakeDataManager();
public MainViewModel()
{
this.CurrentPerson = _fakeDataManager.GetNextPerson();
}
private Person _currentPerson;
public Person CurrentPerson
{
get { return this._currentPerson; }
set
{
this._currentPerson = value;
this.RaisePropertyChanged("CurrentPerson");
this.ChildViewModelA.SetPerson(this.CurrentPerson);
this.ChildViewModelB.SetPerson(this.CurrentPerson);
}
}
public class ChildViewModelA : ViewModelBase
{
private Person _currentPerson;
public Person CurrentPerson
{
get { return this._currentPerson; }
set
{
this._currentPerson = value;
this.RaisePropertyChanged("CurrentPerson");
}
}
}
Easy to implement, however quickly get's hard to remain. Not a lot of code reuse. No loose coupling. Should not use this.
Example 2: Putting data in a container
public class MainViewDataContainer : INotifyPropertyChanged
{
private Person _currentPerson;
public Person CurrentPerson
{
get { return this._currentPerson; }
set
{
this._currentPerson = value;
this.RaisePropertyChanged("CurrentPerson");
}
}
}
public class MainViewModel : ViewModelBase
{
...
private readonly FakeDataManager _fakeDataManager = new FakeDataManager();
private readonly MainViewDataContainer _dataContainer = new MainViewDataContainer();
public MainViewModel()
{
this._childViewModelA = new ChildViewModelA(_dataContainer);
this._childViewModelB = new ChildViewModelB(_dataContainer);
this._dataContainer.CurrentPerson = _fakeDataManager.GetNextPerson();
}
public class ChildViewModelA : ViewModelBase
{
private readonly MainViewDataContainer _dataContainer;
public ChildViewModelA(MainViewDataContainer dataContainer)
{
this._dataContainer = dataContainer;
}
public MainViewDataContainer DataContainer { get { return this._dataContainer; } }
}
Easier to maintain, more code reuse. Looser coupling.
Example 3: Storing in MainViewModel Properties and provide to children through an interface
public interface IMainDataProvider
{
Person CurrentPerson { get; }
}
public class MainViewModel : ViewModelBase, IMainDataProvider
{
private readonly ChildViewModelA _childViewModelA;
private readonly ChildViewModelB _childViewModelB;
private readonly FakeDataManager _fakeDataManager = new FakeDataManager();
public MainViewModel()
{
this._childViewModelA = new ChildViewModelA(this);
this._childViewModelB = new ChildViewModelB(this);
this.CurrentPerson = _fakeDataManager.GetNextPerson();
}
private Person _currentPerson;
public Person CurrentPerson
{
get { return this._currentPerson; }
set
{
this._currentPerson = value;
this.RaisePropertyChanged("CurrentPerson");
}
}
}
public class ChildViewModelA : ViewModelBase
{
private readonly IMainDataProvider _dataProvider;
public ChildViewModelA(IMainDataProvider dataProvider)
{
this._dataProvider = dataProvider;
}
public IMainDataProvider DataProvider { get { return this._dataProvider; } }
}
Yet again easier to maintain, more code reuse. Loose coupling.
Example 3 seems to be the best solution, however is this true? How do you think about this? Are there better ways to solve this issue?
If CurrentPerson is used by both ChildViewModels and MainView is the one who can change the CurrentPerson. How I will proceed to solve this problem is:
Define one base VM ChildViewModelBase : ViewModelBase with CurrentPerson property (with INotifyPropertyChanged and all). All of my ChildViewModels will extend this VM.
Use Event Aggregator http://msdn.microsoft.com/en-us/library/ff921122.aspx. Using EventAggregator you can subscribe to CurrentPersonChanged event in your ChildViewModelBase.
From you MainViewModel once the CurrentPerson is loaded from db, raise CurrentPersonChanged event with new Person as event arguement.
In event handler in the ChildViewModelBase, set CurrentPerson that you get as event argument.
In this way it would not matter to your ChildViewModels who is loading the Person from DB. Today it is MainView, tommorow it can be some other View also. You will just need to raise the above event with the person object as argument and Child Views will get it.
I have a specialized UserControl to play media content called PlayerView.
The control has its own commands (readonly, not provided by client).
public partial class PlayerView
{
public PlayerView()
{
InitializeComponent();
PlayCommand = new RelayCommand(() =>
{
// Play some media: audio/video.
});
}
...
#region PlayCommand property
private static readonly DependencyPropertyKey PlayCommandPropertyKey = DependencyProperty.RegisterReadOnly(
"PlayCommand",
typeof(ICommand),
typeof(PlayerView),
new PropertyMetadata());
public static readonly DependencyProperty PlayCommandProperty = PlayCommandPropertyKey.DependencyProperty;
public ICommand PlayCommand
{
get { return (ICommand)GetValue(PlayCommandProperty); }
private set { SetValue(PlayCommandPropertyKey, value); }
}
#endregion
...
}
The play command of the control works fine from XAML:
<Controls:PlayerView x:Name="PlayerView" />
<Button Command="{Binding ElementName=PlayerView, Path=PlayCommand, Mode=OneWay}" Content="Play" />
But currently, I am implemeting slideshow feature and I would like to execute the play command of the control from the ViewModel.
public class SlideshowViewModel : ViewModelBase
{
// Stores collection of audio/video clips to be played by the PlayerView.
// Assume that this ViewModel should invoke PlayerView PlayCommand.
}
public class MainViewModel : ViewModelBase
{
// Stores a lot of stuff.
public SlideshowViewModel Slideshow { get; }
}
The question is: how the SlideshowViewModel can execute the PlayCommand of this control? Is there a best practice?
If I am understanding your issue correctly, the ViewModel should contain the implementation of the Command, not the View. This would be a truer MVVM implementation, and then the VM can call that command from within itself, if necessary.
edit:
to answer your question,
public partial class PlayerView : IHaveAPlayCommand
{
public PlayerView()
{
this.DataContext = new ViewModel(this);
}
}
public class ViewModel
{
IHaveAPlayCommand view;
public ViewModel(IHaveAPlayCommand view)
{
this.view = view
}
}