logo

C# How to databind a WPF datagrid using a bindablecollection

The Model View View model architecture allows for two way binding. In other words, Data changed in a datagrid will bind to data in an databindable collection and data changed in a databindable collection will bind with the datagrid, automatically. The MVVM model simplifies interaction with the datagrid.

We are using caliburn for the mvvm architecture. Caliburn create a bootstrapper entry point. The MainViewModel is loaded with a mainview.xaml. The mainview has a grid for containing a button. The button will be used to activate the AddressViewModel through an LoadAddressBook button click event. The views and the viewmodels are connected by naming convention. The XXXviews are stored in the view directory and the XXXviewModels are stored in the ViewModels directory

A usercontrol containing a textbox and a datagrid is loaded into the MainWindow view on the addressbook button click. The textbox x:Name="SearchName" is connected to the AddressViewModel Property SearchName. The x:Name="AddressBooks" is connected to the databindable AddressViewModel property AddressBooks, a databindablecollection. When an user enters data into the textbox, a text change event is raised and the AddressBookViewModel SearchName setter is invoked assigning the value to the _searchName variable. When the user presses the SearchButton, the Search method in the addressviewModel is called an the repository loads the databindablecollection with addressbook items.

The App.XML is initialized with a Boostrapper entry point. The Bootstrapper loads the mainViewModel and its view.

App.xml

<Application x:Class="MillenniumERP.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MillenniumERP"
             >
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:Bootstrapper x:Key="Bootstrapper" />
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Bootstrapper.cs


public class Bootstrapper : BootstrapperBase
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Bootstrapper"/> class.
        /// </summary>
        public Bootstrapper()
        {
            Initialize();
        }

        /// <summary>
        /// Override this to add custom behavior to execute after the application starts.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The args.</param>
        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            DisplayRootViewFor<MainViewModel>();
        }
    }

MainView


<Window x:Class="MillenniumERP.Views.MainView"
        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:MillenniumERP.Views"
        mc:Ignorable="d"
        Title="MainView" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="20"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <Button x:Name="LoadAddressBook" Grid.Column="1" Grid.Row="1" Content="AddressBook" />
        <ContentControl Grid.Column="1" Grid.Row="2" x:Name="ActiveItem" Grid.ColumnSpan="2" Margin="0,20,0,0"/>
    </Grid>
</Window>

MainViewModel

public class MainViewModel : Conductor<object>
    {
        /// <summary>
        /// Loads the address book.
        /// </summary>
        public void LoadAddressBook()
        {
            ActivateItem(new AddressBookViewModel());
        }
    }

AddressView User control


<UserControl x:Class="MillenniumERP.Views.AddressBookView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MillenniumERP.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Background="LightSkyBlue">
    <Grid>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="20"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <Button x:Name="Search" Grid.Column="2" Grid.Row="1" Content="Search" Margin="20,0,0.4,0" Height="19" VerticalAlignment="Top" />
        <TextBox  MinWidth="200" x:Name="SearchName" Grid.Column="1" Grid.Row="1" />

        <DataGrid x:Name="AddressBooks" AutoGenerateColumns="false" Grid.Column="1" Grid.Row="2" Grid.ColumnSpan="3" Margin="0,20,0,0">
            <DataGrid.Columns>
                <DataGridTextColumn Width="*" Header = "Key" Binding="{Binding AddressId}" />
                <DataGridTextColumn Width="*" Header = "Name" Binding="{Binding Name}" />
                <DataGridTextColumn Width="*" Header = "State" Binding="{Binding BillingState}" />
            </DataGrid.Columns>
        </DataGrid>
    

    </Grid>
</UserControl>

AddressViewModel


public class AddressBookViewModel : Screen
    {
        private BindableCollection<AddressBook> _addressBooks = new BindableCollection<AddressBook>();
        private string _searchName = "";
        UnitOfWork unitOfWork = new UnitOfWork();

        public void Search()
        {
            AddressBooks.Clear();
            IQueryable <AddressBook> query = unitOfWork.addressBookRepository.GetObjectsAsync(a => a.Name.Contains(SearchName));
            foreach (var item in query)
            {
                AddressBooks.Add(item);
            }

        }
        public string SearchName
        {
            get { return _searchName; }
            set
            {
                _searchName = value;
       
            }
        }

        public BindableCollection<AddressBook> AddressBooks
        {
            get { return _addressBooks; }
            set { _addressBooks = value; }
        }

    }
s