1

I'll post how far I got (see Update/Edit 2014-06-29 below) with this other Q/A as a edit below to this original question:

...or...

"Looking for a way to bind/tie ObservableCollections to positions on the Canvas, so code-behind can tell the ObservableCollections which cards to display where."

Experts,

That's a lot for a subject line. I'll start with the same question asked at first the highest level, then break it down i levels to explain the project idea/need:

VB/S2012, but I can convert from C#

Top Level

-Place playing cards (I have the images) on a card table (it's a canvas inside a Viewbox).

Next Level of Detail

-I imagine each possible card position is known (solitaire) in advance. An ObservableCollection seems perfect to store the cards themselves, and to have the logic act upon. -With just 52 cards, I could also enter each into a ResourceDictionary, or just into the MainWindow XAML file. Or, just as easily do it in code-behind.

A Little More Detail

-The code-behind will determine which card (face up or down) will be in which position, and there won't be any user interaction with the cards. -I have images for cards in both PNG and SVG, but the PNG files look fine at full screen, so hopefully I can avoid complex converters with SVG. -I imagine assigning the positions of all possible card positions, and somehow binding those positions in a way that my ObservableCollections can use directly (position 0 is draw pile, etc.)

Progress So Far

[note: Since I can't get a working combination, I can't say whether Image or filling a Rectangle, or even another approach is best. This is why I am asking. So do not assume I am married to either of these approaches. I just want to get to the business lofic and have this part done!]

-I can draws the Viewbox/Canvas, and with a rectangle/image object, it resizes perfectly with the main window:

<Viewbox>
    <Grid>
        <Canvas x:Name="TheCanvas" HorizontalAlignment="Left" Height="774" Margin="10,29,0,0" VerticalAlignment="Top" Width="969">
            <Image x:Name="Foundation1" Height="679" Canvas.Left="10" Width="726" Source="images/10_of_clubs.png" />
        [...]

-I've added a ResourceDictionary like so:

Application.XAML

<Application x:Class="Application"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="CardTable.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="PlayingCardsResourceDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

PlayingCardsResourceDictionary.xaml (in root dir of project):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DrawingBrush x:Key="CardImages" >
        <DrawingBrush.Drawing>
            <DrawingGroup>
                <DrawingGroup.Children>
                    <ImageDrawing x:Name="H2" ImageSource="images/2_of_hearts.png" />
                    <ImageDrawing x:Name="H3" ImageSource="images/3_of_hearts.png" />
                </DrawingGroup.Children>
            </DrawingGroup>
        </DrawingBrush.Drawing>
    </DrawingBrush>
</ResourceDictionary>

-I was hoping to use assignments (like "H2", "H3") in place of files & paths with this approach, but I cannot get it to show anything. -With this hard coded:

<Image x:Name="Foundation1" Height="679" Canvas.Left="10" Width="726" Source="images/10_of_clubs.png" />

...I tried to adjust the Foundation1.Source property in code, but even though this would seem to be able to work (based on above):

Foundation1.Source = "images/jack_of_diamonds.png"

...it doesn't (string worked in XAML, but not in code-behind):

Value of type 'String' cannot be converted to 'System.Windows.Media.ImageSource'.   

-The images/ directory above is off the project's root folder, and Intellisense will complain if I call a file with "ImageSource=" that doesn't exist-so I know that part is right.

Example:

I can assign CardImages as a StaticResource for Card1, but CardImages has more than one image in it:

enter image description here

-And no attempts from code-behind can convince an image to be produced.

Needed/Summary

A way to bind/tie ObservableCollections to positions on the Canvas, so code-behind can tell the ObservableCollections which cards to display where. (so good I'll use it above for high level description/secondary subject!)


Update/Edit 2014-06-29

Although it might seem like a duplicate, it doesn't look like it. For one, it doesn't address the images as available/aliased in a resourcedictionary, and another point includes I can't get it to work... :)

As indicated at the top of the post, here is the complete system & where I am with what @Clemens posted a year ago in what is thought by him to be a duplicate:

Note: Don't be confused. I am _hoping_ it really is a dupe, and I can just post here a complete working solution. It's been too many hours tweking all parameters with virtually nothing on the canvas. So please feel free to make it work! :)

The Card() class is just properties-nothing more. Here it is with the parts I've been trying to get to work:

Public Class Card
    Public Property Left As Double 
    Public Property Top As Double 
    Public Property Width As Integer ' of card in pixels
    Public Property Height As Integer ' of card in pixels
    Public Property Source As String  ' path to image
    Public Property SourceImage As Image ' image as an Image()
End Class

Next is the XAML itself (constantly being added/to/edited):

<Window x:Class="MainWindow" x:Name="CardTable"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Solitaire" Height="843" Width="997">
    <Grid>
        <Viewbox>
            <Grid Height="584">
                <Canvas x:Name="TheCanvas" HorizontalAlignment="Left" Height="564" 
                        Margin="10,10,0,0" VerticalAlignment="Top" Width="688" 
                        >
                    <ItemsControl ItemsSource="{Binding Source=TheCards}" Width="300" Height="400" Background="Azure">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <Canvas/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemContainerStyle>
                            <Style>
                                <Setter Property="Canvas.Left" Value="{Binding Left}"/>
                                <Setter Property="Canvas.Top" Value="{Binding Top}"/>
                            </Style>
                        </ItemsControl.ItemContainerStyle>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Image Source="{Binding Source}" Width="{Binding Width}" Height="{Binding Height}" />
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </Canvas>
            </Grid>
        </Viewbox>
    </Grid>
</Window>

Finally, the code-behind, which creates the ObservableCollection of Card():

Imports System.Collections.ObjectModel

Class MainWindow
    Public Property TheCards As New ObservableCollection(Of Card)()

    Public Sub New()
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.
        Me.Setup()
    End Sub
    Private Sub Setup()
        Dim aCard As Vegas.Card = New Card() With {.Source = "images/queen_of_diamonds.png", .Left = 200, .Top = 150.0, .Width = 125, .Height = 200}
        TheCards.Add(aCard)
    End Sub
End Class

The resulting WPF Window, when rendered, is a disappointing:

enter image description here

note: If you look carefully, you can see that I put the pointer at the bottom right corner of the azure background, thus showing that at least that showed up...

I hope I've kept this update straight & to the point.

Thank you all again!


Update/Edit 2014-06-30

Error output I just noticed in Immediate Window:

[...]
System.Windows.Data Error: 40 : BindingExpression path error: 'Width' property not found on 'object' ''Char' (HashCode=7536755)'. BindingExpression:Path=Width; DataItem='Char' (HashCode=7536755); target element is 'Image' (Name=''); target property is 'Width' (type 'Double')
System.Windows.Data Error: 40 : BindingExpression path error: 'Height' property not found on 'object' ''Char' (HashCode=7536755)'. BindingExpression:Path=Height; DataItem='Char' (HashCode=7536755); target element is 'Image' (Name=''); target property is 'Height' (type 'Double')
System.Windows.Data Error: 40 : BindingExpression path error: 'Source' property not found on 'object' ''Char' (HashCode=7536755)'. BindingExpression:Path=Source; DataItem='Char' (HashCode=7536755); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')
[...]

Makes me think they need to be in "<ItemsControl.ItemContainerStyle>" below...:

xmlns:proj="clr-namespace:Vegas"
[...]
<Grid>
    <Viewbox>
        <Grid Height="584">
            <Canvas x:Name="TheCanvas" HorizontalAlignment="Left" Height="564" 
                        Margin="10,10,0,0" VerticalAlignment="Top" Width="688" 
                        >
                <ItemsControl ItemsSource="{Binding Source=TheCards}" 
                                  Width="300" 
                                  Height="400" 
                                  Background="Pink">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Canvas />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate DataType="{x:Type proj:Card}">
                            <Image Source="{Binding Source}" 
                                   Width="{Binding Width}" 
                                   Height="{Binding Height}" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemContainerStyle>
                        <Style x:Name="CardStyle">
                            <Setter Property="Canvas.Left" Value="{Binding Left}"/>
                            <Setter Property="Canvas.Top" Value="{Binding Top}"/>
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                </ItemsControl>
            </Canvas>
        </Grid>

Thank you very much in advance for any answers, solutions, or insights. I just want this display framework for the card table done so I can move on to the fun part of programming the code-behind! :)

user229044
  • 232,980
  • 40
  • 330
  • 338
  • 2
    You're looking for an [`ItemsControl`](http://drwpf.com/blog/itemscontrol-a-to-z/). And BTW, Remove the `DrawingBrush`, I'm not sure what you want to achieve with that but it's wrong. And add your images [as resources](http://stackoverflow.com/a/19938962/643085) in the project. And forget code behind. WPF is not winforms. Create a proper ViewModel and use proper DataBinding instead. – Federico Berasategui Jun 29 '14 at 20:37
  • @Clemens, I looked, found none. None came up in the dupe suggester thingie at the top when creating/editing. I take all that back. Thanks for adding the citation. I actually went there, and that question didn't seem to have the soup-to-nuts, and might not have been the best approach when answered. Thanks! – Compassionate Narcissist Jun 29 '14 at 21:26
  • the link to the duplicate is up at the top; and he is not a mod; mods have diamonds next to their name. He has a gold badge in VB so he can close a question without other voters – Ňɏssa Pøngjǣrdenlarp Jun 29 '14 at 21:28
  • @Clemens, I cannot get the example code you gave as the dupe to this question a year ago working, with all kinds of adjustments. It appears not to be a complete example, but just parts. I can't get _anything_ to show up on my canvas, so for now I'm struggling for the answer... – Compassionate Narcissist Jun 30 '14 at 01:32
  • 1
    If you have solved your problem, post the answer *as an answer*, and accept it. Answers don't go in the body of the question. – user229044 Jul 01 '14 at 01:03
  • @meagar, yep. I started out doing just that, but those above me complained when I did-even though in rules & regs it promotes solving your own question, and posting it (even tutorial-style, like Code Project)... I just try to stay out of trouble, which is hard with a myriad of bosses... – Compassionate Narcissist Jul 01 '14 at 22:29

1 Answers1

1

If you have a particular class for your cards, then you can define a DataTemplate for it. If you have properties for the positions, then you could also data bind those in an ItemsContainerStyle:

<DataTemplate DataType="{x:Type YourPrefix:Card}">
    <Image Width="{Binding Width}" Height="{Binding Height}" 
        Source="{Binding Source}" />
</DataTemplate>
<Style x:Key="CardStyle">
    <Setter Property="Canvas.Left" Value="{Binding Left}" />
    <Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>

Then you could data bind a collection of these to the ItemsSource property of an ItemsControl and use a Canvas as the ItemsPanel:

<ItemsControl ItemsSource="{Binding Cards}"
    ItemsContainerStyle="{StaticResource CardStyle}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

You can read the Data Binding Overview page on MSDN for more information about this.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • 1
    This won't work out of the box, as the `Canvas.Left` and `Canvas.Top` properties would have to be set on the item container (i.e. the ListBoxItem here), not on the control in the DataTemplate. The corresponding bindings would have to be moved to an appropriate ItemContainerStyle. – Clemens Jun 29 '14 at 21:48
  • Error output I just noticed in Immediate Window: – Compassionate Narcissist Jun 30 '14 at 20:00
  • Can you clarify that please @PatTrainor? – Sheridan Jun 30 '14 at 20:33
  • @Sheridan, the errors were actually up in the edit/OP area, which is the only place I can post to. Even the solution I just posted isn't allowed as a solution... :) Thank you very much for your help! – Compassionate Narcissist Jul 01 '14 at 00:15