16

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
  • 25,418
  • 43
  • 131
  • 202

2 Answers2

23

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)

Dan
  • 7,286
  • 6
  • 49
  • 114
Anvaka
  • 15,658
  • 2
  • 47
  • 56
  • Thx! Sounds reasonable. Will have a look at it! – stiank81 Feb 23 '10 at 11:56
  • 2
    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. – John Bowen Feb 23 '10 at 17:17
  • Cant agree with you John... It's an ItemsControl. It uses UIElement as default container... – Anvaka Feb 23 '10 at 17:46
  • 1
    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. – John Bowen Feb 23 '10 at 19:19
  • 3
    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 http://drwpf.com/blog/2008/03/25/itemscontrol-i-is-for-item-container. – Anvaka Feb 23 '10 at 20:36
20

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.

Dan
  • 7,286
  • 6
  • 49
  • 114
Kenneth_hj
  • 485
  • 1
  • 4
  • 11