是否可以使用绑定按名称加载控件?

时间:2013-07-10 13:53:35

标签: wpf binding controls

在我的应用程序中,我需要一组不同的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-1 只需使用设备对象中的路径在我的视图模型中加载UserControl即可。这将打破MVVM分离并渲染该部分'untestable'

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>

2 个答案:

答案 0 :(得分:1)

我认为Idea 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。它将根据您想要的任何标准返回正确的模板。

答案 1 :(得分:0)

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

XAML:

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

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