Reputation: 13516
In my application I need a collection of different I/O devices e.g. Serial, OPC, USB devices etc. I have used the MEF framework to provide a way of handling the collection and allowing new devices to be added.
My project looks something like this:
Atf.Model 'contains the model
Atf.Gui 'the views and view-models
Atf.Devices 'contains implementations of IDevice and exports them
Many devices need configuration so the device interface exposes a path to a custom control and the corresponding view-model which handle the editing of the device configuration. I am trying to stick to the MVVM pattern and want to separate the view and model as far as possible. At the same time I want to keep the coupling to the device collection as loose as possible.
In Atf.Gui
I have a control which displays all the discovered devices and displays the activated devices. When an activated device is selected I want to display its' editor dynamically.
How might I do this? Here are some (crazy) ideas:
Idea-1 Simply load a UserControl
in my view-model using the path in the device object. This would break the MVVM separation and render that part 'untestable'
public System.Windows.Controls.UserControl ConfigureControl
{
get
{
// code to load the UserControl using the path property
}
}
Idea-2 Have the device expose only a view-model and use mapping (defined in the device repository) to pick up the view. Not sure how this would be done.
<myeditorcontainer>
<ContainerControl Content="{Binding CurrentlySelectedDeviceViewModel}"/>
</myeditorcontainer>
Idea-3 In my view, load a control using binding. Not sure if this is possible at all.
<myeditorcontainer>
<UserControl Path="{Binding CurrentlySelectedDeviceViewPath}"
DataContext="{Binding CurrentlySelectedDeviceViewModel}"/>
</myeditorcontainer>
Upvotes: 0
Views: 67
Reputation: 13516
This is what I did in the end and it seems to work ok. The key features are that the view-model has the same name as the view but with 'Model' appended and that the view-models derive from a particular class - DeviceSettingsBase.
The XAML:
<ContentControl Content="{Binding ConfigControl}"/>
The View-Model:
private RequiredDeviceViewModel rdvm;
public ContentControl ConfigControl
{
get
{
// get assembly which contains this object
Assembly assy = Assembly.GetAssembly(rdvm.Device.GetType());
// get View-Type using the name defined in the Device
Type viewType = assy.GetType(rdvm.Device.ConfigureControlResourceName, true);
// get ViewModel-Type using the viewname + Model (may be null)
Type viewModelType = assy.GetType(rdvm.Device.ConfigureControlResourceName + "Model", false);
// instantiate the view control
ContentControl view = (ContentControl)Activator.CreateInstance(viewType);
// instantiate viewModel - if type not null
if (viewModelType != null)
{
object viewModel = (object)Activator.CreateInstance(viewModelType, new object[] { rdvm.RequiredDevice.Config.ConfigString });
view.DataContext = viewModel;
// all device viewModels must derive from DeviceSettingsBase
CurrentConfigViewModel = viewModel as DeviceSettingsBase;
if (CurrentConfigViewModel != null)
{
CurrentConfigViewModel.IsEditable = IsEditable;
CurrentConfigViewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(vmb_PropertyChanged);
CurrentConfigViewModel.SettingsChanged += new SettingsChangedHandler(vmb_SettingsChanged);
}
}
return view;
}
}
Upvotes: 0
Reputation: 30418
I think that Idea 2 is the way to go.
Assuming that ContainerControl
derives from ContentControl
, you should be able to specify a DataTemplate
for each type of view model that you'll show, and then the correct UI will be rendered based on the type of the view model. For example:
<myeditorcontainer>
<ContainerControl Content="{Binding CurrentlySelectedDeviceViewModel}">
<ContainerControl.Resources>
<DataTemplate DataType="{x:Type ViewModel1Type}">
<!-- controls for ViewModel1's UI -->
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel2Type}">
<!-- controls for ViewModel2's UI -->
</DataTemplate>
</ContainerControl.Resources>
</ContainerControl>
</myeditorcontainer>
If you need more flexibility than the type of the view model in order to decide which template to use, you can specify a ContentTemplateSelector. It will return the correct template to use based on whatever criteria you want.
Upvotes: 1