1

My mainwindow ViewModel has an ObservableCollection of ViewModels, called ViewModels.

The Mainwindow XAML has an ItemsControl with ItemsSource bound to ViewModels.

When I have

<ItemsControl ItemsSource="{Binding ViewModels}" />

The Views associated with each ViewModel in the collection are rendered one below the other. The Views are UserControls, displaying dataGrids.

How can I position them in a customizable way, for example such that VM1 is on the left, and VM2 and VM3 are stacked one on top of the other to the right of VM1.

Each VieModel has PosX, PosY, Width and Height properties, and I've been trying various templating methods but no success so far.

I have found examples of how to this with Observable collections of images, but one thing I'm struggling is that my collection is of ViewModels.

animuson
  • 53,861
  • 28
  • 137
  • 147
GilShalit
  • 6,175
  • 9
  • 47
  • 68
  • I need to show different views associated with different view models in itemsControls using observablecollections as what you did. I am new to WPF. Can you share your code with me! – Manish Dubey Mar 25 '15 at 15:34
  • @Manish Dubey, see the code in the accepted answer below – GilShalit Mar 30 '15 at 09:08

1 Answers1

6

Make your ItemsControl.ItemsPanel into a Canvas, and set your Top/Left/Height/Width in the ItemTemplate.

<ItemsControl ItemsSource="{Binding ViewModels}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- Height, Width, and Background are required to render size correctly -->
            <Canvas Height="600" Width="800" Background="White" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemContainerStyle>
        <Style TargetType="{x:Type FrameworkElement}">
            <Setter Property="Canvas.Top" Value="{Binding Top}" />
            <Setter Property="Canvas.Left" Value="{Binding Left}" />
            <Setter Property="Height" Value="{Binding Height}" />
            <Setter Property="Width" Value="{Binding Width}" />
        </Style>
    </ItemsControl.ItemContainerStyle>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ContentControl>
                <ContentPresenter ClipToBounds="True" Content="{TemplateBinding Content}"/>
            </ContentControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Thanks Rachel, but dont i need a DataTemplate too. Running with your solution i get a blank canvas, whilst as i said above, omitting all the ItemsControl internal stuff, i get my ViewModels stacked on top of each other. – GilShalit Mar 10 '11 at 16:14
  • @GilShalit Yes you need a DataTemplate. I thought you had that covered, sorry. I'll add it in – Rachel Mar 10 '11 at 16:20
  • Almost there, with your gracious help... Now I'm getting the first viewmodel to display OK, but the second one which I've set at left=VM1.width, is not displaying. Any ideas? And what does ClipToBounds do in this context? – GilShalit Mar 10 '11 at 16:50
  • @GilShalit Is VM2.Left, Top, Height, and Width all set correctly? I'd verify the values are correct first. Second, check and make sure the Canvas is large enough to fit its content. I usually do this by setting the background color of the Canvas so I can see the space it is using. The ClipToBounds is just there since I copy/pasted that line from some code I have and didn't both removing it. It clips the object if the size exceeds the available space in the canvas. – Rachel Mar 10 '11 at 17:23
  • Obviously I checked and double checked... But now, at your urging, i checked again, and for VM2, the values were being set in a function that was not being called... Thanks for all your help. – GilShalit Mar 10 '11 at 17:56