C# Code, Tutorials and Full Visual Studio Projects

CardPanel – A custom WPF Panel

Posted by on Feb 9, 2012 in Code Snippets, WPF | 0 comments

Panels are just about everywhere in WPF. I wanted to create a simple panel to demonstrate how easy it is to make your own.

This panel arranges the children of the panel like a deck of playing cards, where each is stacked on the one prior.

Here is a video that quickly demonstrates how it works.

As always the source code is available for free so grab a copy.
Download the Source Code




Please note that this example uses the FluidMoveBehavior from Microsoft Blend, so you will need to have the Microsoft.Expression.Interaction and System.Windows.Interactivity for it to work properly.




CardPanel

The CardPanel class like all Panels arranges a set of children. CardPanel arranges them like a deck of playing cards. Here is the code:

using System;
using System.Windows;
using System.Windows.Controls;

namespace Jarloo.Cards
{
    public class CardPanel : Panel
    {
        public static readonly DependencyProperty CardHeightProperty = 
            DependencyProperty.Register("CardHeight", typeof (double), typeof (CardPanel), new PropertyMetadata(50D));

        public static readonly DependencyProperty CardWidthProperty = 
            DependencyProperty.Register("CardWidth", typeof (double), typeof (CardPanel), new PropertyMetadata(100D));

        public static readonly DependencyProperty OffsetXProperty = 
                DependencyProperty.Register("OffsetX", typeof (double), typeof (CardPanel), new PropertyMetadata(1D));

        public static readonly DependencyProperty OffsetYProperty = 
            DependencyProperty.Register("OffsetY", typeof (double), typeof (CardPanel), new PropertyMetadata(5D));

        public double OffsetY
        {
            get { return (double) GetValue(OffsetYProperty); }
            set { SetValue(OffsetYProperty, value); }
        }

        public double OffsetX
        {
            get { return (double)GetValue(OffsetXProperty); }
            set { SetValue(OffsetXProperty, value); }
        }
        
        public double CardWidth
        {
            get { return (double) GetValue(CardWidthProperty); }
            set { SetValue(CardWidthProperty, value); }
        }
        
        public double CardHeight
        {
            get { return (double) GetValue(CardHeightProperty); }
            set { SetValue(CardHeightProperty, value); }
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            Size resultSize = new Size(0, 0);

            foreach (UIElement child in Children)
            {
                child.Measure(new Size(CardWidth,CardHeight));
                resultSize.Width = Math.Max(resultSize.Width, child.DesiredSize.Width);
                resultSize.Height = Math.Max(resultSize.Height, child.DesiredSize.Height);
            }
            
            return resultSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            double topOffset = 0;
            double leftOffset = 0;

            foreach (UIElement child in Children)
            {
                double left = (finalSize.Width - CardWidth) - leftOffset;
                double top = (finalSize.Height - CardHeight) - topOffset;

                child.Arrange(new Rect(left, top, CardWidth, CardHeight));

                topOffset += OffsetY;
                leftOffset += OffsetX;
            }

            return finalSize;
        }
    }
}

The CardPanel class is really simple as you can see. It has four properties that you can change.

  • CardHeight – The height to make each card
  • CardWidth – The width to make each card
  • OffsetX – How far each new card should be offset to the left from the previous
  • OffsetY – How far each new card should be offset to the top from the previous

CardPanel forces all cards to be the exact same width and height. But contrary to the name the children can be anything you like, and don’t need to be any special card class. Just like a StackPanel a CardPanel can accept any FrameworkElement as a child.

Jarloo.Cards Example

The example code has a small application that lets you stack card objects. This example demonstrates how to use this panel in an ItemsControl in place of a StackPanel or WrapPanel.

Here is the relevant code from that example:

<ItemsControl ItemsSource="{Binding Cards}" Margin="0,0,0,-31">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Jarloo.Cards:CardPanel CardWidth="150" CardHeight="215" OffsetX="1" OffsetY="4">
                <i:Interaction.Behaviors>
                    <ei:FluidMoveBehavior AppliesTo="Children" Duration="00:00:00.25"/>
                </i:Interaction.Behaviors>
            </Jarloo.Cards:CardPanel>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
                    
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Margin="0,0,0,0" MouseUp="Border_MouseUp" Tag="{Binding}">
                <Border.ToolTip>
                    <Image Source="{Binding ImageSource}" Height="107.5" Width="75" />
                </Border.ToolTip>

                <Border.Effect>
                    <DropShadowEffect Color="#FFA1A1A1" ShadowDepth="2"/>
                </Border.Effect>

            <Image Source="{Binding ImageSource}" Height="215" Width="150"/>
            </Border>                            
        </DataTemplate>
    </ItemsControl.ItemTemplate>                	

Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>