WPF - Set DataTemplate for programmatically added GridViewColumns
Asked Answered
D

3

5

Couldn't find an answer to this one.

I have a WPF ListView control that can contain varying number of columns. For example, it can display customer data, displaying columns Id, Name, Email etc, or it can contain products, displaying ID, Name, Price, NumberInStock, Manufacturer, well, you get the idea: varying number of columns, varying names.

What I want to do, is for certain columns to display the data differently. For example, instead of printing 'Yes' or 'No' as the value of the column NumberInStock, I want to display a neat image.

If I'd have a fixed amount of columns, with fixed names to bind to, I kinda see how this is easy. Just define a DataTemplate for that particular column and I'd use that to define the view of my column. However, I can't see how to do it in my situation.

I am very new to WPF, so excuse me if my approach is bad :-) In my XAML, I have defined a ListView control, which is pretty much empty. In my code behind, I use:

    // get all columns from my objects (which can be either a Customer of Product)
    foreach (string columnName in MyObject.Columns)
        {
          GridViewColumn column = new GridViewColumn();
          // Bind to a property of my object
          column.DisplayMemberBinding = new Binding("MyObject." + columnName);
          column.Header = columnName;
          column.Width = 50;
          // If the columnname is number of stock, set the template to a specific datatemplate defined in XAML
          if (columnName == "NumberInStock")
            column.CellTemplate = (DataTemplate)FindResource("numberInStockImageTemplate");
          explorerGrid.Columns.Add(column);
        }

Ok, I'm sure this could be done a bit prettier (if you have any advice, please!) but the biggest problem is that I can't see any difference in the column. It just displays the text value of the 'NumberInStock' column. My DataTemplate is defined in the XAML:

<Window.Resources>
<DataTemplate x:Name="NumberInStock" x:Key="NumberInStock">
      <Border BorderBrush="Red" BorderThickness="2.0">
        <DockPanel>
          <Image Width="24" Height="24" Margin="3,0" Source="..\Images\instock.png" />
        </DockPanel>
      </Border>
</DataTemplate>
</Window.Resources>

Of course, I would still have to add the functionality that it would display a 'yes' or 'no' image depending on the value of NumberInStock, but that is step 2 really. I would be happy to see an image and a red border in my ListView!

Thanks in advance, Razzie

Dry answered 24/2, 2009 at 21:43 Comment(0)
L
10

This tripped me up for a while.

DisplayMemberBinding and CellTemplate are mutually exclusive. Specifying a DisplayMemberBinding causes the CellTemplate to be ignored.

From MSDN:

The following properties are all used to define the content and style of a column cell, and are listed here in their order of precedence, from highest to lowest:

* DisplayMemberBinding
* CellTemplate
* CellTemplateSelector

Also see a C# Disciples post about this.

Lp answered 26/2, 2009 at 4:47 Comment(1)
Thanks, that looks indeed like the problem. I already edited my code though to use a DataGrid control that comes with SP1. It may be more suitable in my situation anyway. But this is definately the right solution. Many thanks!Dry
C
3

This is the easiest way to insert a picture

GridViewColumn column = new GridViewColumn { Header = "IM" };            
DataTemplate template = new DataTemplate();

FrameworkElementFactory factory  =  new FrameworkElementFactory(typeof(Grid));
template.VisualTree = factory;
FrameworkElementFactory imgFactory  =  new FrameworkElementFactory(typeof(Image));

Binding newBinding  =  new Binding("IMG");
imgFactory.SetBinding(Image.SourceProperty,newBinding);
imgFactory.SetValue(Image.WidthProperty,15.0);
imgFactory.SetValue(Image.HeightProperty, 15.0);

factory.AppendChild(imgFactory);
column.CellTemplate = template;
view.Columns.Add(column);

ListViewMain.View = view;
Cleanlimbed answered 16/5, 2012 at 15:27 Comment(2)
Some more explaination would be usefull, but this lead me to the right answer (on my case). In a nutshell: This will generate the content creator for a column, so each row the factory is called to generate controls defined on the factory. thank you.. +1Reactionary
This style worked for me. NOTE: My binding was to a property of a child object property, so it looked like this: Binding newBinding = new Binding("ChildProp.SubProp"); newBinding.Converter = new MyConverter();Rufena
Z
0

I think the problem is that the string that you're passing to FindResource() doesn't match the key of the resource that you defined in the XAML. Try passing "NumberInStock" instead and see if that works.

Zoubek answered 26/2, 2009 at 4:2 Comment(1)
Sharp! Unfortunately, it was merely a copy-paste-edit-for-question error on my side. It points to the correct key in my code. Moreover, FindResource(string) throws an exception when the key is not found. Thanks anyway!Dry

© 2022 - 2024 — McMap. All rights reserved.