Binding WPF Canvas Children to an ObservableCollection

17,820

Solution 1

I think you can do this with ItemsControl + ItemsPanelTemplate. Like this:

<ItemsControl ItemsSource="{Binding YourCollection}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

To read more about this approach refer to Dr.WPF: ItemsControl: A to Z (P is for Panel)

Solution 2

The solution Anvaka suggested is great, but as John Bowen pointed out you need to know a bit more, if you would like to actually bind your items to the Canvas attached properties.

Here's an example on how to bind to Canvas.Left and Canvas.Top:

<ItemsControl ItemsSource="{Binding YourCollection}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding YourModelLeft}" />
            <Setter Property="Canvas.Top" Value="{Binding YourModelTop}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

Btw, I found the solution on this question, after I tried Anvaka's suggestion, where the binding didn't work.

Hopefully this helps others, looking for the same answer.

Share:
17,820
stiank81
Author by

stiank81

System Developer mainly living in the .Net-world. Coding in C#, Javascript, html, css, ..

Updated on July 10, 2022

Comments

  • stiank81
    stiank81 almost 2 years

    In my WPF application I have a Canvas in which I do some drawing. Earlier I handled the drawing in the code behind, but now I've factored everything out to a ViewModel. This gives me some challenges..

    I have a few InkPresenter objects holding Strokes. Earier I added them as children to the Canvas in the code behind - like this:

    // Build an InkPresenter: 
    var someInkPresenter = BuildInkPresenter(..); 
    //_myCanvas is the <Canvas> I want to display it in: 
    _myCanvas.Children.Add(someInkPresenter); 
    

    Now - not building the InkPresenter in the code-behind of the XAML that holds _myCanvas I need to do this differently. What I'd like to do is to create an InkPresenter and add it to a collection:

    public ObservableCollection<InkPresenter> Drawings;
    

    My problem now is how to bind the Canvas to this ObservableCollection - and have the InkPresenters displayed when added to the collection. Can I achieve this using Data Bindings somehow?

  • stiank81
    stiank81 over 14 years
    Thx! Sounds reasonable. Will have a look at it!
  • John Bowen
    John Bowen over 14 years
    This is normally the approach you'd take but in this case you're probably going to run into issues because the InkPresenters are not going to be direct children of the Canvas like they are when you add them in code. Instead the Visual Tree is going to have an additional ContentPresenter injected around each InkPresenter item. To get this method to work you should look at pulling out the actual data (Strokes) from each InkPresenter and storing a collection of those in your VM. You can then create a custom ItemsControl that overrides GetContainerForItemOverride to return an InkPresenter.
  • Anvaka
    Anvaka over 14 years
    Cant agree with you John... It's an ItemsControl. It uses UIElement as default container...
  • John Bowen
    John Bowen over 14 years
    Anvaka - No. It doesn't. If you look at ItemsControl in Reflector or Snoop an instance at runtime you can see it uses a ContentPresenter (which does derive from UIElement) as the default container. Even if it was using a base UIElement it's still modifying the Canvas visual tree from Stian's original.
  • Anvaka
    Anvaka over 14 years
    John, I've tried that before posting disagreement :). InkPresenter is direct child of a Canvas. If a UIElement is added to the Items collection of an explicit ItemsControl instance (as opposed to an instance of a derived class like ListBox), it will become a direct child of the items panel. If a non-UIElement is added, it will be wrapped within a ContentPresenter. Check the table at the end of this post drwpf.com/blog/2008/03/25/itemscontrol-i-is-for-item-contain‌​er.