2009-06-05 19 views
7

Jestem nowym użytkownikiem WPF, dlatego właśnie zacząłem tworzyć bardzo prostą grę w karty pamięci, aby poznać jej składnię i tym podobne. Gra polega na tym, że wszystkie karty są skierowane w dół, odwracasz dwa, a jeśli pasują, usuń je, w przeciwnym razie odrzuć je i spróbuj usunąć wszystkie karty w jak najkrótszym czasie. Jak już mówiłem, bardzo proste ... :)Układ tabeli w WPF

Moje pytanie brzmi, czy nie ma elementu tabeli, jak w HTML, więc mogę łatwo umieścić karty w jednolitym układzie zamiast marnować marginesy?

Odpowiedz

10

Oto przykład z użyciem UniformGrid, jak sugerował Matt Hamilton.

Po pierwsze, pozwala utworzyć klasy i dane, których będziemy używać. Każda karta będzie reprezentowana przez obiekt kartę i mają właściwość czołowa:

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

Dalej, będziemy potrzebować klasę, która ma naszą kolekcję kart, a także właściwość, która pozwala nam określić liczbę kart . W przypadku CardCollection możemy użyć numeru ObservableCollection, ponieważ spowoduje to automatyczne powiadomienie interfejsu użytkownika po dodaniu lub usunięciu karty. Właściwość NumberOfCards będzie potrzebować własnej metody powiadamiania interfejsu użytkownika, w tym celu możemy implement interfejs INotifyPropertyChanged. Będziemy także chcą właściwość, która reprezentuje liczbę wierszy/kolumn do wykorzystania, to będzie po prostu pierwiastek kwadratowy z naszych 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 
} 


Wreszcie możemy ustawić to jako naszych DataContext w Oknie i związać się z naszą klasą kart w XAML. Dla XAML użyłem prostego ItemsControl, więc nie można go wybrać i ustawiam DataTemplate jako przycisk, tak aby każda karta mogła zostać kliknięta, to wszystko, co jest potrzebne!

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> 

Inną rzeczą, którą chciałbym polecić patrząc na to ContentControl3D realizacja Josh Smith. Może to dać całkiem niezłe "przerzucanie", którego szukasz w klasie.

+0

Bardzo ładne! +1 ... mam nadzieję, że zostanie to "zaakceptowane"! –

+0

o to jest niesamowite, dziękuję bardzo! –

2

Dla swojego scenariusza polecam UniformGrid. Szybkie wyszukiwanie przyniosło this article, który zawiera kod i zrzuty ekranu, które mogą pomóc.

+0

Najlepsza część UniformGrid jest w stanie ustawić ItemsPanel ItemsControl, aby z niego korzystać. Następnie karty mogą być wiązane przez ItemSorce i nie musisz deklarować każdej karty w XAML. – rmoore

+0

Chciałbym zapytać użytkownika, ile kart chce grać, a następnie wygenerować je zamiast posiadania stałej liczby kart w XAML. czy masz artykuł dotyczący ItemsControl/ItemSource? –

+0

Nie mogę mówić za @rmoore, ale wiem, że on mówi o użyciu UniformGrid jako ItemsPanel dla ListBox. Oto artykuł, który robi coś podobnego z WrapPanel: http://compilewith.net/2008/03/wpf-listbox-itemspaneltemplate-and.html –

0

Istnieje tabela w WPF, oto dobry article na rozpoczęcie pracy z nim. Z doświadczenia wynika, że ​​tabela w WPF nie jest łatwa w użyciu, a użycie siatki jest ogólnie lepszym rozwiązaniem.

+1

Obiekt Table jest używany w TextBlocks i FlowDocuments, a nie w bardzo wielu elementach interfejsu użytkownika. – YotaXP