I have tried to figure out why a UserControl i have created cant have its DataContext set in xaml like
<UserControl.DataContext>
<vm:MainViewModel/>
</UserControl.DataContext>
If i do it throws an xamlparse exception saying DataContext=null.
if i instead in the codebehind do
this.DataContext = new MainViewModel();
there is no problem at all.
the MainView and MainViewModel are both dll projects.
The Main Window only contains MainView.
MainView and MainviewModel Code:
<UserControl x:Class="UmlEditor.view.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UmlEditor.view"
xmlns:vm="clr-namespace:UmlEditor.ViewModel;assembly=UmlEditor.ViewModel"
>
<!--This dosent work-->
<UserControl.DataContext>
<vm:MainViewModel/>
</UserControl.DataContext>
<Grid>
<Button Command="{Binding HeyCommand}" Content="i work" Width="100" Height="50"/>
</Grid>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.CommandWpf;
using System.Windows.Input;
namespace UmlEditor.ViewModel
{
public class MainViewModel
{
public ICommand HeyCommand { get; private set; }
public MainViewModel()
{
HeyCommand = new RelayCommand(hej);
}
private void hej()
{
Console.WriteLine("i am the bomb");
}
}
}
I Finally figured it out.
as both view and viewmodel are two seperate .dll and the app(window) a seperate project.
The app need reference to both .dll even though it only directly uses view
Related
I'm working on a WinUI app (c# and xaml) with multiple frames and pages.
The problem is that I need to modify a UIElement property (TextBox.Text) from another class. I've been trying so many things and none of them has worked yet.
I'd be glad if someone could inspire me with some useful ways to do it. It can be anything aside from xaml data binding (<property={"Binding bindingName"}).
Thanks for the help.
In this sample code, I have 2 pages (Page1 and Page2) and 1 ViewModel (MainViewModel). Each page has a TextBox that are bound to the same property in the MainViewModel.
If you change the text in Page1, the text in Page2 will be changed, and vice versa.
NuGet packages
CommunityToolkit.Mvvm
Microsoft.Extensions.DependencyInjection
App.xaml.cs
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
namespace MultiplePagesSingleViewModel;
public partial class App : Application
{
private Window? window;
public App()
{
this.InitializeComponent();
Ioc.Default.ConfigureServices(
new ServiceCollection()
// This needs to be Singleton
.AddSingleton<MainViewModel>()
.BuildServiceProvider());
}
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
this.window = new MainWindow();
this.window.Activate();
}
}
Page1.xaml
<Page
x:Class="MultiplePagesSingleViewModel.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:MultiplePagesSingleViewModel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<StackPanel>
<TextBlock Text="Page 1" />
<TextBox Text="{x:Bind ViewModel.SomeText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Page>
Page1.xaml.cs
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
namespace MultiplePagesSingleViewModel;
public sealed partial class Page1 : Page
{
public Page1()
{
this.InitializeComponent();
// This MainViewModel is the same instance that Page2 gets.
ViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
}
public MainViewModel ViewModel { get; }
}
Page2.xaml
<Page
x:Class="MultiplePagesSingleViewModel.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:MultiplePagesSingleViewModel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<StackPanel>
<TextBlock Text="Page 2" />
<TextBox Text="{x:Bind ViewModel.SomeText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Page>
Page2.xaml.cs
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
namespace MultiplePagesSingleViewModel;
public sealed partial class Page2 : Page
{
public Page2()
{
InitializeComponent();
// This MainViewModel is the same instance that Page1 gets.
ViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
}
public MainViewModel ViewModel { get; }
}
MainViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace MultiplePagesSingleViewModel;
// This class needs to be "partial" for CommunityToolkit.Mvvm.
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
// The CommunityToolkit.Mvvm will automatically generate
// a UI-Interactive "SomeText" property for you.
private string someText = "Default text";
}
MainWindow.xaml
<Window
x:Class="MultiplePagesSingleViewModel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:MultiplePagesSingleViewModel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid ColumnDefinitions="*,*">
<local:Page1 Grid.Column="0" />
<local:Page2 Grid.Column="1" />
</Grid>
</Window>
Well, finally after two weeks of research I've found and check a solution.
The solution is basically rising and catching events from the different classes. I found this information in a MSDN post. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-publish-events-that-conform-to-net-framework-guidelines
This post is REALLY STRAIGHT FORWARD. I've created the CustomEventArgs class and then the publisher class is my controller (.cs) and my subscriber is the model (.xaml.cs).
The only problem that I've encountered is setting alll the methods and attributes as static in order to be able to call the controller function from other models. The problem is that on the raiseEvent(this, e); in the publisher class, this cannot be static so i've written null instead.
Also, I'd like to say that it works extremely fluent and with no lag or delay at all even if I constantly use it.
I hope this helps to everybody encountering the same problem.
I have two xaml toggles in separate files that I want to update simultaneously (if one is switched on the other should be too (and vice versa). My first switch in xaml is:
<Switch Grid.Column="1" x:Name="toggleSwitch1" IsToggled="true" Toggled="OnToggled"/>
Using C# how can I return a boolean value of this switch so that I can update another switch simultaneously? Then once retrieving the value, how can I update the xaml of the toggle status for the other switch?
Your Switch control means, as I can understand, that you using UWP, but I'm not sure.
Anyway, the idea is to bind both controls IsToggled properties to same property of some ViewModel:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MyWPFApp
{
public class ControlsViewModel : INotifyPropertyChanged
{
private bool switchToggled;
public bool SwitchToggled
{
get => switchToggled;
set
{
switchToggled = value;
OnPropertyChanged(nameof(SwitchToggled));
}
}
public ControlsViewModel() { }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in XAML of both Windows set bindings to Switch control (in my example - CheckBox control):
<!-- Window 1 -->
<Window x:Class="MyWPFApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyWPFApp"
mc:Ignorable="d"
Title="Window 1" Height="100" Width="300">
<Grid>
<CheckBox Content="Window1 CheckBox"
IsChecked="{Binding SwitchToggled}"/>
<!-- Replace IsChecked to IsToggled property -->
</Grid>
</Window>
<!-- Window 2 -->
<Window x:Class="MyWPFApp.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyWPFApp"
mc:Ignorable="d"
Title="Window 2" Height="100" Width="300">
<Grid>
<CheckBox Content="Window2 CheckBox"
IsChecked="{Binding SwitchToggled}"/>
<!-- Replace IsChecked to IsToggled property -->
</Grid>
</Window>
Code-behind of both Windows in example is same:
using System.Windows;
namespace MyWPFApp
{
public partial class Window1 : Window // or public partial class Window2
{
public Window1(ControlsViewModel cvm) // or public Window2
{
InitializeComponent();
DataContext = cvm;
}
}
}
And when calling that example Windows to show from Main one, you creating ControlsViewModel instance and pass it to both:
using System.Windows;
namespace MyWPFApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var cvm = new ControlsViewModel();
new Window1(cvm).Show();
new Window2(cvm).Show();
}
}
}
So checking/unchecking (toggle/untoggle) one of them will affect another and vice versa. Also, you can change SwitchToggled from code somewhere, which would affect both controls too.
Please note, that this is just example to try explain the idea. More MVVM pattern explanations and examples you can find at MSDN.
I am writing a User Control named "GridAndChart" that can be filled with values at Design-Time just like this:
<local:GridAndChart HorizontalAlignment="Left"
VerticalAlignment="Top" Width="660"
Height="342"
Margin="28,29,0,0"
NameX="Auslastung in %"
NameY="Wirkungsgrad in %"
MinValueX="0"
MaxValueX="100"
MinValueY="0"
MaxValueY="100"
TitleOfChart="Kennlinie Wirkungsgrad"
Xvals='12,323,43,55,65,7'
Yvals='60,99,99,99,99,99'
/>
Most of the Properties you can see there (Like TitleOfChart, Xvals,...) I defined in the Code-Behind-File of my User-Control as Dependency-Properties.
My Problem is that only those Dependency-Properties are displayed correctly in my Test-Window, that are bound in the UserControl-XAML and therefore invoked by the initializeComponent(), like for example TitleOfChart. By displayed correctly I mean: Display of the Design-time-value from the XAML where I include my UserControl. The other dependency properties don't display correctly because they show only their default value.
From my example you will see that the Evaluation of my dummy-calculations will happen with the default values of the Dependency Properties. Not, after that they got their "Design-Time-Values", read out from the Main.xaml.
Thankyou for your help!
EDIT: Reproducible Code (4 Files)
File 1 and 2: The Window that uses the Control and it's Code-Behind
MainWindow.xaml
<Window x:Class="MinimalReproducibleExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MinimalReproducibleExample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:SimpleUserControl Working="I defined this Text at Design time" NotWorking="1,2,3,4"/>
</Grid>
</Window>
MainWindow.xaml.cs (I left it empty)
using ...
namespace MinimalReproducibleExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
File 3 and 4: The User-Control and it's Code-Behind
SimpleUserControl.xaml
<UserControl x:Class="MinimalReproducibleExample.SimpleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MinimalReproducibleExample"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Name="TextBox1XAML" />
<TextBox Grid.Row="1" Name="TextBox2XAML" />
</Grid>
</UserControl>
SimpleUserControl.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MinimalReproducibleExample
{
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
string notWorking = NotWorking;//just to show that there happens some calculation with the values
int a = Convert.ToInt32(notWorking.ElementAt(0)); //before they are displayed
int b = Convert.ToInt32(notWorking.ElementAt(1));
int c = a + b;
TextBox2XAML.Text = c.ToString();
}
public string Working
{
get { return GetValue(WorkingProperty).ToString(); }
set { SetValue(WorkingProperty, value); }
}
public static readonly DependencyProperty WorkingProperty = DependencyProperty.Register(nameof(Working), typeof(string), typeof(SimpleUserControl), new PropertyMetadata("The default is working.", (s, e) => (s as SimpleUserControl).TextBox1XAML.Text = (string)e.NewValue));
public string NotWorking
{
get { return GetValue(NotWorkingProperty).ToString(); }
set { SetValue(NotWorkingProperty, value); }
}
public static readonly DependencyProperty NotWorkingProperty = DependencyProperty.Register(nameof(NotWorking), typeof(string), typeof(SimpleUserControl), new PropertyMetadata("The default is also working.", (s, e) => (s as SimpleUserControl).NotWorking = (string)e.NewValue));
}
}
In your MCVE if you put the code to the Loaded event handler of your usercontrol, then all will work as expected. See example:
Usercontrol:
public partial class SimpleUserControl : UserControl
{
public string SimpleTxtProp { get; set; } = "AAA";
public SimpleUserControl()
{
//Constructor being called from InitializeComponent() of parent element. All properties are initialized with values from code behind,
//after object creation is finished, it's property will be initialized with values set in parent element for this control.
InitializeComponent();//Content of control being initialized: constructor -> InitializeComponent()
var isAAA = SimpleTxtProp == "AAA";//true
Loaded += (o, e) =>
{
var isBBB = SimpleTxtProp == "BBB"; //true
};
}
}
MainWindow:
<local:SimpleUserControl SimpleTxtProp="BBB"/>
It doesn't matter whether you consider dependency or simple property.
InitializeComponent() of MainWindow does
trigger the instantiation of SimpleUserControl, hence constructor
with default values/values from code behind being called,
then the properties of the created object being set.
So once an istance of "SimpleUserControl" is loaded, it has the values, which was set in XAML of MainWindow.
I have asked how to bind listbox to mvvm in this thread:
Listbox is not Populating using binding
I created another thread to ask why my ObservableList is not Refreshing.
The List is now populated on Load
but not populating or refreshing after I select the Folder
The program is supposed to Populate the list after I select the program
this is the structure of my folders and class
this is the code for my FolderBrowserDialogVM
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Bates_Writer.ViewModel
{
public class FolderBrowserDialogVM :ViewModelBase
{
public System.Collections.ObjectModel.ObservableCollection<string> FileNames { get; }
= new System.Collections.ObjectModel.ObservableCollection<string>()
{
"Wayne",
"Cinderella",
"Malificient"
};
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
RaisePropertyChanged("SelectedPath");
}
}
private string _defaultPath;
public FolderBrowserDialogVM()
{
RegisterCommands();
}
public FolderBrowserDialogVM(string defaultPath)
{
_defaultPath = defaultPath;
RegisterCommands();
}
private void RegisterCommands()
{
OpenCommand = new RelayCommand(ExecuteOpenFileDialog);
}
private void ExecuteOpenFileDialog()
{
var dialog = new FolderBrowserDialog();
dialog.ShowDialog();
SelectedPath = dialog.SelectedPath;
FileNames.Clear();
foreach (var file in System.IO.Directory.EnumerateFiles(SelectedPath, "*", System.IO.SearchOption.AllDirectories))
{
FileNames.Add(file);
Console.WriteLine(file);
}
}
}
}
this is the code for FilesView.xaml(user control)
<UserControl x:Class="Bates_Writer.View.FilesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Bates_Writer.View"
xmlns:vm="clr-namespace:Bates_Writer.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<vm:FolderBrowserDialogVM>
</vm:FolderBrowserDialogVM>
</UserControl.DataContext>
<Grid>
<ListBox ItemsSource="{Binding FileNames}" Margin="5,5,5,5"/>
</Grid>
</UserControl>
I tried How to: Implement Property Change Notification
but I can't make it work.
this is my FolderBrowserDialogV.xaml(user control)
<UserControl x:Class="Bates_Writer.View.FolderBrowserDialogV"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Bates_Writer.View"
xmlns:vm="clr-namespace:Bates_Writer.ViewModel"
mc:Ignorable="d" Height="42.056" Width="679.439">
<UserControl.DataContext>
<vm:FolderBrowserDialogVM>
</vm:FolderBrowserDialogVM>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="127*"/>
<ColumnDefinition Width="506*"/>
<ColumnDefinition Width="110*"/>
</Grid.ColumnDefinitions>
<Label Content="Folder Location: " FontSize="14" VerticalContentAlignment="Center"/>
<TextBox Grid.Column="1" FontSize="14" Margin="5,5,5,5" VerticalContentAlignment="Center" Text="{Binding SelectedPath}"/>
<Button Command="{Binding OpenCommand}" Content="Browse" Grid.Column="2" FontSize="14" Margin="5,5,5,5" Cursor="Hand"/>
</Grid>
</UserControl>
I even uploaded the sample project here
It looks like you have two instances of FolderBrowserDialogVM. One instance is DataContext for your FolderBrowserDialogV(here you have bound the command, but not the ObservableCollection) and another one for your FilesView(here you have bound ObservableCollection, but not the command, so your ObservableCollection will not be changed in this instance). For it works in your case, you should use(bind to) the same instance.
Delete from your UserControls:
<UserControl.DataContext>
<vm:FolderBrowserDialogVM>
</vm:FolderBrowserDialogVM>
</UserControl.DataContext>
Create an instance of your ViewModel in resources of your main window and set this instance as DataContext for your Usercontrols:
<MainWindow
xmlns:vm="clr-namespace:Bates_Writer.ViewModel" .. >
<MainWindow.Resources>
<vm:FolderBrowserDialogVM x:Key="vmInstance"/>
</MainWindow.Resources>
<StackPanel>
<YourUserControl1 DataContext="{StaticResource vmInstance}"/>
<YourUserControl2 DataContext="{StaticResource vmInstance}"/>
</StackPanel>
</MainWindow>
I am trying to implement an intermediary class ViewModelSelector that sets up and selects the current View/ViewModel to be shown as part of the main view main window MainView.xaml. I am trying to do this by defining a DataTemplate for ViewModel1 (see below) inside MainView.xaml and then using a ContentControl which I bind to the property CurrentViewModel of ViewModelSelector. The ViewModelSelector assigns ViewModel1 to its property CurrentViewModel. The idea is then to extend this with more DataTemplates and ViewModels and using UserControls to have the ViewModelSelector setup and decide which ViewModel to show (ViewModel1, ViewModel2, etc.). However for some reason this is not working:
When binding the ContentControl to ViewModelSelector.CurrentViewModel using <ContentControl Content="{Binding ViewModelSelector.CurrentViewModel}"/> the datatemplate is not shown (see MainView.xaml below). But no other error is thrown (that I can tell).
For debugging purposes I also created a CurrentViewModelInMainViewModel property in MainViewModel.cs which I set to ViewModelSelector.CurrentViewModel (CurrentViewModelInMainViewModel = ViewModelSelector.CurrentViewModel;). Binding directly to it (<ContentControl Content="{Binding CurrentViewModelInMainViewModel}"/>) works and the DataTemplate is shown.
So what am I doing wrong?
Here is the elided code. I hope I did not put any error in it, as I am not at work right now and can't test ...
My MainViewModel.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ProgramEditor.ViewModel
{
using GalaSoft.MvvmLight;
using ProgramEditor.View;
class MainWindowViewModel : ViewModelBase
{
private ViewModelSelector ViewModelSelector;
public ViewModelSelector ViewModelSelector
{
get { return ViewModelSelector; }
set {
ViewModelSelector = value;
RaisePropertyChanged("ViewModelSelector");
}
}
private ViewModelBase currentViewModelInMainViewModel;
public ViewModelBase CurrentViewModelInMainViewModel
{
get
{
return currentViewModelInMainViewModel;
}
set
{
if (currentViewModel == value)
return;
currentViewModelInMainViewModel = value;
RaisePropertyChanged("CurrentViewModelInMainViewModel");
}
}
public MainWindowViewModel()
{
ViewModelSelector ViewModelSelector = new ViewModelSelector();
CurrentViewModelInMainViewModel = ViewModelSelector.CurrentViewModel;
}
}
}
The class ViewModelSelector:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ProgramEditor.ViewModel
{
using GalaSoft.MvvmLight;
class ViewModelSelector : ViewModelBase
{
public ViewModelBase CurrentViewModel
{
get { return currentViewModel; }
set
{
if (currentViewModel == value)
return;
currentViewModel = value;
RaisePropertyChanged("CurrentViewModel");
}
}
private ViewModelBase currentViewModel;
public ViewModelSelector()
{
CurrentViewModel = new ViewModel1();
}
}
}
The dummy ViewModel1:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ProgramEditor.ViewModel
{
using GalaSoft.MvvmLight;
public class ViewModel1 : ViewModelBase
{
}
}
<DataTemplate DataType="{x:Type ViewModel:FirstViewModel}">
<TextBlock Text="There be dragons here." FontSize="50"/>
</DataTemplate>
My MainView.xaml:
<Window x:Class="ProgramEditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:ProgramEditor.View"
xmlns:ViewModel="clr-namespace:ProgramEditor.ViewModel"
Title="MainWindow" Height="900" Width="1600">
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:ViewModel1}">
<TextBlock Text="There be dragons here." FontSize="50"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<!-- This works (shows up in main window): -->
<ContentControl Content="{Binding CurrentViewModelInMainViewModel}"/>
<!-- This does not work (does not show up in main window): -->
<!--<ContentControl Content="{Binding ViewModelSelector.CurrentViewModel}"/>-->
</StackPanel>
</Window>
The answer was a trivial programming error. I was overriding the ViewModelSelector property in the constructor of MainViewModel.cs here: ViewModelSelector ViewModelSelector = new ViewModelSelector();
After changing that line to ViewModelSelector = new ViewModelSelector(); it now works as expected.