DEV Community

Cover image for WPF DataGrid Binding
John Peters
John Peters

Posted on

WPF DataGrid Binding

The WPF DataGrid is the goto grid-like control for desktop development.

Create the columns first



<DataGrid x:Name="processListDataGrid"
          EnableRowVirtualization="True"
          AutoGenerateColumns="False"
          RowDetailsVisibilityMode="VisibleWhenSelected"
 >
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Padding="5">Testing One Two Three</Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>




Enter fullscreen mode Exit fullscreen mode

The code above says: "For each item in the DataGrid create a Button that has the text shown".

This is the result.

Alt Text

These buttons have implicit binding to the Text shown inside the button tag. Similar to innerText in HTML this property is call 'content' instead.

In all of our DataGrid columns we prefer the DataGridTemplateColumn approach. We do this to take advantage of premade user controls like the Button. The current theme is applied as a result.

Binding to a Collection of Items

The data we show, in our case, is that of an ObservableCollection which is recommended in WPF land.

The binding can be done in the code behind as follows:



var content = new ObservableCollection<ProcessInfo>(new ProcessList());
processListDataGrid.ItemsSource = content;



Enter fullscreen mode Exit fullscreen mode

Alt Text

By setting the items source directly we are providing the data the grid needs. However, nothing showed until we told each column the property name to show.



 <DataGrid.Columns>
            <DataGridTemplateColumn Width="120">
                <DataGridTemplateColumn.CellTemplate>  
                    <DataTemplate>
                        <Button Tag="{Binding id}" Click="OnKillProcess" HorizontalAlignment="Stretch">Stop Process</Button>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Style="{DynamicResource gray}" Text="{Binding id}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Style="{DynamicResource gray}"  Text="{Binding windowtitle}"></TextBlock>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock  Style="{DynamicResource gray}" Text="{Binding processname}"></TextBlock>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>



Enter fullscreen mode Exit fullscreen mode

For first timers, none of this is intuitive, and even for those returning to WPF there's plenty of stumbling blocks. Keep in mind that our data model defines the fields.

Data Model



public class ProcessInfo {
    public string id { get; set; }
    public string windowtitle { get; set; }
    public string processname { get; set; }
}

Don't forget the getters and setters!


Enter fullscreen mode Exit fullscreen mode

Where did the data come from?



public class ProcessList : List<ProcessInfo> {
    public ProcessList()        {
        var items = Process.GetProcesses().Where(item => item.MainWindowTitle != string.Empty ||
        item.ProcessName.Contains("chromedriver")).ToList();
        items.ForEach(item =>
        {
            this.Add(new ProcessInfo()
            {
                id = item.Id.ToString(),
                windowtitle = item.MainWindowTitle,
                processname = item.ProcessName,
            });
        });
    }
}



Enter fullscreen mode Exit fullscreen mode

Of course, the System.Diagnostics.Process class!

The Button Click Handler



private async void OnKillProcess(object sender, RoutedEventArgs e)
{
    Button btn = (Button)sender;
    int id = int.Parse(btn.Tag.ToString());
    await Task.Run(() =>
    {
        var proc = Process.GetProcessById(id);
        proc.Kill();
    });
    this.RefreshView();
}



Enter fullscreen mode Exit fullscreen mode

Async first to stop any GUI lag.

It's all simple once we know how, it's easily remembered if we log how to do it on Dev.to.

JWP2021 WPF UserControl Process

Top comments (2)

Collapse
 
jwp profile image
John Peters

Ah yes Sebastian; I forgot about the ICommand and it's RelayCommand brother. Thanks for reminder!

If we make our eventHandlers async, and put in code like this:

await Task.Run(() =>
{
    var proc = Process.GetProcessById(id);
    proc.Kill();
});
// this won't run until await is done.
this.RefreshView();

Enter fullscreen mode Exit fullscreen mode

The GUI Will never freeze and the next statement after the await doesn't execute until the Task is done! Kind of like a built in state machine for the button and the view.

Collapse
 
jwp profile image
John Peters

Also, the button tag contains the process ID.