patterncsharpMinor
Simple Tic-Tac-Toe Application in WPF
Viewed 0 times
simpleapplicationtoewpftictac
Problem
I made a Tic-Tac-Toe game in WPF and was just hoping for some input as I am pretty new to WPF and I am still a student so I'm sure that my C# code in general may be lacking
XAML:
C# File:
```
namespace TicTacToc
{
public partial class MainWindow : Window
{
public GameBoard MyGameBoard = new GameBoard();
public MainWindow()
{
InitializeComponent();
this.DataContext = MyGameBoard;
}
public void Button_Click(object sender, RoutedEventArgs e)
{//Updates UI and calls gameBoard update
var clickedButton = sender as Button;
if(MyGameBoard.currentPlayer == GameBoard.CurrentPlayer.X)
{
clickedButton.Foreground = (SolidColorBrush)(new BrushConverter().ConvertFrom("#811717"));
}
else if (MyGameBoard.currentPlayer == GameBoard.CurrentPlayer.O)
{
clickedButton.Foreground = (SolidColorBrush)(new BrushConverter().ConvertFrom("#126712"));
}
clickedButton.Background = Brushes.WhiteSmoke;
clickedButton.Content = MyGameBoard.currentPlayer;
clickedButton.IsHitTestVisible = false;
MyGameBoard.UpdateBoard(clickedButton.Name);
}
private void Restart_Click(object sender, RoutedEventArgs
XAML:
C# File:
```
namespace TicTacToc
{
public partial class MainWindow : Window
{
public GameBoard MyGameBoard = new GameBoard();
public MainWindow()
{
InitializeComponent();
this.DataContext = MyGameBoard;
}
public void Button_Click(object sender, RoutedEventArgs e)
{//Updates UI and calls gameBoard update
var clickedButton = sender as Button;
if(MyGameBoard.currentPlayer == GameBoard.CurrentPlayer.X)
{
clickedButton.Foreground = (SolidColorBrush)(new BrushConverter().ConvertFrom("#811717"));
}
else if (MyGameBoard.currentPlayer == GameBoard.CurrentPlayer.O)
{
clickedButton.Foreground = (SolidColorBrush)(new BrushConverter().ConvertFrom("#126712"));
}
clickedButton.Background = Brushes.WhiteSmoke;
clickedButton.Content = MyGameBoard.currentPlayer;
clickedButton.IsHitTestVisible = false;
MyGameBoard.UpdateBoard(clickedButton.Name);
}
private void Restart_Click(object sender, RoutedEventArgs
Solution
First, your grids aren't based on chunks of 100:
Rather than using "33", those should just be "1". If you wanted the middle one twice as wide as the rows/columns on either side, then you would give that one "2*", and so on.
Now for an MVVM lesson. You almost never want to have code in your code-behind for the view. What you should do is set up a ViewModel.
So you have your view
Then your code-behind
And your VM
See these files for the necessary files to set up the Command structure: https://github.com/Hosch250/CheckersUI/tree/master/CheckersUI/Commands
Now, the easy part is wiring this up to the UI; the tricky part might be understanding the wiring.
So first, we need to let the UI know where to look for the data. We set the
And now we can use our VM in the view:
Now, clicking this button calls the
And displaying a value:
You can do bindings on colors, and more. Converters are also useful:
Here is an example implementation of a converter:
I hope this makes sense; I know it can appear somewhat convoluted, but it really will make your code cleaner and easier to maintain. Basically, all your logic and display data goes in the VM, and the view is for pure presentation. Because the code-behind is part of the view, it should also only be involved in presentation details that cannot be expressed in the XAML, like fancy drag/drop details. Feel free to ping me if you have any questions.
Rather than using "33", those should just be "1". If you wanted the middle one twice as wide as the rows/columns on either side, then you would give that one "2*", and so on.
Now for an MVVM lesson. You almost never want to have code in your code-behind for the view. What you should do is set up a ViewModel.
So you have your view
MainWindow.xaml:Then your code-behind
MainWindow.xaml.cs:/* don't touch this file */And your VM
MainWindowViewModel.cs:public class MainWindowViewModel : INotifyPropertyChanged
{
// store all your data in properties here; fire the `PropertyChanged` event in the setter
// example property
private int _foo;
public int Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged();
}
}
// implement a `RelayCommand` or `DelegateCommand` class off the `ICommand` interface; then use instances of it to create commands
// these commands will bind to the `Command` element of `Button`s, etc.
// example command: assign this in the ctor of your VM
public DelegateCommand FooCommand { get; }
}See these files for the necessary files to set up the Command structure: https://github.com/Hosch250/CheckersUI/tree/master/CheckersUI/Commands
Now, the easy part is wiring this up to the UI; the tricky part might be understanding the wiring.
So first, we need to let the UI know where to look for the data. We set the
DataContext:var mainWindow = new MainWindow();
var mainWindowVM = new MainWindowViewModel();
mainWindow.DataContext = mainWindowVM;And now we can use our VM in the view:
Now, clicking this button calls the
FooCommand command in your VM. (If you have one additional piece of data to pass to the command, you can use the CommandParameter binding.)And displaying a value:
You can do bindings on colors, and more. Converters are also useful:
Here is an example implementation of a converter:
public class FooConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
(int) value == 0 ? Colors.Blue : Colors.Black;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
throw new NotImplementedException(); // this converter is just for the display--this method is never called
}I hope this makes sense; I know it can appear somewhat convoluted, but it really will make your code cleaner and easier to maintain. Basically, all your logic and display data goes in the VM, and the view is for pure presentation. Because the code-behind is part of the view, it should also only be involved in presentation details that cannot be expressed in the XAML, like fancy drag/drop details. Feel free to ping me if you have any questions.
Code Snippets
<Grid.RowDefinitions>
<RowDefinition Height="33*" />
<RowDefinition Height="33*" />
<RowDefinition Height="33*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="33*" />
<ColumnDefinition Width="33*" />
<ColumnDefinition Width="33*" />
</Grid.ColumnDefinitions><!-- your XAML goes here -->/* don't touch this file */public class MainWindowViewModel : INotifyPropertyChanged
{
// store all your data in properties here; fire the `PropertyChanged` event in the setter
// example property
private int _foo;
public int Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged();
}
}
// implement a `RelayCommand` or `DelegateCommand` class off the `ICommand` interface; then use instances of it to create commands
// these commands will bind to the `Command` element of `Button`s, etc.
// example command: assign this in the ctor of your VM
public DelegateCommand FooCommand { get; }
}var mainWindow = new MainWindow();
var mainWindowVM = new MainWindowViewModel();
mainWindow.DataContext = mainWindowVM;Context
StackExchange Code Review Q#157609, answer score: 3
Revisions (0)
No revisions yet.