0

I resurrected code from 10 years ago to draw cards (uses cards.dll). I'll have another program actually manipulate them. I got it working in WinForm, but would really like to figure out a way to access the graphics in WPF.

It works as I'll paste it, but again as a winform:

Form1.vb:

Imports System.Runtime.InteropServices

Public Class Form1
    Private SQ As Card = New Card(Suit.Spade, Face.Queen)
    Private DK As Card = New Card(Suit.Diamond, Face.King)
    Private C10 As Card = New Card(Suit.Club, Face.Ten)
    Private H2 As Card = New Card(Suit.Heart, Face.Two)
    Private SA As Card = New Card(Suit.Spade, Face.Ace)
#Region "Form Events"
    Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' MyBase.Load
        Card.Init()
    End Sub
    Private Sub Main_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        Card.Deinit()
    End Sub
#End Region
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        SQ.PaintGraphicFace(e.Graphics, 10, 10)
        DK.PaintGraphicFace(e.Graphics, 50, 10)
        C10.PaintGraphicFace(e.Graphics, 110, 200)
        C10.PaintGraphicBack(e.Graphics, 30, 300)
        H2.PaintGraphicBack(e.Graphics, 250, 370)
        SA.PaintGraphicFace(e.Graphics, 200, 50)
    End Sub
End Class

Just getting this far took a bit of work/digging!

Now Card.vb:

Public Class Card

#Region "Construcor"
    Public Sub New(ByVal cardSuit As Suit, ByVal cardFace As Face)
        Init()
        FCardSuit = cardSuit
        FCardFace = cardFace
    End Sub
#End Region
#Region "Private class vars"
    Private FCardFace As Face
    Private FCardSuit As Suit
#End Region
#Region "External methods and related fields"
    Private Shared initialized As Boolean = False
    Private Shared width As Integer = 75
    Private Shared height As Integer = 100
    '
    Private Declare Function cdtInit Lib "cards.dll" (ByRef width As Integer, ByRef height As Integer) As Boolean
    Private Declare Function cdtDrawExt Lib "cards.dll" (ByVal hdc As IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal dx As Integer, ByVal dy As Integer, ByVal card As Integer, ByVal suit As Integer, ByVal color As Integer) As Boolean
    Private Declare Sub cdtTerm Lib "cards.dll" ()
#End Region
#Region "Properties"
    Public Property CardSuit() As Suit
        Get
            Return FCardSuit
        End Get
        Set(ByVal Value As Suit)
            FCardSuit = Value
        End Set
    End Property
#End Region
#Region "Open & Close"
    Public Shared Sub Init()
        If (initialized) Then Return
        initialized = True
        cdtInit(width, height)
    End Sub
    Public Shared Sub Deinit()
        If (Not initialized) Then Return
        initialized = False
        cdtTerm()
    End Sub
#End Region
#Region "Painting"
    Public Sub PaintGraphicFace(ByVal g As Graphics, ByVal posX As Integer, ByVal posY As Integer)
        PaintGraphicFace(g, posX, posY, width, height)
    End Sub
    Public Sub PaintGraphicFace(ByVal g As Graphics, ByVal posX As Integer, ByVal posY As Integer, ByVal sizeX As Integer, ByVal sizeY As Integer)
        Dim hdc As IntPtr = g.GetHdc()
        Try
            Dim Card As Integer = CType(Me.FCardFace, Integer) * 4 + CType(Me.FCardSuit, Integer)
            cdtDrawExt(hdc, posX, posY, sizeX, sizeY, Card, 0, 0)
        Finally
            g.ReleaseHdc(hdc)
        End Try
    End Sub
    Public Sub PaintGraphicBack(ByVal g As Graphics, ByVal x As Integer, ByVal y As Integer)
        PaintGraphicBack(g, x, y, width, height)
    End Sub
    Public Sub PaintGraphicBack(ByVal g As Graphics, ByVal x As Integer, _
     ByVal y As Integer, ByVal dx As Integer, ByVal dy As Integer)
        Dim hdc As IntPtr = g.GetHdc()
        Try
            cdtDrawExt(hdc, x, y, dx, dy, 59, 1, 0)
        Finally
            g.ReleaseHdc(hdc)
        End Try
    End Sub
#End Region

End Class

Public Enum Suit
    Diamond = 1
    Heart = 2
    Spade = 3
    Club = 4
End Enum

Public Enum Face
    Ace = 0
    Two = 1
    Three = 2
    Four = 3
    Five = 4
    Six = 5
    Seven = 6
    Eight = 7
    Nine = 8
    Ten = 9
    Jack = 10
    Queen = 11
    King = 12
End Enum

Tweaked a bit from original sources to make more sense, and to get rid of lots of typos in examples over the years... weird...

Anyway, the above is enough to get someone painting cards immediately. I could not find any such working examples. The only catch? Put cards.dll in the same dir as the exe, or somewhere else your environ will find it...

Because of the calls to hdc, I can't find any parallels to WPF.

user229044
  • 232,980
  • 40
  • 330
  • 338

1 Answers1

1

Based on this answer, you can use a Bitmap to draw on (you will need to add a reference to System.Drawing in your WPF application), and use that Bitmap as the source for a WPF Image control.

MainWindow.xaml

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="600" Width="600">
    <DockPanel LastChildFill="True">
        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Image Name="myImage" Stretch="None"/>
        </ScrollViewer>
    </DockPanel>
</Window>

MainWindow.xaml.vb

Imports System.Drawing
Imports System.Windows.Interop

Class MainWindow

    Private SQ As Card = New Card(Suit.Spade, Face.Queen)
    Private DK As Card = New Card(Suit.Diamond, Face.King)
    Private C10 As Card = New Card(Suit.Club, Face.Ten)
    Private H2 As Card = New Card(Suit.Heart, Face.Two)
    Private SA As Card = New Card(Suit.Spade, Face.Ace)

    Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded

        Using tempBitmap = New Bitmap(1000, 1000)
            Using g = Graphics.FromImage(tempBitmap)
                SQ.PaintGraphicFace(g, 10, 10)
                DK.PaintGraphicFace(g, 50, 10)
                C10.PaintGraphicFace(g, 110, 200)
                C10.PaintGraphicBack(g, 30, 300)
                H2.PaintGraphicBack(g, 250, 370)
                SA.PaintGraphicFace(g, 200, 50)
                Dim hbmp = tempBitmap.GetHbitmap()
                Dim options = BitmapSizeOptions.FromEmptyOptions()
                Me.myImage.Source = Imaging.CreateBitmapSourceFromHBitmap(hbmp,
                    IntPtr.Zero, Int32Rect.Empty, options)
            End Using
        End Using

        Me.myImage.InvalidateMeasure()
        Me.myImage.InvalidateVisual()

    End Sub

End Class
Community
  • 1
  • 1
Mark
  • 8,140
  • 1
  • 14
  • 29
  • Thanks, @Mark, this was awesome! Now that I've gone through all the work, I am doubting that using a bitmap as the interface/output is a good idea... I am leaning toward WPF and individual images... For one thing, hit detection would be easier, and animation is a LOT easier than doing the redraw thing in WinForm... Still, I will create a Winform version of the above test and put it on my blog for others! Thanks again! – Compassionate Narcissist Jun 26 '14 at 02:04