C# – Populate WPF DataGrid dynamically

cdatagriddynamicformattingwpf

My program needs to take input from a fire alarm panel over a serial connection and populate a list based on it. When a new device is reported from the panel, the statement is parsed and the device is added to the device List.

That part of my program all works fine and dandy. The problem now is displaying the list of fire alarm devices to the user.

I am hoping to do this using a DataGrid (unless there's a better way?) but am not able to find a lot of helpful documentation on WPF DataGrids that is relevant to me. Most of what's out there seems to be displaying data from a database. Mine however, needs to update every time the panel spits out a new device description and the device List in my program is appended.

I see I can set AutoGenerateColumns to true and initially display my list just fine. BUT, I would like to customize the column headers. Also this doesn't update when the List is appended so I'm not sure how to "refresh" it.

When AutoGenerateColumns is false, I get no data displayed. When the program runs it shows me the correct number of rows corresponding to the number of items in my list, but no data. Wondering how/if I need to link each column with its corresponding device data member?

Lastly, how do you format a DataGrid to look pretty through re-sizes? I can set column width and all that, but what I want is a few of the columns to be fixed width, and the middle column to expand to fill remaining available area.

This is my first stab at WPF. Any help would be greatly appreciated!

Best Answer

Personaly i dont like DataGrid much. Yes they are easier to bind and they offer built-in resize and sorting options but they are not as flexible as an ItemsControl with a good DataTemplating on your Objects. Let me explain myself.

I tend to populate my ItemsControl with an ObservableCollection. Then, i use a DataTemplate in order to tell my ItemsControl how to display my custom items.

Your CustomObjects can be Modeles objects if your doing MVVM. If your list is binded to an ObservableCollection, the Added and Removed items will appear dynamicly into your list, which is what i belive your trying to do. For the column size, you could put a Grid specifying GridColumns width to fixed Width for some columns and * for others so they fill the remaining space.

Here's an alternative to the GridView I use a ScrollViewer around my ItemControl so if the ItemsControl get too big, you can scroll it. The ItemsControl's ItemSource is binded to your FireAlarms's ObservableCollection. The WrapPanel in the ItemsControl will contain each DataTemplate. It's Width is binded to his parent (or ancestor if you will) which is an ItemsControl

<ScrollViewer
    Grid.Row="x"
    Grid.Column="y"
    VerticalScrollBarVisibility="Auto"
    Margin="5">

    <ItemsControl
    BorderBrush="DarkBlue"
    BorderThickness="2"
    ItemsSource="{Binding Path=FireAlarms}"
    ItemTemplate="{StaticResource FireAlarmsTemplate}"
    >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel 
                        Orientation="Horizontal"
                        Width="{Binding RelativeSource=
                            {RelativeSource FindAncestor,
                            AncestorType={x:Type ItemsControl}},
                            Path=ActualWidth}"
                        >
                </WrapPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</ScrollViewer>

Ok then you need a DataTemplate. You can put the DataTemplate in your windows's ressource or in a DataDictionnary. Lets say you have a class :

FireAlarm
{
    Public String AlarmInfo1;
    Public String AlarmInfo2;
    Public String AlarmInfo3;
}

Here could be a nice DataTemplate to start with :

<DataTemplate x:Key="FireAlarms">
    <Border 
        BorderBrush="SteelBlue" 
        Background="LightBlue" 
        BorderThickness="2" 
        Margin="10" 
        Padding="10">
        <StackPanel 
            Orientation="Vertical"
            >
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition Width="5"></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>


                <Label 
                    Grid.ColumnSpan="3"
                    Grid.Row="0"
                    Content="{Binding Path=AlarmName}"
                    Margin="5,-5,5,10"
                    FontWeight="Bold"
                    FontSize="16"
                    HorizontalContentAlignment="Center"
                    HorizontalAlignment="Center">
                </Label>

                <TextBlock  
                    Text="Alarm information 1" Grid.Row="1" Grid.Column="0" />
                <TextBox 
                    Text="{Binding Path=AlarmInfo1}"
                    Grid.Column="2"
                    Grid.Row="1"
                    >
                </TextBox>

                <TextBlock 
                    Text="Alarm information 2" Grid.Row="2" Grid.Column="0" />
                <TextBox 
                    Text="{Binding Path=AlarmInfo2}"
                    Grid.Column="2"
                    Grid.Row="2"
                    >
                </TextBox>

                <TextBlock  
                    Text="Alarm information 3" Grid.Row="3" Grid.Column="0" />
                <TextBox 
                    Text="{Binding Path=AlarmInfo3}"
                    Grid.Column="2"
                    Grid.Row="3"
                    >
                </TextBox>
            </Grid>
        </StackPanel>
    </Border>
</DataTemplate>

Ok I hope this is usefull for you. My Template will generate 1 square per alarm. If you'd rather have it in a Table like a GridView, you could modify this using a verticaly oriented stack panel and use a grid with variable // invariable column width but since you asked for anything usefull, i tough i'd guive you something fun to work with!

Enjoy!

Related Topic