1

I have an app written in WPF (MVVM), which based on some conditions, will create instances of different UserControls, These UserControls are completely independent, used to display certain information. They have some custom logic inside, like timers and so on, so I can't use Templates.

Now I face the problem that I want to create a list of UserControls in the ViewModel, and bind the host UI to it. The problem is that I don't know how to bind and what to bind. In a non MVVM project, you would simply get the layout where you want to put your controls, and add them there as children. In MVVM app, I don't know how to do this. I imagine having a WrapPanel with ItemsSource, that will add all the controls and resize itself as needed, based on the UserControls.

Can someone suggest a solution?

EDIT: My ViewModel exposes an ObservableCollection of IMyDriver right now. So that's what I thought, to break a little bit MVVM to get what I describe next: Now, Each IMyDriver can be a different type of driver, and can implement different other interfaces. I need the UI to create specific UserControls that know how to get maximum from these Drivers, based on their capabilities. In short, the UserControls connect to the device through the driver for polling data. And each UserControl does it in a specific way.

XMight
  • 1,991
  • 2
  • 20
  • 36
  • *I want to create a list of UserControls in the ViewModel*... no, no and no! That's *not* how MVVM works, so either change your approach, or remove that MVVM tag from your question... you can't have it both ways. – Sheridan Sep 16 '14 at 14:25
  • 1
    DataTemplates link VM types to UserControls. Bam. Done. –  Sep 16 '14 at 15:08
  • Will, this answer, along with Sheridan's made me think in the right direction. THX a lot! – XMight Sep 17 '14 at 08:06

3 Answers3

1

You can do it quite simply and easily by declaring specific data type classes for the data in each UserControl and define DataTemplates that expose your UserControls in the App.xaml file:

<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourViewModel">
    <YourViewsPrefix:YourView />
</DataTemplate>
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourOtherViewModel">
    <YourViewsPrefix:YourOtherView />
</DataTemplate>
<DataTemplate DataType="{x:Type YourViewModelsPrefix:AnotherViewModel">
    <YourViewsPrefix:AnotherView />
</DataTemplate>

Now whenever the Framework comes across an instance of these view model classes, it will render the associated view/UserControl. You can display them by having a property of the type of your view model using a ContentControl like this:

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

...

public YourBaseViewModelClass YourViewModelProperty { get; set; }

Make sure that all of your view models extend this class:

public YourViewModel : YourBaseViewModelClass { }
...
public AnotherViewModel : YourBaseViewModelClass { }

Then you can swap each view model (and display each related view) like this:

YourViewModelProperty  = new AnotherViewModel();
Sheridan
  • 68,826
  • 24
  • 143
  • 183
1

Based on what Will commented, and what Sheridan answered, I have found the solution to my problem.

So:

  1. I don't break MVVM by leaving ViewModel types intact.

  2. I create DataTemplates in my Window's Resources tag, and in each data template, I assign the DataTemplate to be my UserControl defined in another assembly (UICommons)

    <DataTemplate x:Key="IMultiChannelMeasurementDCDataTemplate">
        <uicommon:MeasurementMax8ChannelMonitoringUserControl/>
    </DataTemplate>
    
  3. I create a Template Selector in my application assembly, and based on the interfaces the DataTypes implement, I return the right DataTemplate, that I assign in the same Window's Resources tag

        <!-- DataTemplate Selector -->
    <local:DriverComponentDataTemplateSelector x:Key="templateSelector"
          DefaultDCDataTemplate="{StaticResource DefaultDCDataTemplate}"
          IIhcDCDataTemplate="{StaticResource IIhcDCDataTemplate}" 
          IMultiChannelMeasurementDCDataTemplate="{StaticResource IMultiChannelMeasurementDCDataTemplate}"
          IProgrammablePowerSourceDCDataTemplate="{StaticResource IProgrammablePowerSourceDCDataTemplate}"
          IEnvDCDataTemplate="{StaticResource IEnvDCDataTemplate}"/>
    
  4. I create an ItemsControl in the Window, with the following XAML code, that binds itself to my ObservableCollection of items

    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
        <ItemsControl ItemTemplateSelector="{StaticResource templateSelector}" ItemsSource="{Binding DriverComponentsInfo}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" x:Name="ucWrapPanel">
                    </WrapPanel>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>            
        </ItemsControl>
    </ScrollViewer>
    
  5. I enjoy dynamically created UserControls based on different Drivers!

P.S. I upvoted Will's comment and Sheridan's answer, because without these, I wouldn't be able to find the solution. Thx!

Community
  • 1
  • 1
XMight
  • 1,991
  • 2
  • 20
  • 36
0

They have some custom logic inside, like timers and so on, so I can't use Templates.

This does not follow. I think you may have a misconception about the capabilities of WPF.

Also, as you want to use MVVM: Binding to a list of UserControls is breaking the pattern. View-models should only ever reference other view-models (and models); they do not know anything about the UI. Bind to a collection of view-models which have associated UserControls as their views (consider using implicit DataTemplates). To bind a WrapPanel you use an ItemsControl and set its ItemsPanel accordingly.

Community
  • 1
  • 1
H.B.
  • 166,899
  • 29
  • 327
  • 400