Reputation: 733
I am working on a wpf dll which contains a number of views with accompanying view models to satisfy MVVM.
In my project I have a class which acts as my "view manager" which handles binding each view to their correct view model.
namespace ControlsAndResources
{
public class View
{
private static readonly ViewModelLocator s_viewModelLocator = new ViewModelLocator();
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.RegisterAttached("ViewModel", typeof(string),
typeof(ViewModelLocator), new PropertyMetadata(new PropertyChangedCallback(OnChanged)));
public static void SetViewModel(UserControl view, string value) => view.SetValue(ViewModelProperty, value);
public static string GetViewModel(UserControl view) => (string)view.GetValue(ViewModelProperty);
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl view = (UserControl)d;
string viewModel = e.NewValue as string;
switch (viewModel)
{
case "TestViewModel":
view.DataContext = s_viewModelLocator.TestViewModel;
break;
case "FooViewModel":
view.DataContext = s_viewModelLocator.FooViewModel;
break;
default:
view.DataContext = null;
break;
}
}
}
}
Then I do the binding on each of my xaml declarations (here is one example)
<UserControl x:Class="Foo.Bar.TestView"
...
...
xmlns:controls="clr-namespace:Foo.Bar.ControlsAndResources"
controls:View.ViewModel="TestViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
...
...
</Grid>
</UserControl>
And this works perfectly. But now what I would like to do is, in my MainView.xaml, include a ContentControl or an ItemControl and use Binding to update my views from my View class. How do I go about doing that?
Upvotes: 1
Views: 79
Reputation: 37059
Here's what I would do: I would write a MainViewModel
that implements INotifyPropertyChanged. I'd give it a SelectedChild property. I'm going to make some assumptions about the INotifyPropertyChanged implementation you've got. Let me know if those assumptions are off base, and we can get it working with what you've got.
MainViewModel.cs
private Object _selectedChild;
public Object SelectedChild
{
get => _selectedChild;
set => SetProperty(ref _selectedChild, value);
}
MainViewModel will be the DataContext of our main view, probably MainWindow.
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
// More about this guy later
DynamicDataTemplateCreator creator = this.FindResource("DynamicDataTemplateCreator")
as DynamicDataTemplateCreator;
// This gibberish is a stand in for whatever information the template creator
// may need to figure out what type of view belongs to what type of viewmodel.
creator.ViewLookupInformation =
"My expatriate aunt Sally ate nine autumnal polar bears in Zanzibar.";
DataContext = new MainViewModel();
}
In MainWindow's XAML, we'll put a ContentControl bound to SelectedChild, and we'll create an instance of a template selector (see below) that will be used to create the template that displays SelectedChild:
MainWindow.xaml
<Window.Resources>
<local:DynamicDataTemplateCreator x:Key="DynamicDataTemplateCreator" />
</Window.Resources>
<Grid>
<ContentControl
Content="{Binding SelectedChild}"
ContentTemplateSelector="{StaticResource DynamicDataTemplateCreator}"
/>
DynamicDataTemplateCreator.cs
And here's how we create DataTemplates to display viewmodels with views whose type is determined at runtime.
public class DynamicDataTemplateCreator : DataTemplateSelector
{
// If mainwindow or the main viewmodel has information that we need about the
// dynamically loaded views, pass that information via this property. It can be any
// type you want, preferably the exact type of the information you are passing.
public object ViewLookupInformation { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// item is the viewmodel.
Type viewType = null;
// A quickie UserControl I wrote for testing.
//viewType = typeof(VMView);
/*
* logic here to determine the type of view we want. Assign that Type to viewType
* If you need extra information from the main program, smuggle it in to here via
* ViewLookupInformation
*/
return new DataTemplate
{
VisualTree = new FrameworkElementFactory(viewType)
};
}
}
Upvotes: 1