1

I have inherited a project that displays 2D data in grid. However, things seem to be done in a wrong way. I have set up a minimum working example for a hall and seats:

Data is stored in a model as a 2D array:

    public class DataModel
    {
        public enum SeatState { Empty, Reserved, Sold }
        public SeatState[,] Seats;
        public DataModel(int rows, int columns)
        {
            Seats = new SeatState[columns, rows];
        }
    }

The displaying Window gets reference to the Seats array in constructor, creates Rectangle objects, inserts them into the grid and stores them for later "rendering" use. When the data change, Render method is called, walks through the entire 2D array and sets Rectangle styles.

The XAML with style resources:

<Window x:Class="SE_MWE.HallWindow"
        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:local="clr-namespace:WPF_Quora_binding"
        mc:Ignorable="d"
        Title="HallWindow" Height="450" Width="800">

    <Window.Resources>
        <Style x:Key="EmptySeat" TargetType="Rectangle">
            <Setter Property="Margin" Value="1" />
            <Setter Property="Fill" Value="LightSeaGreen" />
        </Style>

        <Style x:Key="ReservedSeat" TargetType="Rectangle" BasedOn="{StaticResource EmptySeat}">
            <Setter Property="Fill" Value="OrangeRed" />
        </Style>

        <Style x:Key="SoldSeat" TargetType="Rectangle" BasedOn="{StaticResource EmptySeat}">
            <Setter Property="Fill" Value="DarkMagenta" />
        </Style>

    </Window.Resources>

    <Grid x:Name="HallGrid" Margin="10" />

</Window>

And the C# code:

    public partial class HallWindow : Window
    {
        private readonly DataModel.SeatState[,] _seats; //reference to data
        private Rectangle[,] _uiSeats; // stored UI elements for rendering

        public HallWindow(DataModel.SeatState[,] seats)
        {
            InitializeComponent();
            _seats = seats;
            InitializeHall();
            RenderHall();
        }

        private void InitializeHall()
        {
            _uiSeats = new Rectangle[_seats.GetLength(0), _seats.GetLength(1)];

            // define grid rows and columns according to data width and height
            for (int i = 0; i < _seats.GetLength(0); i++)
            {
                HallGrid.ColumnDefinitions.Add(new ColumnDefinition());
            }

            for (int i = 0; i < _seats.GetLength(1); i++)
            {
                HallGrid.RowDefinitions.Add(new RowDefinition());
            }

            // insert child elements, store them for render
            for (int x = 0; x < _seats.GetLength(0); x++)
            {
                for (int y = 0; y < _seats.GetLength(1); y++)
                {
                    Rectangle UISeat = new Rectangle();

                    HallGrid.Children.Add(UISeat);
                    Grid.SetRow(UISeat, y);
                    Grid.SetColumn(UISeat, x);

                    _uiSeats[x, y] = UISeat;
                }
            }
        }

        public void RenderHall()
        {
            for (int x = 0; x < _seats.GetLength(0); x++)
            {
                for (int y = 0; y < _seats.GetLength(1); y++)
                {
                    DataModel.SeatState seatState = _seats[x, y];
                    Rectangle UISeat = _uiSeats[x, y];

                    UISeat.Style = FindStyle(seatState);
                }
            }
        }

        private Style FindStyle(DataModel.SeatState state)
        {
            switch (state)
            {
                case DataModel.SeatState.Empty:
                    return FindResource("EmptySeat") as Style;
                case DataModel.SeatState.Reserved:
                    return FindResource("ReservedSeat") as Style;
                case DataModel.SeatState.Sold:
                    return FindResource("SoldSeat") as Style;
                default:
                    return null;
            }
        }
    }

This way is definitely not the correct MVVM. I know that instead of manually calling Render() method whenever data change, we should only create a binding between the UI elements and the 2D data, and then the framework takes care of all UI updates automatically. I use this regularly with simple dependency properties and bindings written directly into XAML.

However, I'm confused, how to fix this - how to make this kind of binding.

Jan Koupil
  • 87
  • 1
  • 1
  • 8

0 Answers0