0

在我的应用程序中,我需要一组不同的 I/O 设备,例如串行、OPC、USB 设备等。我使用 MEF 框架提供了一种处理集合并允许添加新设备的方法。

我的项目看起来像这样:

Atf.Model     'contains the model
Atf.Gui       'the views and view-models
Atf.Devices   'contains implementations of IDevice and exports them

许多设备需要配置,因此设备接口公开了自定义控件的路径和处理设备配置编辑的相应视图模型。我试图坚持 MVVM 模式,并希望尽可能地分离视图和模型。同时我想保持与设备集合的耦合尽可能松散。

Atf.Gui我有一个控件,它显示所有发现的设备并显示激活的设备。选择激活的设备后,我想动态显示其编辑器。

我该怎么做?这里有一些(疯狂的)想法:

Idea-1UserControl只需使用设备对象中的路径在我的视图模型中加载一个。这将打破 MVVM 分离并使该部分“不可测试”

public System.Windows.Controls.UserControl ConfigureControl 
{
   get 
   {
       // code to load the UserControl using the path property
   }
}

Idea-2让设备只公开一个视图模型并使用映射(在设备存储库中定义)来获取视图。不知道如何做到这一点。

<myeditorcontainer>
    <ContainerControl Content="{Binding CurrentlySelectedDeviceViewModel}"/>
</myeditorcontainer>

Idea-3在我看来,使用绑定加载控件。不确定这是否可能。

<myeditorcontainer>
    <UserControl Path="{Binding CurrentlySelectedDeviceViewPath}" 
                 DataContext="{Binding CurrentlySelectedDeviceViewModel}"/>
</myeditorcontainer>
4

2 回答 2

1

我认为想法 2 是要走的路。

假设ContainerControl派生自ContentControl,您应该能够为DataTemplate要显示的每种视图模型类型指定一个,然后将根据视图模型的类型呈现正确的 UI。例如:

<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>

如果您需要比视图模型类型更大的灵活性来决定使用哪个模板,您可以指定一个ContentTemplateSelector。它将根据您想要的任何标准返回要使用的正确模板。

于 2013-07-10T14:00:36.360 回答
0

这就是我最后所做的,它似乎工作正常。关键特性是视图模型与视图具有相同的名称,但附加了“模型”,并且视图模型派生自特定类 - DeviceSettingsBase。

XAML:

<ContentControl Content="{Binding ConfigControl}"/>

视图模型:

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;
    }
}
于 2013-07-12T09:06:11.917 回答