Reputation: 141
I am trying to implement a simple WPF Application, with two nested UserControls inside the Mainwindow, and each UserControl having their own viewmodel. One Viewmodel implements a command which allows the user to choose a Directory. When a folder is chosen inside the first viewmodel, it is supposed to send a message to all other registered viewmodels. So we have three views, three viewmodels (including MainViewModel) and the Dependency Container. Though I implemented IRecipient, the ReceiverVM does not update its property.
Here are my Viewmodels, for convenience I've put them in the same file:
namespace MessagingReproduction;
public partial class MainViewModel : ObservableRecipient
{
}
public partial class SenderViewModel : ObservableRecipient
{
private string selectedDirectory;
public string SelectedDirectory
{
get => selectedDirectory;
set
{
var oldValue = selectedDirectory;
SetProperty(ref selectedDirectory, value);
Messenger.Send<PropertyChangedMessage<string>>(new PropertyChangedMessage<string>(this, nameof(SelectedDirectory), oldValue, selectedDirectory));
}
}
// simulate a FolderDialog
[RelayCommand]
private void SelectDirectory()
=> this.SelectedDirectory = "C:";
}
public partial class ReceiverViewModel : ObservableRecipient, IRecipient<PropertyChangedMessage<string>>
{
[ObservableProperty]
private string selectedDirectory;
public void Receive(PropertyChangedMessage<string> message)
{
selectedDirectory = message.NewValue;
}
}
My ReceiverView.xaml:
<Grid>
<TextBlock Text="{Binding SelectedDirectory, UpdateSourceTrigger=PropertyChanged}"
Width="120" Height="22"/>
</Grid>
my SenderView.xaml:
<StackPanel>
<Button Command="{Binding SelectDirectoryCommand}"
Width="120" Height="22"/>
<TextBlock Text="{Binding SelectedDirectory, UpdateSourceTrigger=PropertyChanged}"
Width="120" Height="22"/>
</StackPanel>
The MainWindow:
<StackPanel>
<local:SenderView DockPanel.Dock="Top"/>
<local:ReceiverView DockPanel.Dock="Bottom"/>
</StackPanel>
The Setup for Dependency Injection insinde App.xaml.cs:
public partial class App : Application
{
public App()
{
Services = ConfigureServices();
InitializeComponent();
}
public new static App Current => (App)Application.Current;
public IServiceProvider Services { get; }
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddTransient<MainViewModel>();
services.AddTransient<SenderViewModel>();
services.AddTransient<ReceiverViewModel>();
return services.BuildServiceProvider();
}
}
Setting Datacontexts inside each view like so:
public ReceiverView()
{
InitializeComponent();
DataContext = App.Current.Services.GetService<ReceiverViewModel>();
}
Despite the implementation of IRecipient, the viewmodels do not communicate. Note that my Views, except the Mainwindow, are UserControls.
Upvotes: 1
Views: 1963
Reputation: 21
I am only learning to use the MVVM toolkit myself, but it seems your are not implementing any message and also you are not registering your ReceiverViewModel as a recipient for the message.
First, create the message:
public class ChangeDirectoryMessage : ValueChangedMessage<string>
{
public ChangeDirectoryMessage(string newDirectory) : base(newDirectory)
{
}
}
Then, register the message in the receiving class:
public class ReceiverViewModel : ObservableObject, IRecipient<ChangeDirectoryMessage>
{
[ObservableProperty]
private string selectedDirectory;
public ReceiverViewModel()
{
WeakReferenceMessenger.Default.Register<ChangeDirectoryMessage>(this);
}
public void Receive(ChangeDirectoryMessage message)
{
selectedDirectory = message.NewValue;
}
}
And finally send a message from anywhere when the selected directory has changed:
WeakReferenceMessenger.Default.Send(new ChangeDirectoryMessage(selectedDirectory));
There is a good example on how to use this messenger in the documentation:
https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/messenger
Upvotes: 2