views:

227

answers:

1

How can I create board for simple game, looks like for chess,but user could dynamically change number of column and rows? In cells I could insert symbol of pawn, like small image or just ellipse or rectangle with fill. This board should have possibility add and remove pawn from cells and move pown from one cell to another.

My first idea was Grid. I do it in code behind, but it's hurt to implement events or everything in runtime create board :/

        int size = 12;
        Grid board = new Grid();
        board.ShowGridLines = true;
        for (int i = 0; i < size;i++ )
        {
            board.ColumnDefinitions.Add(new ColumnDefinition());
            board.RowDefinitions.Add(new RowDefinition());
        }

        //komputer
        Rectangle ai = new  Rectangle();
        ai.Height = 20;
        ai.Width = 20;
        ai.AllowDrop = true;
        ai.Fill = Brushes.Orange;
        Grid.SetRow(ai, 0);
        Grid.SetColumn(ai,0);
        //człowiek
        Rectangle hum = new Rectangle();
        hum.Height = 20;
        hum.Width = 20;
        hum.AllowDrop = true;
        hum.Fill = Brushes.Green;
        Grid.SetRow(hum,size);
        Grid.SetColumn(hum,size);
        board.Children.Add(ai);
        board.Children.Add(hum);
        this.Content = board;
  • It's a way to do this dynamically col and row change in XAML ?
  • It's better way to implement that board create and implement events on move pawn from one cell to another ?
+2  A: 

You'll still have to use code-behind to change the RowDefinitions and ColumnDefinitions properties of the Grid in this example, since they're not dependency properties. But all the rest of the logic can be handled in the view model class.

The XAML:

<Window x:Class="GameBoard.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:GameBoard="clr-namespace:GameBoard"
        Title="Window1"
        SizeToContent="WidthAndHeight">
    <Grid Margin="50">
        <Grid.Resources>

            <!-- This template presents the Piece object.  Note that you can't set
                 the Grid.Row and Grid.Column properties on this Rectangle - well,
                 you *can*, but the Grid won't see them.  See the Style below.  -->
            <DataTemplate DataType="{x:Type GameBoard:Piece}">
                <Rectangle Fill="{Binding Fill}"
                           Width="50"
                           Height="50" />
            </DataTemplate>

            <!-- When the ItemsControl creates its items, it wraps each item in a
                 ContentPresenter.  You have to set Grid.Row and Grid.Column
                 on this ContentPresenter in order for the Grid to see them. -->
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="Grid.Row"
                        Value="{Binding Row}" />
                <Setter Property="Grid.Column"
                        Value="{Binding Column}" />
            </Style>

        </Grid.Resources>
        <Border BorderBrush="Black"
                BorderThickness="1">
            <ItemsControl x:Name="Board"
                          ItemsSource="{Binding}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="50" />
                                <RowDefinition Height="50" />
                                <RowDefinition Height="50" />
                                <RowDefinition Height="50" />
                                <RowDefinition Height="50" />
                                <RowDefinition Height="50" />
                                <RowDefinition Height="50" />
                                <RowDefinition Height="50" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="50" />
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </Border>
    </Grid>
</Window>

The Piece class - obviously you'll need to implement INotifyPropertyChanged on the Row and Column properties to handle moving pieces about.

public class Piece
{
    public int Column { get; set; }
    public Brush Fill { get; set; }
    public int Row { get; set; }
}

Populating the board:

    public Window1()
    {
        InitializeComponent();

        ObservableCollection<Piece> pieces = new ObservableCollection<Piece>();
        pieces.Add(
            new Piece {Row = 0, Column = 0, Fill = new SolidColorBrush(Colors.BlanchedAlmond)});
        pieces.Add(
            new Piece {Row = 7, Column = 7, Fill = new SolidColorBrush(Colors.RosyBrown)});
        pieces.Add(
            new Piece { Row = 3, Column = 4, Fill = new SolidColorBrush(Colors.BlueViolet) });
        pieces.Add(
            new Piece { Row = 5, Column = 4, Fill = new SolidColorBrush(Colors.Orange) });

        Board.DataContext = pieces;
    }

I've used an ItemsControl to contain the pieces in this example. You could use a ListBox instead - that's kind of nice because it gives you item selection for free. Note that if you do this you'll have to change the Style's TargetType to ListBoxItem, since that's what the ListBox wraps its item elements in instead of ContentPresenter.

Edit:

I wrote this answer quite a while ago, and it's got a problem.

Assigning the Grid.Row and Grid.Column properties using a style that's applied to the item container generated by the grid is right. Figuring out that the item container is a ContentPresenter and creating a default style for that type is not. (It'll work reliably in this case, but there are lots of cases where it won't.)

You should still create a style, but it should be assigned to the ItemsControl's ItemContainerStyle. This style's automatically applied to whatever container element the control generates for its items - so if the ItemsControl you're using is a ListBox, it will apply it to the ListBoxItem, and if it's a TabControl, it'll get applied to the TabItem, and so on.

Robert Rossney
For now I decide const number of column and row declared in XAML. I implement INotifyPropertyChanged and I almost understand Your idea... but how to implement moving Piece? I think that I click it by mouse and next select other cell and it should change place(interface implementation help me change row and column then). How find that selected place in board is specified cell?
netmajor
There are a lot of ways you could do it. Probably the simplest: create a Cell class that's just like the Piece class, create one for each cell, and add them to the `ItemsSource` before you add the Piece objects. The pieces appear later in the Z-order than the cells, so they'll display (and be selected by mouse clicks). If you're using a ListBox, the SelectedItem will (if it's not null) always be either a Piece or a Cell.
Robert Rossney
OK,I see what you mean, It's like toggle between empty cell class or Piece. But for me it's important to know actual position of pawn toward new position, cause if it's close- pawn will multiple, other will just move. So how to get from mouse down event position(column,row) new pawn move?...
netmajor
How do you *want* it to work? Do you want the user to click and drag? Do you want to click on a piece and then click on its destination?
Robert Rossney
Yes, like that. Maybe real drag will hurt to animate, but just use ZIndex to show and hide Piece or empty cell. Most important is to calculate distance between actual and new. If it's 1- Piece will multiple, if 2 - actual Piece will hide and new will show :)
netmajor
who are x:Type GameBoard:Piece? and {x:Type ContentPresenter}?how they exist in the context ?
yoav.str
The `Piece` class is defined in the example. The `ContentPresenter` class is pretty well documented on MSDN - though I'm going to edit this answer because there's a better way of doing this referencing the item container directly.
Robert Rossney