Change width of ItemsControl cells on button click

111 Views Asked by At

I am trying to create a table whose cells are dynamic in width and height. Following is my xaml code:

 <ItemsControl Grid.Row="1" Grid.Column="1" DataContext="{Binding GameBoardApp}" ItemsSource="{Binding BlockList}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel></WrapPanel>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Grid.Row" Value="{Binding Row}"/>
                    <Setter Property="Grid.Column" Value="{Binding Col}"/>
                    <Setter Property="Grid.Width" Value="{Binding Width, Mode=TwoWay}" />
                    <Setter Property="Grid.Height" Value="{Binding Height, Mode=TwoWay}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderThickness="1" BorderBrush="Black">
                        <Grid>
                            <Rectangle Fill="{Binding Background}" />
                            <TextBlock Text="{Binding Name}"/>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

I am able to design the UI and see the content but I am not able to set up 2-way data binding to change the width of the cell. Following is my Code Behind:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            GameBoardApp = new GameBoard(50);
            DataContext = this;
        }
        public GameBoard GameBoardApp { get; set; }

        private void IncreaseSize_Click(object sender, RoutedEventArgs e)
        {
            GameBoardApp = new GameBoard(GameBoardApp.CellWidth + 1);
            Debug.Print(GameBoardApp.CellWidth.ToString());
        }

        private void DecreaseSize_Click(object sender, RoutedEventArgs e)
        {
            GameBoardApp = new GameBoard(GameBoardApp.CellWidth-1);
            Debug.Print(GameBoardApp.CellWidth.ToString());
        }
    }

public class GameBoard : INotifyPropertyChanged
{
    public GameBoard(int cellWidthVal)
    {
        _cellWidth = cellWidthVal;
        var m1 = new Block() { Name = "b1", Background = Brushes.Red, Col = 50, Row = 50, Width = CellWidth };
        var m2 = new Block() { Name = "b2", Background = Brushes.Gray, Col = 50, Row = 50, Width = CellWidth };
        var m3 = new Block() { Name = "b3", Background = Brushes.Goldenrod, Col = 0, Row = 0, Width = CellWidth };
        var m4 = new Block() { Name = "b4", Background = Brushes.Honeydew, Col = 0, Row = 0, Width = CellWidth };
        var m5 = new Block() { Name = "b5", Background = Brushes.Bisque, Col = 70, Row = 70, Width = CellWidth };
        var m6 = new Block() { Name = "b6", Background = Brushes.Honeydew, Col = 0, Row = 0, Width = CellWidth };
        var m7 = new Block() { Name = "b7", Background = Brushes.Honeydew, Col = 0, Row = 0, Width = CellWidth };
        var m8 = new Block() { Name = "b1", Background = Brushes.Red, Col = 0, Row = 0, Width = CellWidth };
        var m9 = new Block() { Name = "b2", Background = Brushes.Gray, Col = 0, Row = 0, Width = CellWidth };
        var m10 = new Block() { Name = "b3", Background = Brushes.Goldenrod, Col = 0, Row = 0, Width = CellWidth };
        var m11 = new Block() { Name = "b4", Background = Brushes.Honeydew, Col = 0, Row = 0, Width = CellWidth };
        var m12 = new Block() { Name = "b5", Background = Brushes.Honeydew, Col = 0, Row = 0, Width = CellWidth };
        var m13 = new Block() { Name = "b6", Background = Brushes.Honeydew, Col = 0, Row = 0, Width = CellWidth };
        var m14 = new Block() { Name = "bhg jg7", Background = Brushes.Honeydew, Col = 0, Row = 0, Width = CellWidth };

        _blockList = new ObservableCollection<Block>() { m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11};
    }

    int _cellWidth;
    public int CellWidth { get { return _cellWidth; } set { _cellWidth = value; RaisePropertyChanged("CellWidth"); } }

    ObservableCollection<Block> _blockList;
    public ObservableCollection<Block> BlockList { get { return _blockList; } set { _blockList = value; RaisePropertyChanged("BlockList"); } }

    public event PropertyChangedEventHandler PropertyChanged;
    void RaisePropertyChanged(string propname)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
    }
}

public class Block : INotifyPropertyChanged
{

    int _row;
    public int Row { get { return _row; } set { _row = value; RaisePropertyChanged("Row"); } }

    int _col;
    public int Col { get { return _col; } set { _col = value; RaisePropertyChanged("Col"); } }

    string _name;
    public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }

    Brush _background;
    public Brush Background { get { return _background; } set { _background = value; RaisePropertyChanged("Background"); } }

    int _width = 50;
    public int Width { get { return _width; } set { _width = value; RaisePropertyChanged("Width"); } }

    int _height = 50;
    public int Height { get { return _height; } set { _height = value; RaisePropertyChanged("Width"); } }

    public event PropertyChangedEventHandler PropertyChanged;
    void RaisePropertyChanged(string propname)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
    }
}

I am trying to refresh the ItemControl once I make the changes in GameBoard but not able to do it.
Preferably I would want to change the width with only one width variable in GameBoard class but looks like that will not be possible here

Regards

1

There are 1 best solutions below

0
mm8 On

Fix your ItemContainerStyle to target the ContentPresenter and bind its Width and Height properties to your source properties:

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <Setter Property="Width" Value="{Binding Width}" />
        <Setter Property="Height" Value="{Binding Height}" />
    </Style>
</ItemsControl.ItemContainerStyle>

And since you are creating a new GameBoard object in your click event handlers, you also need to implement the INotifyPropertyChanged interface and raise change notifications in the window:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        GameBoardApp = new GameBoard(50);
        DataContext = this;
    }

    private GameBoard _gameBoardApp;

    public GameBoard GameBoardApp
    {
        get { return _gameBoardApp; }
        set { _gameBoardApp = value; RaisePropertyChanged(); }
    } 

    private void IncreaseSize_Click(object sender, RoutedEventArgs e)
    {
        GameBoardApp = new GameBoard(GameBoardApp.CellWidth + 1);
        Debug.Print(GameBoardApp.CellWidth.ToString());
    }

    private void DecreaseSize_Click(object sender, RoutedEventArgs e)
    {
        GameBoardApp = new GameBoard(GameBoardApp.CellWidth - 1);
        Debug.Print(GameBoardApp.CellWidth.ToString());
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

This should make your example work as expected.

As a side note, the Grid.Row and Grid.Column attached properties are not useful when you host your item containers in a WrapPanel.