Moin5262
Moin5262

Reputation: 27

Making a NxN tic tac toe GUI wpf c#

I am making a NxN tic tac toe game in WPF c#. I want user to enter Rows and Column count(NxN), and my code will generate those number of columns and rows. I can't figure it out, how will I produce that number of rows and columns dynamically. I have posted my XAML code, is there a way that I can loop my XAML code? thanks

<Grid x:Name="Container">
    <!-- First here i want to make N columns-->
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <!-- Here i want to make N rows-->
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!-- Then here i want to add that number of buttons in N x N columns and rows -->
    <Button x:Name="Button0_0" Grid.Row="0" Grid.Column="0" Click="Button_Click"/>
    <Button x:Name="Button0_1" Grid.Row="0" Grid.Column="1" Click="Button_Click" />
    <Button x:Name="Button0_2" Grid.Row="0" Grid.Column="2" Click="Button_Click"/>

    <Button x:Name="Button1_0" Grid.Row="1" Grid.Column="0" Click="Button_Click"/>
    <Button x:Name="Button1_1" Grid.Row="1" Grid.Column="1" Click="Button_Click"/>
    <Button x:Name="Button1_2" Grid.Row="1" Grid.Column="2" Click="Button_Click"/>

    <Button x:Name="Button2_0" Grid.Row="2" Grid.Column="0" Click="Button_Click"/>
    <Button x:Name="Button2_1" Grid.Row="2" Grid.Column="1" Click="Button_Click"/>
    <Button x:Name="Button2_2" Grid.Row="2" Grid.Column="2" Click="Button_Click"/>                                                  
</Grid>

Upvotes: 2

Views: 2149

Answers (3)

ASh
ASh

Reputation: 35680

ItemsControl + UniformGrid as a Panel is a good choice to display a rectangular game board. I have already posted similar solution (How to create and use matrix of (color) boxes C# WPF) but it is missing Rows and Columns binding. Here is a more elaborated example for TicTacToe.

Let's create view model classes:

public class BoardCell : INotifyPropertyChanged
{
    private string _sign;
    private bool _canSelect = true;

    public string Sign
    {
        get { return _sign; }
        set
        {
            _sign = value;
            if (value != null)
                CanSelect = false;
            OnPropertyChanged();
        }
    }

    public bool CanSelect
    {
        get { return _canSelect; }
        set
        {
            _canSelect = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class Board
{
    public int Rows { get; set; }
    public int Columns { get; set; }

    private ObservableCollection<BoardCell> _cells;
    public ObservableCollection<BoardCell> Cells
    {
        get
        {
            if (_cells == null)
                _cells = new ObservableCollection<BoardCell>(Enumerable.Range(0, Rows*Columns).Select(i => new BoardCell()));
            return _cells;
        }
    } 
}

and then create a view:

<Grid x:Name="Container">
    <Grid.DataContext>
        <wpfDemos:Board Rows="8" Columns="8"/>
    </Grid.DataContext>
    <ItemsControl x:Name="Board" ItemsSource="{Binding Path=Cells}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate >
                <UniformGrid Rows="{Binding Path=Rows}" Columns="{Binding Path=Columns}"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding Path=Sign}" 
                        IsEnabled="{Binding Path=CanSelect}"
                        Click="CellClick"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

in the code-behind we have one method to allow 2 players make thier moves in turn:

private bool _firstPlayer = true;
private void CellClick(object sender, RoutedEventArgs e)
{
    var cell = (sender as Button).DataContext as BoardCell;
    cell.Sign = _firstPlayer ? "X" : "O";
    _firstPlayer = !_firstPlayer;
    // TODO check winner 
}

to follow MVVM pattern this method should be wrapped into a command (ICommand), moved to Board class and attached to view via Button.Command binding.

board after some turns

summary: separate logic and presentation. work with data. let controls generate their content based on bindings and provided templates.

Upvotes: 3

johnDisplayClass
johnDisplayClass

Reputation: 269

As Zohar has mentioned you could use the code behind to add child GridColumn's to the Grid.ColumnDefinitions, and then add Button's to the Container Grid. Its an easy approach, but it means using code behind which causes many frowns.

Personally I would rather create a Behaviour and attach it to the Grid. Have your behaviour bind to an integer of Rows and Columns on your viewmodel, and then add ColumnDefinitions and RowDefinitions to the AssociatedObject. Behaviours are a great way to encapsulate reusable features, and eliminates the need for writing code behind.

This chap demonstrates how to create behaviours that bind to your model, and also how to attach them to an element in the view.

http://julmar.com/blog/programming/playing-with-wpf-behaviors-a-watermarktext-behavior/

Upvotes: 0

Rachel
Rachel

Reputation: 132558

I have code for some AttachedProperties on my blog that does exactly this.

XAML code ends up looking like this :

<Grid local:GridHelpers.RowCount="{Binding RowCount}"
      local:GridHelpers.ColumnCount="{Binding ColumnCount}" />

Here's the code for the Attached properties, in case the blog post link ever goes down. It also includes some properties to specify your Star rows/columns.

public class GridHelpers
{
    #region RowCount Property

    /// <summary>
    /// Adds the specified number of Rows to RowDefinitions. 
    /// Default Height is Auto
    /// </summary>
    public static readonly DependencyProperty RowCountProperty =
        DependencyProperty.RegisterAttached(
            "RowCount", typeof(int), typeof(GridHelpers),
            new PropertyMetadata(-1, RowCountChanged));

    // Get
    public static int GetRowCount(DependencyObject obj)
    {
        return (int)obj.GetValue(RowCountProperty);
    }

    // Set
    public static void SetRowCount(DependencyObject obj, int value)
    {
        obj.SetValue(RowCountProperty, value);
    }

    // Change Event - Adds the Rows
    public static void RowCountChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || (int)e.NewValue < 0)
            return;

        Grid grid = (Grid)obj;
        grid.RowDefinitions.Clear();

        for (int i = 0; i < (int)e.NewValue; i++)
            grid.RowDefinitions.Add(
                new RowDefinition() { Height = GridLength.Auto });

        SetStarRows(grid);
    }

    #endregion

    #region ColumnCount Property

    /// <summary>
    /// Adds the specified number of Columns to ColumnDefinitions. 
    /// Default Width is Auto
    /// </summary>
    public static readonly DependencyProperty ColumnCountProperty =
        DependencyProperty.RegisterAttached(
            "ColumnCount", typeof(int), typeof(GridHelpers),
            new PropertyMetadata(-1, ColumnCountChanged));

    // Get
    public static int GetColumnCount(DependencyObject obj)
    {
        return (int)obj.GetValue(ColumnCountProperty);
    }

    // Set
    public static void SetColumnCount(DependencyObject obj, int value)
    {
        obj.SetValue(ColumnCountProperty, value);
    }

    // Change Event - Add the Columns
    public static void ColumnCountChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || (int)e.NewValue < 0)
            return;

        Grid grid = (Grid)obj;
        grid.ColumnDefinitions.Clear();

        for (int i = 0; i < (int)e.NewValue; i++)
            grid.ColumnDefinitions.Add(
                new ColumnDefinition() { Width = GridLength.Auto });

        SetStarColumns(grid);
    }

    #endregion

    #region StarRows Property

    /// <summary>
    /// Makes the specified Row's Height equal to Star. 
    /// Can set on multiple Rows
    /// </summary>
    public static readonly DependencyProperty StarRowsProperty =
        DependencyProperty.RegisterAttached(
            "StarRows", typeof(string), typeof(GridHelpers),
            new PropertyMetadata(string.Empty, StarRowsChanged));

    // Get
    public static string GetStarRows(DependencyObject obj)
    {
        return (string)obj.GetValue(StarRowsProperty);
    }

    // Set
    public static void SetStarRows(DependencyObject obj, string value)
    {
        obj.SetValue(StarRowsProperty, value);
    }

    // Change Event - Makes specified Row's Height equal to Star
    public static void StarRowsChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
            return;

        SetStarRows((Grid)obj);
    }

    #endregion

    #region StarColumns Property

    /// <summary>
    /// Makes the specified Column's Width equal to Star. 
    /// Can set on multiple Columns
    /// </summary>
    public static readonly DependencyProperty StarColumnsProperty =
        DependencyProperty.RegisterAttached(
            "StarColumns", typeof(string), typeof(GridHelpers),
            new PropertyMetadata(string.Empty, StarColumnsChanged));

    // Get
    public static string GetStarColumns(DependencyObject obj)
    {
        return (string)obj.GetValue(StarColumnsProperty);
    }

    // Set
    public static void SetStarColumns(DependencyObject obj, string value)
    {
        obj.SetValue(StarColumnsProperty, value);
    }

    // Change Event - Makes specified Column's Width equal to Star
    public static void StarColumnsChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
            return;

        SetStarColumns((Grid)obj);
    }

    #endregion

    private static void SetStarColumns(Grid grid)
    {
        string[] starColumns = 
            GetStarColumns(grid).Split(',');

        for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
        {
            if (starColumns.Contains(i.ToString()))
                grid.ColumnDefinitions[i].Width = 
                    new GridLength(1, GridUnitType.Star);
        }
    }

    private static void SetStarRows(Grid grid)
    {
        string[] starRows = 
            GetStarRows(grid).Split(',');

        for (int i = 0; i < grid.RowDefinitions.Count; i++)
        {
            if (starRows.Contains(i.ToString()))
                grid.RowDefinitions[i].Height = 
                    new GridLength(1, GridUnitType.Star);
        }
    }
}

Upvotes: 0

Related Questions