How to get started with DataGrid

Nov 27, 2008 at 3:24 AM
I got a DataGrid up-and-running but need some help getting oriented.

I started off by copying XAML from a previous ListView GridView implementation but found that my columns were duplicated. First, I had all the columns in order as defined; then repeated in the order defined in the binding List<T> (actually an ObservableCollection<T>). So I took out all the column defs between <my:DataGrid.Columns></my:DataGrid.Columns> and was (sort of) pleasantly surprised to see all the columns of my list, however not in the order I'd like. The cool thing was that it automatically created appropriate controls in each cell: checkboxes in cells with bool data, comboboxes in cells with enum data (with the selection items already there!!) and so on. So that was pretty cool. But now I need to get the columns in order with custom header names (as opposed to the default of field name) and in my custom order. The backing List<T> (again ObservableCollection<T>) is auto generated from a WCF proxy creation, so I can't really go changing the order of fields in the class definition since that would just get over-written the next time I update the proxy. I like the idea of have so much done automagically - like appropriate controls in each field - so I'd rather not have to go and create all the DataGrid.Columns again.

So I need some gentle guidance. Thanks

Bill

Nov 27, 2008 at 3:28 AM
sorry, forgot to post some code:

<!--BinDataGrid.xaml-->
<UserControl x:Class="Aceco.eZ80WIN.ScTech.BinDataGrid"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit"
    x:Name="BinDataGridControl"
    Height="Auto" Width="Auto">
    <UserControl.Resources>
        <ObjectDataProvider x:Key="BinData" />
    </UserControl.Resources>
    <Grid DataContext="{Binding BinData, ElementName=BinDataGridControl}" >
        <DockPanel Height="Auto" Name="dockPanel" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
            <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" VerticalAlignment="Bottom" Height="30" FlowDirection="LeftToRight">
                <Button Width="75" Name="btnDeleteBin">Delete Bin</Button>
                <Button Width="75" Name="btnAddBin" Click="btnAddBin_Click">Add Bin</Button>
            </StackPanel>
            <my:DataGrid DockPanel.Dock="Top" Name="BinDataDetails" ItemsSource="{Binding}" />
        </DockPanel>
    </Grid>
</UserControl>

// BinDataGrid.xaml.cs
namespace Aceco.eZ80WIN.ScTech {
    /// <summary>
    /// Interaction logic for BinDataGrid.xaml
    /// </summary>
    public partial class BinDataGrid : UserControl {
        public ScBinDataCollection binData;
        public ScBinDataCollection BinData {
            get {
                if (binData == null) {
                    using (ScBinCmdServiceClient proxy = new ScBinCmdServiceClient()) {
                        binData = proxy.BinCmdList();
                    }
                }
                return binData;
            }
        }

        public BinDataGrid() {
            InitializeComponent();
        }

        private void btnAddBin_Click(object sender, RoutedEventArgs e) {
            binData.Add(new ScBinData());
        }

    }
}

Coordinator
Nov 27, 2008 at 4:58 AM
Hi iamwpmccormick,

DataGrid has a feature called column AutoGeneration and what you are seeing is the result of this. You have two solutions for your problem...

  • Set AutoGenerateColumns to false on DataGrid in which case you will have to write all the column definitions and you can choose what ever order you want.
  • Retain AutoGenerateColumns to true.
    • In this case DataGrid has events named AutoGeneratingColumn and AutoGeneratedColumns.
    • AutoGeneratingColumn event is fired for each auto generated column before it gets added to the DataGrid. This gives you a chance to change properties of  the column or change the entire column itself. You can even choose to not include the column at all. Here you can set the DisplayIndex property of the column to control its display position.
    • Note that in this event handler the DisplayIndex should always be backward pointing, meaning if you are handling 10 column you can set its DisplayIndex from 0 to 9 even though the final total number of columns will be 20 (this is because the remaining columns are not added yet and DataGrid will not like it if you set it higher than 9).
    • AutoGeneratedColumns event is fired once all the columns in the DataGrid are auto generated and here you can change the DisplayIndex of the columns in what ever way you want.
    • You can use either of these events to achieve your expected behavior.
Nov 27, 2008 at 5:49 AM
OK. Thanks. So I think in my application, AutoGenerateColumn=False would be best.

Now I'd like to be able to make the default control in each cell appropriate for the data in the cell; checkbox for bool's, combobox for enum's with the list choices complete, etc.

                    <my:DataGridComboBoxColumn Header="Type" ItemsSource="{Binding Path=ScBinType}" />

The above is not working.

Thanks.

Coordinator
Nov 27, 2008 at 6:27 AM
http://blogs.msdn.com/vinsibal/archive/2008/10/31/wpf-datagrid-datagridcomboboxcolumn-v1-intro.aspx for DataGridComboBoxColumn
http://www.codeplex.com/wpf/Wiki/View.aspx?title=Feature%20Walkthrough%20Sample&referringTitle=Tips%20%26%20Tricks
 in general  should give more idea.
Nov 27, 2008 at 3:59 PM
Thanks, that helps a little, but I still have no idea how to bind to an enum type to get the list of selections in the combobox. How does it do this automatically (when I have AutoGeneration turned on?) It can't be done in the same way that I'm binding to get the data for the grid itself because the enums are defined in a different class. I realize this is more of an issue with binding in general than with the DataGrid (really just a lack of understanding how binding works,) but still, any help would be appreciated.

Thanks,

Bill
Nov 27, 2008 at 4:56 PM
Hi Bill,

I have not tried this before ... so thought I would give it a go. And do you know what? it just works. The AutoGenerated column for an enum type is a ComboBox column type which is itself bound to eth enum values. Just give it a go:

public partial class Window1 : Window
{
    private List<Character> characters;

    public Window1()
    {
        InitializeComponent();

        characters = new List<Character>(){
            new Character()
            {
                Surname = "Grimmes",
                Forename = "Frank",
                Sex =SexEnum.Male
            },
            new Character()
            {
                Surname = "Simpson",
                Forename = "Bart",
                Sex=SexEnum.Male
            },
            new Character()
            {
                Surname = "Simpson",
                Forename = "Lisa",
                Sex=SexEnum.Female
            }
        };

        this.DataContext = characters;
    }
}

public enum SexEnum
{
    Male, Female
}

public class Character
{
    public string Surname { get; set; }
    public string Forename { get; set; }
    public SexEnum Sex { get; set; }
}

<Window ... Height="300" Width="300">
    
    <Grid>
        <dg:DataGrid ItemsSource="{Binding}">            
        </dg:DataGrid>
    </Grid>
</Window>

Pretty cool eh?

Regards,
Colin E.
--
http://wpfadventures.wordpress.com/ - My WPF blog
http://www.codeproject.com/KB/WPF/WPFDataGridExamples.aspx - WPF DataGrid Practical Examples

http://www.codeproject.com/KB/WPF/WPFDataGridExamples.aspx


Nov 27, 2008 at 6:03 PM
Yes, perhaps I understated how cool this is in my original post.

I need to have AutoGenerateColumn=False and handle the binding of enum data/types to ComboBox columns manually.

Thanks for your input anyway.

Bill
Nov 27, 2008 at 6:20 PM
This is what I have so far:

<UserControl x:Class="Aceco.eZ80WIN.ScTech.BinDataGrid"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:dc="clr-namespace:Aceco.eZ80WIN.ScTech.ScCmdSvc"
    x:Name="BinDataGridControl"
    Height="Auto" Width="Auto">
    <UserControl.Resources>
        <ObjectDataProvider x:Key="BinType" ObjectType="{x:Type dc:BIN_TYPE}" />
    </UserControl.Resources>
    <Grid DataContext="{Binding BinData, ElementName=BinDataGridControl}" >
            <dg:DataGrid DockPanel.Dock="Top" Name="BinDataDetails" AutoGenerateColumns="False" ItemsSource="{Binding}">
                <dg:DataGrid.Columns>
                    <dg:DataGridTextColumn Header="Bin ID" Binding="{Binding ScBinId}" />
                    <dg:DataGridComboBoxColumn Header="Bin Type"
                            SelectedValueBinding="{Binding ScBinType}"
                            SelectedValuePath="ScBinType"
                            DisplayMemberPath="ScBinType"
                            ItemsSource="{Binding Source={StaticResource BinType}}"/>

    <snip>
Nov 27, 2008 at 8:37 PM
Ah ... sorry, didn't realise you wanted this without AutoGenerated columns. You can find out how to bind a ComboBox to an enum on this following blog post:

http://joshsmithonwpf.wordpress.com/2007/06/20/displaying-sorted-enum-values-in-a-combobox/

It should work just the same for a DataGrid column.

Colin E.
Coordinator
Nov 27, 2008 at 8:40 PM
http://blogs.msdn.com/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx should help you out. This post basically shows how to use enum for ItemsSource of ListBox in XAML without needing to write your own object data provider but you can use the same strategy for DataGridComboBoxColumn.ItemsSource.
Nov 29, 2008 at 7:27 AM
Finally got this figured out. Here are the relevant sections:

<UserControl x:Class="Aceco.eZ80WIN.ScTech.BinDataGrid"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:dc="clr-namespace:Aceco.eZ80WIN.ScTech.ScCmdSvc"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"


        <ObjectDataProvider x:Key="BinType" ObjectType="{x:Type sys:Enum}" MethodName="GetValues">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="dc:BIN_TYPE" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>

                    <dg:DataGridComboBoxColumn Header="Bin Type"
                            SelectedValueBinding="{Binding ScBinType}"
                            ItemsSource="{Binding Source={StaticResource BinType}}"/>


Happy coding!

Bill