WPF: Placing game pieces on board

Viewed 63

I'm currently creating a board game in WPF. I'm creating my board in my PlayerViewModel so I have a nice 10x10 board. I also create pieces that the player starts with (4). Here is where I get stuck, because I'm not quite sure if it's possible to just put the player's pieces in my GamePiece.xaml the way I've done it since this is the "component" that creates the whole board for me. The circle/image is the component I want as a player's disc.

I bind this into my GameView.xaml, however the problem is that I want to have circles as my pieces in the game. Obviously here I'm just creating the whole board and so it's also creating the pieces (circles/photos) and I can't seem to manipulate this and decide how many I want to show on the board in the beginning. I have tried different ways, like put the pieces on specific coordinates and just having the color of the square it's taking up on the board change, but it doesn't look very nice.

1 Answers

First, you would create a ListView and configure it to place the items in rows (by using WrapPanel as ItemsPanel and setting the width and height for the ListView), and -as you mentioned- you can use BoardPiece UserControl as ItemTemplate.

Your GameView.xaml can be something similar to this

<UserControl
    x:Class="GameView">
    
    <StackPanel Orientation="Vertical">

        <ListView
            Width="600"
            Height="500"
            ItemsSource="{Binding BoardPieces}"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">

            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>

            <ListView.ItemTemplate>
                <DataTemplate>
                    <testingThings:BoardPiece Margin="0" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <Button
            Width="100"
            Margin="8"
            Click="ButtonBase_OnClick"
            Content="Fill All" />
    </StackPanel>
    
</UserControl>

Then, Configure the ViewModel: create your items and store them in an ObservableCollection to bind them later with the ListView..

public class GameViewViewModel
{
    public ObservableCollection<BoardPieceItem> BoardPieces { set; get; } = 
            new ObservableCollection<BoardPieceItem>();

    public GameViewViewModel()
    {
        for (var i = 0; i < 10; i++)
        for (var j = 0; j < 10; j++)
        {
            // random coloring at initialization, do it as you want..
            BoardPieces.Add(new BoardPieceItem
            {
                Index = (i, j),
                RectangleColor = "#00FF00", // green
                EllipseColor = (i + j) % 2 == 0
                    ? "#00FFFFFF" // transparent
                    : "#000000", // Black
            });
        }
    }
}

Now, Bind the ViewModel with the View

public partial class GameView
{
    public GameView()
    {
        InitializeComponent();
        DataContext = new GameViewViewModel();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        if (sender is Button { DataContext: TestDialogVm vm })
        {
            foreach (var item in vm.BoardPieces)
            {
                item.EllipseColor = "#0000FF";
            }
        }
    }
}

In BoardPiece.xaml there is no need to use Converters

<UserControl
    x:Class="SharedModule.Views.TestLab.BoardPiece"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:testLab="clr-namespace:SharedModule.Views.TestLab"
    d:DataContext="{d:DesignInstance testLab:BoardPieceItem}"
    MouseDown="UIElement_OnMouseDown"
    mc:Ignorable="d">
    <Grid>
        <Rectangle
            Width="45"
            Height="45"
            Fill="{Binding RectangleColor}"
            Stroke="Black"
            StrokeThickness="1.3" />
        <Ellipse
            Width="20"
            Height="20"
            Fill="{Binding EllipseColor}" />
    </Grid>
</UserControl>

Finally, Configure the BoardPieceItem to make it possible to update the game board at runtime..

public class BoardPieceItem : INotifyPropertyChanged
{
    public (int, int) Index { get; set; }
    private string _rectangleColor;

    public string RectangleColor
    {
        get => _rectangleColor;
        set
        {
            _rectangleColor = value;
            OnPropertyChanged();
        }
    }

    private string _ellipseColor;

    public string EllipseColor
    {
        get => _ellipseColor;
        set
        {
            _ellipseColor = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Now you are all set, see how UIElement_OnMouseDown will update the item's color, and ButtonBase_OnClick will update the cells of the board at all (I could go all the way MVVM, but used some events in a hurry).


This is how the game looks like at my side

enter image description here

Related