How to display images (cards) on a WPF/XAML canvas (at specific positions) with ObservableCollection?

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:

-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:

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! :)

Answers


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.


Need Your Help

Java do while loop back to the beginning of application

java loops while-loop do-while

This question is a follow-up from this link Java SE do while loop issues

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.