Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Table layout in WPF

Tags:

wpf

I'm very new to WPF, so I've just started making a very simple Memory card game just to learn the syntax and such. The game is where all the cards are facing down, you flip two over and if they match you remove them, otherwise reflip them down and try to remove all the cards in the shortest number of flips. Like I said, very simple... :)

My question is, is there no table element like in HTML so I can easily put the cards in a uniform layout instead of having to mess with margins?

like image 614
jb. Avatar asked Jun 05 '09 00:06

jb.


2 Answers

Here's an example with it using the UniformGrid as Matt Hamilton suggested.

First, lets create the classes and data that we will be using. Each card will be represented by a Card object, and have a Face property:

public class Card
{
    public string Face { get; set; }
    public Card() { }
}

Next, we will need a class that has our collection of Cards, and also a property that lets us set the number of cards. For the CardCollection we can use an ObservableCollection since that will automaticaly notify the UI when a Card is added or removed. The NumberOfCards property will need it's own method to notify the UI, for this we can implement the INotifyPropertyChanged interface. We'll also want a property that represents the number of Rows/Columns to use, this will just be the square root of our NumberOfCards:

public class Cards : INotifyPropertyChanged
{
    private int myNumberOfCards;
    public int NumberOfCards
    {
        get { return this.myNumberOfCards; }
        set
        {
            this.myNumberOfCards = value;
            NotifyPropertyChanged("NumberOfCards");

            // Logic is going in here since this is just an example,
            // Though I would not recomend hevily modifying the setters in a finalized app.
            while (this.myNumberOfCards > CardCollection.Count)
            {
                CardCollection.Add(new Card { Face = (CardCollection.Count + 1).ToString() });
            }
            while (this.myNumberOfCards < CardCollection.Count)
            {
                CardCollection.RemoveAt(CardCollection.Count - 1);
            }

            NotifyPropertyChanged("CardColumns");
        }
    }
    public int CardColumns
    {
        get
        {
            return (int)Math.Ceiling((Math.Sqrt((double)CardCollection.Count)));
        }
    }
    private ObservableCollection<Card> myCardCollection;
    public ObservableCollection<Card> CardCollection
    {
        get
        {
            if (this.myCardCollection == null)
            { this.myCardCollection = new ObservableCollection<Card>(); }
            return this.myCardCollection;
        }
    }
    public Cards(int initalCards)
    {
        NumberOfCards = initalCards;
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    #endregion
}


Finally, we can set this as our DataContext in the Window, and bind to our Cards class in the XAML. For the XAML I used a simple ItemsControl, so that it isn't selectable, and I set the DataTemplate to be a button, so that each card can be clicked on, that's all that is needed!

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        this.DataContext = new Cards(25);
    }
}

<Window x:Class="Sample_BoolAnimation.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1"
    Height="300"
    Width="300">
    <Grid>
        <DockPanel>
            <DockPanel DockPanel.Dock="Top">
                <TextBlock Text="Number of Cards:" />
                <TextBox Text="{Binding NumberOfCards, UpdateSourceTrigger=PropertyChanged}" />
            </DockPanel>
            <ItemsControl ItemsSource="{Binding CardCollection}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="{Binding CardColumns}" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Content="{Binding Face}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />

                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DockPanel>
    </Grid>
</Window>

Another thing that I would recomend looking at is Josh Smith's ContentControl3D implementation. As that can give you the 'flipping' behavior that you are looking for implementing in the Card class quite nicely.

like image 155
rmoore Avatar answered Sep 28 '22 05:09

rmoore


I'd recommend UniformGrid for your scenario. A quick search yielded this article which includes some code and screenshots that might help.

like image 44
Matt Hamilton Avatar answered Sep 28 '22 04:09

Matt Hamilton