I'm currently building the UI for the app I'm working on and I have a few problems with the bindings.
The Scenario:
I have a pivot control with every pivot element consisting of an extra Frame/Page.
Now I have a TextBlock on the first PivotItem. I bind this to a "string" and use a button to switch between two possible contents of the button.
When the button is on the same Page/Frame it works like a charm. But when I implement a button on the MainPage and implement the same Viewmodel for the MainPage then it doesn't work. It will only change the string content on the MainPage.
Is it possible to implement the change for every Page/Frame?
And when that is done I have a Page where I gather data with a serial port.
I save the data to a List and I want to be able to use this list from 2 different Pages/Frames.
Thinking about the scenarion above then it would probably gather the data for the page where I have the button to get the data but it would probably display nothing on the other page.
How can I build it like I want it to be?
Here is a short example:
Mainpage.xaml
<StackPanel>
<Button Height="50" Width="200" Content="Change" FontSize="30" FontWeight="Bold" Margin="50 50 0 0" Click="{x:Bind MainViewModel.Change}"/>
<Pivot x:Name="MainPivot" Margin="50 50">
<PivotItem Header="Page 1">
<Frame x:Name="Page1" />
</PivotItem>
</Pivot>
</StackPanel>
Mainpage.xaml.cs
public MainPage()
{
this.InitializeComponent();
Page1.Navigate(typeof(Page1));
ViewModel = new MainViewModel();
}
public MainViewModel ViewModel { get; private set; }
Page1.xaml
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{x:Bind Path=ViewModel.StringModel.String1, Mode=TwoWay}" FontSize="50" FontWeight="Bold" />
<Button Content="Change" FontSize="30" FontWeight="Bold" Click="{x:Bind ViewModel.Change}"/>
</StackPanel>
Page1.xaml.cs
public Page1()
{
this.InitializeComponent();
ViewModel = new MainViewModel();
}
public MainViewModel ViewModel { get; private set; }
MainViewModel.cs
private StringModel _stringModel = new StringModel();
public StringModel StringModel
{
get => _stringModel;
set
{
if (_stringModel != value)
{
_stringModel = value;
OnPropertyChanged();
}
}
}
public void Change()
{
if (StringModel.String1 == "Text1")
{
StringModel.String1 = "Text2";
}
else
{
StringModel.String1 = "Text1";
}
}
StringModel.cs
private string _string1 = "XXX";
public string String1
{
get => _string1;
set
{
_string1 = value;
OnPropertyChanged();
}
}
Sounds like you are missing a "service layer" or "business layer" of your application. You need an external class which manages the data, and can provide models to populate your ViewModels:
I'd suggest using some kind of dependency injection, so each of your page view models have a reference to the DataProvider service class. This class does the serial port work to get a list of models, and provides an interface for getting data and pushing any updates to the ViewModels.
A good way of handling events that are shared, like say a "load data" button that may appear on different view models is an Event Aggregator. A service that can be injected into classes where events can be raised or subscribed to across the application.
Generally children of a XAML parent inherit the binding context of said parent.
So not sure you need to hook up a VM to your frame.
But suppose it does not work with Frames, you are creating a new MainViewModel for the frame as for the mainpage!
The solution here would be to create a singleton MainViewModel and get a hold of that one to hook up the BindingContext.
You can use Publisher-Subscriber pattern (Pub-Sub).
Already explained this here: Communication Between Views in MVVM (Pub-Sub Pattern)
Related
Good day. I'll try to make it shorter as possible. My XAML:
<StackPanel Grid.Row="1" Grid.Column="0" x:Name="stackActions">
<Button x:Name="btnAction_1" Margin="5" Content="Start work" Click="btnAction_1_Click"/>
<Button x:Name="btnAction_2" Margin="5" Content="" Visibility="Collapsed"/>
<Button x:Name="btnAction_3" Margin="5" Content="" Visibility="Collapsed"/>
<Button x:Name="btnAction_4" Margin="5" Content="" Visibility="Collapsed"/>
<Button x:Name="btnAction_5" Margin="5" Content="" Visibility="Collapsed"/>
</StackPanel>
My C#:
public partial class MainWindow : Window
{
// Method for collapsing all buttons
public void HideAllButtons()
{
stackActions.Children.OfType<Button>().ToList().ForEach(button => button.Visibility = Visibility.Collapsed);
}
public MainWindow()
{
InitializeComponent();
}
private void btnAction_1_Click(object sender, RoutedEventArgs e)
{
HideAllButtons();
btnAction_2.Visibility = Visibility.Visible;
btnAction_3.Visibility = Visibility.Visible;
btnAction_2.Content = "Surveys";
btnAction_3.Content = "ClearAll";
btnAction_2.Click += SeeSurveys;
btnAction_3.Click += ClearAll;
}
private void SeeSurveys(object sender, RoutedEventArgs e )
{
btnAction_2.Click -= SeeSurveys;
btnAction_3.Click -= ClearAll;
btnAction_2.Content = "Draft";
btnAction_3.Content = "OnHire";
btnAction_4.Visibility = Visibility.Visible;
btnAction_4.Content = "Condition";
btnAction_5.Visibility = Visibility.Visible;
btnAction_5.Content = "Back";
btnAction_5.Click += btnAction_1_Click;
}
private void ClearAll (object sender, RoutedEventArgs e)
{
HideAllButtons();
btnAction_1.Visibility = Visibility.Visible;
}
}
Actually this is works as i want, but looking like mess with much Click += and Click -= events. I understand that making more massive code confused myseld very soon. I suppose there are any options to bind click event for each button in XAML and change the event itself in C# or reduce the quantity of Visibility checks.
The main idea is changing click events and rewriting buttons content depending of what button was clicked.
So any advice is welcome to make this code more clear and short.
Like many wpf developers and the vast majority of commercial wpf teams, I use MVVM.
With that I would be thinking in terms of templating buttons out from viewmodels that had commands.
Rather than having many buttons where I switched the event handler round, I'd have lists of commands. These could be in a model which was a hierarchical form and a layer translated or picked from that if it suited your real world requirement best.
Or
A series of objects something like:
public class CommandListViewModel
{
public int Id { get; set; }
public ObservableCollection<CommandViewModel> Commands { get; set; }
}
public class CommandViewModel
{
public string Name { get; set; }
public DelegateCommand<MainWindowViewModel> Command { get; set; }
}
These are just sketched out to give an idea of what I have in mind. You would have inotifypropertychanged implemented in these viewmodels and probably want to raise change notification from the property setters.
But the idea is each level is represented by a class commandlistviewmodel which has an id and a bunch of commands with the "name". The name is displayed in a button which has that command.
Each command gets a reference to mainwindowviewmodel passed into it. So it can access properties on that. One of which would be a list or instance of commandlistviewmodel.
Hence any given command could go find a list of commands it needs to "substitute" or some other property in mainwindowviewmodel it should manipulate.
You can create delegatecommand ( or relaycommand ) using lambdas.
Rather or as well as passing in an instance of mainwindowviewmodel these could potentially capture variables from wherever they are constructed. You could make that a mediator or event aggregator which abstracted UI services.
Essentially though.
These commands do what your click handlers do.
These lists of commands are your various switchable levels.
You work with these and bind them to the UI to template into a bunch of buttons.
The way you do that templating is in an itemscontrol. Or a listbox if you wanted to have a built in slider and maybe indicate which one was clicked last using selecteditem.
<ItemsControl ItemsSource="{Binding Commands}"
>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:CommandViewModel}">
<Button Content="{Binding Name}"
Command="{Binding Command}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
With the above a viewmodel which is the datacontext of this itemscontrol ( usually also of the entire window ) would have a public property which was an observablecollection of CommandViewModel. The items or the entire collection are switched out depending on the "level" chosen. You click one command which chooses Surveys and a list of commandviewmodel appropriate for Surveys are shown. One of those looks like it'd be a "Back" command which switched back to the previous list. And so on.
You could also have a property in a commandviewmodel which binds to Visibility so you could collapse a button. I'm not sure you'd need that with this pattern though. It will automatically cope with a "level" which has 3, 5, 10 or whatever number of commands.
You will have a fair bit of reading to do before you would be in a position to switch from your current pattern to mvvm. Delegatecommand is just one of your options, it's from Prism and there's a prism.mvvm nuget you could consider. Or you might prefer mvvmlight which has relaycommand. You'll want to use one of these or some other such framework to save you writing your own icommand implementation.
I hope that's sufficient to at least get you started.
I'd like to create an app, containing the main menu (ribbonmenu) and different usercontrols, each assigned to an own ViewModel.
I was told to not implement classic events in code-behind but to use commands. So far, everything fine, commands for needed methods are implemented.
In my previous approach I "loaded" the UserControl, by assigning the corresponding ViewModel to a ContentControl, that loaded the UserControl, that was assigned to the ViewModel in MainWindow.Resource.
My last approach, simplified with a button instead of a menu:
<Window.Resources>
<DataTemplate x:Name="settingsViewTemplate" DataType="{x:Type viewmodels:SettingsViewModel}">
<views:SettingsView DataContext="{Binding SettingsVM, Source={StaticResource Locator}}"/>
</DataTemplate>
<DataTemplate x:Name="projectsViewTemplate" DataType="{x:Type viewmodels:ProjectViewModel}">
<views:ProjectView DataContext="{Binding ProjectVM, Source={StaticResource Locator}}"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Load Settings" Height="20" Margin="20 20 20 0" Click="ShowSettings"/>
<ContentControl Margin="5" Height="100" Content="{Binding}"/>
</StackPanel>
simplified code-behind:
public SettingsViewModel settingsViewModel;
public MainWindow()
{
InitializeComponent();
settingsViewModel = new SettingsViewModel();
}
private void ShowSettings(object sender, RoutedEventArgs e)
{
DataContext = settingsViewModel;
}
How can I load a UserControl, using ViewModel commands?
Don't use code-behind to handle view models. A View model should handle view models. Generally the same view model that implements the commands.
First create a main view model for the MainWindow as data source. This view model will also handle the switching between the views. It's recommended to let all page view models implement a common base type e.g. IPage.
Also you don't need any locator for this scenario. The views inside the DataTemplate will automatically have their DataContext set to the data type that maps to the DataTemplate. SettingsView will automatically have SetingsViewModel as the DataContext. If this would be the wrong context, then your model design is wrong.
IPage.cs
interface IPage : INotifyPropertyChanged
{
string PageTitel { get; set; }
}
SettingsViewModel.cs
class SettingsViewModel : IPage
{
...
}
ProjectViewModel.cs
class ProjectViewModel : IPage
{
...
}
PageName.cs
public enum PageName
{
Undefined = 0, SettingsPage, ProjectPage
}
MainViewModel.cs
An implementation of RelayCommand can be found at
Microsoft Docs: Patterns - WPF Apps With The Model-View-ViewModel Design Pattern - Relaying Command Logic
class MainViewModel : INotifyPropertyChanged
{
public ICommand SelectPageCommand => new RelayCommand(SelectPage);
public Dictionary<PageName, IPage> Pages { get; }
private IPage selectedPage;
public IPage SelectedPage
{
get => this.selectedPage;
set
{
this.selectedPage = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
this.Pages = new Dictionary<PageName, IPage>
{
{ PageName.SettingsPage, new SettingsViewModel() },
{ PageName.ProjectPage, new ProjectViewModel() }
};
this.SelectedPage = this.Pages.First().Value;
}
public void SelectPage(object param)
{
if (param is PageName pageName
&& this.Pages.TryGetValue(pageName, out IPage selectedPage))
{
this.SelectedPage = selectedPage;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Name="settingsViewTemplate" DataType="{x:Type viewmodels:SettingsViewModel}">
<views:SettingsView />
</DataTemplate>
<DataTemplate x:Name="projectsViewTemplate" DataType="{x:Type viewmodels:ProjectViewModel}">
<views:ProjectView />
</DataTemplate>
</Window.Resources>
<StackPanel>
<!-- Content navigation -->
<StackPanel Orientation="Horizontal">
<Button Content="Load Settings"
Command="{Binding SelectPageCommand}"
CommandParameter="{x:Static PageName.SettingsPage}" />
<Button Content="Load Projects"
Command="{Binding SelectPageCommand}"
CommandParameter="{x:Static PageName.ProjectPage}" />
</StackPanel>
<ContentControl Content="{Binding SelectedPage}" />
<StackPanel>
</Window>
The short version:
public class MyViewModel : ViewModel
public MyViewModel()
{
View = new MyUserControlView();
View.DataContext = this; // allow the view to bind to the viewModel.
}
....
public UIElement View {
get; private set;
}
}
And then in XAML:
<ContentControl Content={Binding View} />
There are variations on this theme but that's the basic premise. e.g., if you have a ViewModel that can be bound to multiple views, or ViewModels that have lifetimes longer than their view, you can use a FrameViewModel class like this:
public class FrameViewModel : INotifyProperyChanged; {
public FrameViewModel(IViewModel viewModel; )
{
ViewModel = viewModel;
View = viewModel.CreateView();
View.DataContext = ViewModel;
}
public IViewModel ViewModel { get; set;...}
public UIElement View { get; set; }
}
And then bind THAT into the host XAML with a ContentControl binding to Frame.View.
A more pure approach is to the use the DataTemplateSelector class to instantiate the User Control in a DataTemplate. This is probably the method that WPF designers had in mind for connecting View and ViewModel in WPF. But it ends up spreading the mapping of View and ViewModel across three separate files (the custom C# DataTemplateSelector implementation; widely-separated static resource declaration and ContentControl wrapper in the hosting Window/Page; and the DataTemplate resources themselves which end up in resource files eventually if you have anything but a trivial number of ViewModel/View bindings.
Purists would argue, I suppose, that there's something dirty about having a viewmodel create a view. But there's something far more dirty about code to make DataTemplateSelectors work spread across five files, and inevitable complications with databindings that ensue while trying to tunnel a binding through a DataTemplate.
I have a Tab Control, with an "Exams" tab and a "Templates" tab. I want to share information between the two tabs.
My background is mostly in web with React and Redux, so my instinct is to have the shared information (of model type Exam) belong to the MainWindow, probably as a window resource.
My tech lead, who is much more experienced than me -- in all things, but also in C# though he's also new to WPF), assures me that this is not the way to do it in this setting, that the tabs' ViewModels should be sharing/passing the information.
So I've set up my basic UI with a text box in the Exams tab and a label to display the contents of the text box in the Templates tab.
<Page x:Class="Gui.Tabs.ExamsTab.ExamsHome" Title="ExamsTab">
<Grid VerticalAlignment="Center">
<StackPanel>
<Label Content="EXAMS TAB" />
<TextBox Text="{Binding ExamString, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="{Binding ExamString}" />
</StackPanel>
</Grid>
</Page>
--------
<Page x:Class="Gui.Tabs.TemplatesTab.TemplatesHome" Title="TemplatesHome">
<Grid VerticalAlignment="Center">
<StackPanel>
<Label Content="TEMPLATES TAB"/>
<Label Content="Text from Exams tab:"/>
<Label BorderBrush="Red" BorderThickness="1" Content="{Binding StringFromExamsTab}"/>
</StackPanel>
</Grid>
</Page>
And I've wired up the ViewModels best I can: (the code-behind and the viewmodels are in the same file for now)
namespace Gui.Tabs.ExamsTab {
public partial class ExamsHome : Page {
public ExamsHome() {
InitializeComponent();
DataContext = ViewModel;
}
public static readonly ExamsTabViewModel ViewModel = new ExamsTabViewModel();
}
public class ExamsTabViewModel :INotifyPropertyChanged {
private string _examString = "Default exam string";
public string ExamString {
get => _examString;
set {
_examString = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
------
namespace Gui.Tabs.TemplatesTab {
public partial class TemplatesHome : Page {
public TemplatesHome() {
InitializeComponent();
DataContext = viewModel;
}
public static readonly TemplatesTabViewModel viewModel = new TemplatesTabViewModel();
}
public class TemplatesTabViewModel {
public string StringFromExamsTab {
// needs to be notified of value change!
get => ExamsHome.ViewModel.ExamString;
}
}
}
In the Exams tab, the text-box and its corresponding label are synced up.
And, in the Templates tab, the red box says "Default exam string", but it doesn't change with the value in the text-box.
I've read that:
The XAML binding engine listens for [the PropertyChangedEvent] for each bound property on classes that implement the INotifyPropertyChanged interface
Which implies to me that as long as INotifyPropertyChanged is implemented in the Exams VM, the bindings in the Templates tab should be notified.
How can I make this work? Open to the possibility that I'm doing it entirely (or mostly) wrong.
I'm trying to create TabItem Headers with Buttons that enable the User to close tabs. The visual representation and the Databinding of the object is just fine.
I've experimented with the DataContext, but so far I haven't found a workable solution.
My XAML:
<TabControl
Grid.Column="3"
Grid.Row="2"
x:Name="TabControlTargets"
ItemsSource="{Binding Path=ViewModelTarget.IpcConfig.DatabasesList, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=ViewModelTarget.SelectedTab, UpdateSourceTrigger=PropertyChanged}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontFamily="Calibri" FontSize="15" FontWeight="Bold" Foreground="{Binding FontColor}" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" Margin="0,0,20,0"/>
<Button HorizontalAlignment="Left" DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}" Command="{Binding Path = ViewModelTarget.buttonRemoveDatabaseCommand}"
CommandParameter="**?**"
>
<Button.Content>
<Image Height="15" Width="15" Source="pack://application:,,,/Images/cancel.png" />
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
I have trouble figuring out how to set the CommandParameter of my button so that it refers to the correct object.
Here is my RelayCommand:
public ICommand buttonRemoveDatabaseCommand
{
get
{
if (_buttonRemoveDatabaseCommand == null)
{
_buttonRemoveDatabaseCommand = new RelayCommand(
param => RemoveDatabase(param)
);
}
return _buttonRemoveDatabaseCommand;
}
}
And here is my RemoveDatabase function:
public void RemoveDatabase(object dB)
{
this.IpcConfig.RemoveDataBase((PCDatabase)dB);
}
I would strongly prefer a solution that sticks to my "no code behind" approach.
As pointed in the comments, you can use CommandParameter="{Binding}" to pass the TabItem context to the command.
A better approach is though to move the command to the ViewModel of your TabItem.
Here an example implementation using Prism and Prism's EventAggregator. You can of course implement this with every other MVVM Framework or even implement it yourself, but that's up to you.
This would be your TabControl ViewModel, which contains a list of all databases or whatever it's meant to represent.
public class DatabasesViewModel : BindableBase
{
private readonly IEventAggregator eventAggregator;
public ObservableCollection<DatabaseViewModel> Databases { get; private set; }
public CompositeCommand CloseAllCommand { get; }
public DatabasesViewModel(IEventAggregator eventAggregator)
{
if (eventAggregator == null)
throw new ArgumentNullException(nameof(eventAggregator));
this.eventAggregator = eventAggregator;
// Composite Command to close all tabs at once
CloseAllCommand = new CompositeCommand();
Databases = new ObservableCollection<DatabaseViewModel>();
// Add a sample object to the collection
AddDatabase(new PcDatabase());
// Register to the CloseDatabaseEvent, which will be fired from the child ViewModels on close
this.eventAggregator
.GetEvent<CloseDatabaseEvent>()
.Subscribe(OnDatabaseClose);
}
private void AddDatabase(PcDatabase db)
{
// In reallity use the factory pattern to resolve the depencency of the ViewModel and assing the
// database to it
var viewModel = new DatabaseViewModel(eventAggregator)
{
Database = db
};
// Register to the close command of all TabItem ViewModels, so we can close then all with a single command
CloseAllCommand.RegisterCommand(viewModel.CloseCommand);
Databases.Add(viewModel);
}
// Called when the event is received
private void OnDatabaseClose(DatabaseViewModel databaseViewModel)
{
Databases.Remove(databaseViewModel);
}
}
Each tab would get one DatabaseViewModel as it's context. This is where the close command is defined.
public class DatabaseViewModel : BindableBase
{
private readonly IEventAggregator eventAggregator;
public DatabaseViewModel(IEventAggregator eventAggregator)
{
if (eventAggregator == null)
throw new ArgumentNullException(nameof(eventAggregator));
this.eventAggregator = eventAggregator;
CloseCommand = new DelegateCommand(Close);
}
public PcDatabase Database { get; set; }
public ICommand CloseCommand { get; }
private void Close()
{
// Send a refence to ourself
eventAggregator
.GetEvent<CloseDatabaseEvent>()
.Publish(this);
}
}
When you click the close Button on the TabItem, then CloseCommand would be called and send an event, that would notify all subscribers, that this tab should be closed. In the above example, the DatabasesViewModel listens to this event and will receive it, then can remove it from the ObservableCollection<DatabaseViewModel> collection.
To make the advantages of this way more obvious, I added an CloseAllCommand, which is a CompositeCommand that registers to each DatabaseViewModels CloseCommand as it's added to the Databases observable collection, which will call all registered commands, when called.
The CloseDatabaseEvent is a pretty simple and just a marker, that determines the type of payload it receives, which is DatabaseViewModel in this case.
public class CloseDatabaseEvent : PubSubEvent<DatabaseViewModel> { }
In real-world applications you want to avoid using the ViewModel (here DatabaseViewModel) as payload, as this cause tight coupling, that event aggregator pattern is meant to avoid.
In this case it's can be considered acceptable, as the DatabasesViewModel needs to know about the DatabaseViewModels, but if possible it's better to use an ID (Guid, int, string).
The advantage of this is, that you can also close your Tabs by other means (i.e. menu, ribbon or context menus), where you may not have a reference to the DatabasesViewModel data context.
I have a WinRT project based on prism with several pages, usercontrols etc.
For some reason I need to bind several views to a single model object accessed by the the viewmodels (each one belonging to a view).
The single model object is injected by a Unity container like other objects, that need to be singleton-like as the eventaggregator for instance.
To keep things simple I made an example with only one bool variable bound to a checkbox in each view, that should be synchronized over the views.
My problem is: When I check the box in the mainpage, the checkbox in a second page is following the value when navigating to that page(UserInpuPage in the example) BUT NOT the checkbox in the UserControl place on the Mainpage.
After a debug session I saw the variables in the single model having the right values, but the GUI on the Usercontrol (MyUserControl in the example) is not updated.
A Mechanism like GetBindingExpression(...) and then UpdateTarget() like in WPF seems not to exist in the WinRT library.
For design reasons (using prism mvvm I don't want to break the concept of the autowire and dynamical instantiation of the vm's) a static context defined in the resources section of the page and/or usercontrol is not what I'm looking for.
How can I achieve the update of the checkbox in the usercontrol with the model the same way as it works for the userinputpage after navigating?
Any help would be appreciated.
// Interface mendatory to work with Unitiy-Injection for RegisterInstance<ISingletonVM>(...)
public interface ISingletonVM
{
bool TestChecked{ get; set; }
}
public class SingletonVM : BindableBase, ISingletonVM
{
bool _testChecked = false;
public bool TestChecked
{
get
{
return _testChecked;
}
set
{
SetProperty(ref _testChecked, value);
}
}
}
This is the relevant code in the viewmodels (same for every vm, but vm from usercontrol in this case):
class MyUserControlViewModel : ViewModel
{
private readonly ISingletonVM _singletonVM;
public MyUserControlViewModel(ISingletonVM singletonVM)
{
_singletonVM = singletonVM;
}
public bool TestChecked
{
get
{
return _singletonVM.TestChecked;
}
set
{
_singletonVM.TestChecked = value;
}
}
}
Relevant XAML code fragments for the three views:
MainPage:
<prism:VisualStateAwarePage x:Name="pageRoot" x:Class="HelloWorldWithContainer.Views.MainPage"...>
...
<StackPanel Grid.Row="2" Orientation="Horizontal">
<ctrl:MyUserControl ></ctrl:MyUserControl>
<CheckBox IsChecked="{Binding TestChecked, Mode=TwoWay}" Content="CheckBox" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel>
...
UserInputPage:
<prism:VisualStateAwarePage x:Name="pageRoot"
x:Class="HelloWorldWithContainer.Views.UserInputPage"
...
<CheckBox IsChecked="{Binding TestChecked, Mode=TwoWay}" Content="CheckBox" HorizontalAlignment="Left" Margin="440,190,0,0" VerticalAlignment="Top"/>
...
UserControl:
<UserControl
x:Class="HelloWorldWithContainer.Views.MyUserControl" prism:ViewModelLocator.AutoWireViewModel="True"
<Grid>
<CheckBox Content="CheckBox" IsChecked="{Binding TestChecked, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="282"/>
</Grid>
Your user control never gets notified about changes in MyUserControlViewModel.TestChecked property and that's why the view is never updated. One thing you can do to fix this is to subscribe to your SingletonVM.PropertyChanged event in the constructor of MyUserControlViewModel. Your ISingletonVM needs to implement the INotifyPropertyChanged interface. So the constructor of MyUserControlViewModel will be something like this:
public MyUserControlViewModel(ISingletonVM singletonVM)
{
_singletonVM = singletonVM;
_singletonVM.PropertyChanged += (sender, args) => OnPropertyChanged("TestChecked");
}