tags:

views:

1061

answers:

3
+2  Q: 

WPF Table?

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?

+2  A: 

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

Matt Hamilton
The best part of a UniformGrid is being able to set a ItemsControl's ItemsPanel to use it. Then the cards can be bound through the ItemsSorce and you don't have to declare each card in XAML.
rmoore
I was going to ask the user how many cards they wanted to play with and then generate them instead of having a static number of cards in the XAML. do you have an article for ItemsControl/ItemSource?
jb
I can't speak for @rmoore, but I gather he's talking about using a UniformGrid as the ItemsPanel for a ListBox. Here's an article that does something similar with WrapPanel: http://compilewith.net/2008/03/wpf-listbox-itemspaneltemplate-and.html
Matt Hamilton
Exactly right Matt, I I whipped up a quick demo to better explain what I was talking about.
rmoore
A: 

There is a Table in WPF, here's a good article on getting started with it. From experience the Table in WPF is not that easy to use and using a Grid is generally a better option.

sipwiz
The Table object is used in TextBlocks and FlowDocuments, and not so much UI elements.
YotaXP
+5  A: 

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.

rmoore
Very nice! +1 ... hope this gets "accepted"!
Matt Hamilton
oh this is awesome, thank you so much!
jb