Search code examples
c#xamllistviewuwplistviewitem

Using StyleSelector in UWP to Change ListViewItem Styles for Minesweeper Game


I am making a minesweeper clone for class. The basic game board is generated through code, but it is a series of ListView columns containing rows of Tile objects. This code generates the ListViews (it works fine):

  // Create as many lists as necessary to fill the board according to the size specified
  void createGameBoard(bool erase = true)
  {
     // Erase any previous game
     if(erase)
     {
        for(int i = mainGameBoard.Children.Count; i > 0; i--)
        {
           mainGameBoard.Children.RemoveAt(i - 1);
        }
     }

     //        Main Game Board
     // column 0   column 1   column 2
     // tile 0,0   tile 1,0   tile 2,0
     // tile 0,1   tile 1,1   tile 2,1
     // tile 0,2   tile 1,2   tile 2,2
     // Create a new list view for each game board column
     for(uint col = 0; col < minesweepGame.Width; col++)
     {
        ListView column = new ListView();
        column.Name = col.ToString();
        column.SelectionMode = ListViewSelectionMode.None;
        column.IsItemClickEnabled = true;
        column.ItemClick += Column_ItemClick;
        ///column.ItemContainerStyleSelector.SelectStyle()

        // Fill the column with tiles
        Tile[] columnTiles = new Tile[minesweepGame.Width];
        for(uint row = 0; row < minesweepGame.Width; row++)
        {
           columnTiles[row] = minesweepGame.TileList[col, row];
        }
        column.ItemsSource = columnTiles;

        // Append the newly created column to the main game board (in order of creation)
        mainGameBoard.Children.Add(column);
     }
  }

This results in a clean grid with animations.

UWP Minesweeper using ListViews

I want to style items differently when they are revealed to the user (once it is clicked, a tile becomes darker and has numbers with different colours). So I looked at the documentation and examples, and created a StyleSelector:

   public class ColumnTileSelector : StyleSelector
   {
      private Style Tile0 = new Style(typeof(ListViewItem));
      private Style Tile1 = new Style(typeof(ListViewItem));
      private Style Tile2 = new Style(typeof(ListViewItem));
      private Style Tile3 = new Style(typeof(ListViewItem));
      private Style Tile4 = new Style(typeof(ListViewItem));

      protected override Style SelectStyleCore(object item, DependencyObject container)
      {
         Tile tile = (Tile)item;
         if(tile.IsShownGraphically)
         {
            switch(tile.AdjacentMines)
            {
               case 0:
                  Tile0.Setters.Add(new Setter(Control.BackgroundProperty, Colors.Red));
                  return Tile0;
               case 1:
                  return Tile1;
               case 2:
                  return Tile2;
               case 3:
                  return Tile3;
               case 4:
                  return Tile4;
               default:
                  return Tile0;
            }
         }
         else
         {
            return Tile0;
         }
      }
   }

I also had previously created styles in the XAML:

   <!-- style the game grid tiles -->
   <Page.Resources>
      <Style TargetType="ListViewItem">
         <Setter Property="MinWidth" Value="30"/>
         <Setter Property="MinHeight" Value="30"/>
         <Setter Property="MaxWidth" Value="30"/>
         <Setter Property="MaxHeight" Value="30"/>
         <Setter Property="Padding" Value="2, 2, 2, 2"/>
         <Setter Property="BorderBrush" Value="SlateGray"/>
         <Setter Property="BorderThickness" Value="1"/>
         <Setter Property="HorizontalContentAlignment" Value="Center"/>
         <Setter Property="FontWeight" Value="ExtraBlack"/>
         <Setter Property="Foreground" Value="DarkBlue"/>
      </Style>

      <Style TargetType="ListViewItem" x:Key="Tile0">
         <Setter Property="Background" Value="LightGray"/>
         <Setter Property="Foreground" Value="Transparent"/>
      </Style>

      <Style TargetType="ListViewItem" x:Key="Tile1">
         <Setter Property="Background" Value="LightGray"/>
         <Setter Property="Foreground" Value="DarkBlue"/>
      </Style>

      <Style TargetType="ListViewItem" x:Key="Tile2">
         <Setter Property="Background" Value="LightGray"/>
         <Setter Property="Foreground" Value="Green"/>
      </Style>

      <Style TargetType="ListViewItem" x:Key="Tile3">
         <Setter Property="Background" Value="LightGray"/>
         <Setter Property="Foreground" Value="Red"/>
      </Style>

      <Style TargetType="ListViewItem" x:Key="Tile4">
         <Setter Property="Background" Value="LightGray"/>
         <Setter Property="Foreground" Value="DarkOrange"/>
      </Style>

      <Style TargetType="StackPanel" x:Key="mainGameBoard">
         <Setter Property="BorderBrush" Value="SlateGray"/>
         <Setter Property="BorderThickness" Value="0"/>
      </Style>

      <local:ColumnTileSelector x:Key="tileColor"/>
   </Page.Resources>

However, nothing actually works.

  1. How do I refer to styles from the XAML in the view's code?
  2. More importantly, how do I specify that my ListViews should use the ColumnTileSelector?
  3. In the code that generates the ListViews, I commented out ///column.ItemContainerStyleSelector.SelectStyle(). Is it possible to specify my ColumnTileSelector class here? I'm confused.

The documentation is not clear on how things are connected together. I understand in principle, but none of the examples I see specify why things react as they do.


Solution

  • <Page>
        <Page.Resources>
             <Style TargetType="ListViewItem" x:Key="Tile1">
                  <Setter Property="Background" Value="LightGray"/>
                  <Setter Property="Foreground" Value="DarkBlue"/>
             </Style>
             <support:ColumnStyleSelector x:Key="ColumnStyleSelector"    
                  Tile1="{StaticResource Tile1}"/>
        </Page.Resources>
       <Grid>
        <ListView ItemContainerStyleSelector="{StaticResource ColumnStyleSelector}"/>
    </Page>
    
    1. Make the styles Tile1, Tile2, etc public properties of the ColumnStyleSelector
    2. Put the Styles in the page Resources
    3. Put the ColumnStyleSelector in the page Resources
    4. Reference the Styles in the Resources in the ColumnStyleSelector attributes