I had originally hard-coded the grid in my Battleships program. This resulted in very repetitive code, since there were 100 cells that contained the same basic information, but I didn’t want to get too bogged down in UI programming matters until I was satisfied with the program overall.
Since I was happy with the Battleships program, but in anticipation of working on a Paint-by-Numbers program, I wanted to make sure I had an understanding of how to create the WPF cells through the C# code. Here is the XAML code (repeated 100 times, where the name is gridxy):
(7/28/21: Something happened and the code has disappeared.)
Here is the corresponding C# code (note that Ocean is the name of the 10×10 puzzle grid):
Grid aGrid = new Grid(); Rectangle aRectangle = new Rectangle(); aRectangle.Style=(Style)FindResource("BlueBorder"); aGrid.Children.Add(aRectangle); Path aPath = new Path(); aPath.Name = string.Format("cell{0}{1}", iRow, iCol); aPath.Style = (Style)FindResource("Ship"); aPath.Data = (GeometryGroup)FindResource("Empty"); aGrid.Children.Add(aPath); pthCells[iRow, iCol] = aPath; aGrid.Name = string.Format("grid{0}{1}", iRow, iCol); Grid.SetRow(aGrid, iRow); Grid.SetColumn(aGrid, iCol); aGrid.MouseLeftButtonUp += Cell_LeftClick; aGrid.MouseRightButtonUp += Cell_RightClick; Ocean.Children.Add(aGrid);
This is embedded in two for loops (iRow and iCol). It’s obvious when comparing these two snippets why the XAML is normally preferable, for several reasons. The code is shorter (even repetitive code can be cut-and-pasted fairly simply), and different variable types are treated the same way by XAML: Always the field/value structure.
An additional obstacle that I found is that the name of components created programmatically don’t appear to show up in FindName. The workaround is straightforward enough, though. By defining arrays of the appropriate type (e.g.,
Path[,] pthCells = new Path[GridSize, GridSize];
I could then refer to the appropriate array element, which was object-linked to the grid element.
Another hurdle was creating the row and column definitions with specific heights and widths (respectively). Intuitively, I thought that a single column/row definition, once created, could be reused; that isn’t true. Also, the width/height specification is not in fact a numeric value, but rather it’s of type GridLength. Hence, this was the needed code:
GridLengthConverter aGLConv = new GridLengthConverter(); GridLength aGL = (GridLength)aGLConv.ConvertFrom(34); int iRow, iCol; for (iRow = 0; iRow < GridSize; iRow++) { ColumnDefinition aColDef = new ColumnDefinition(); aColDef.Width = aGL; Ocean.ColumnDefinitions.Add(aColDef); RowDefinition aRowDef = new RowDefinition(); aRowDef.Height = aGL; Ocean.RowDefinitions.Add(aRowDef); ColumnDefinition aColClueDef = new ColumnDefinition(); aColClueDef.Width = aGL; ColClueBox.ColumnDefinitions.Add(aColClueDef); RowDefinition aRowClueDef = new RowDefinition(); aRowClueDef.Height = aGL; RowClueBox.RowDefinitions.Add(aRowClueDef); ...
A little odd, in my opinion, but that’s the way it is.