2

Using VS 2010 I plan to make several datagrid template columns, but they will all contain a textblock and I want to them to behave like a text column in terms of sorting, filtering, editing, etc. (For example, a column that has a textblock and an image within a stackpanel, but behavior-wise it should really be all about the text.)

When using a template column, I've learned that much of the functionality associated with a normal text cell must be redone. For instance, in order to make the text editable, one must provide a cell editing template like:

<DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
        <TextBox
        FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
        Text="{Binding Path=SomeProperty, Mode=TwoWay, UpdateSourceTrigger=LostFocus}">
            </TextBox>
    </DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>

(And you must also handle grid events in order to make sure it behaves like a normal textbox, like automatically beginning edit mode if the user presses a key down, or tabs to the cell, etc.)

So my question is, what is the best way (if any) for me to avoid having to explicitly write this code (as well as the code for sorting, copying, filtering, etc like a textcell, for EVERY template column I create like this? I'm assuming it's bad practice to be reproducing half a page's worth of code for each column, where the only differences might the binded property name and a handful of visual alterations.

I am beyond frustrated with this, and with WPF in general. I've scoured the web, I've tried adorners, I've tried inheriting a datagrid column, I've tried defining user controls for the data templates, and everything seems to fail with some nasty "gotcha". This is about the 3rd question I've asked regarding this with varying details, with minimal response. It should NOT be this hard to figure out how to implement what are basically glorified text columns, without needing to reinvent the entire wheel every single time and in every single respect. In my humble opinion at least.

Rubens Mariuzzo
  • 28,358
  • 27
  • 121
  • 148
Tekito
  • 840
  • 8
  • 24
  • 1
    You're overcomplicating it all too much. Why don't you just set the `CellStyle` for the relevant Columns, and override the `DataGridCell.Template` instead? You could also define these templates as resources and reuse them. That way, you keep the `DataGridTextColumn` instead of having to implement it all yourself. – Federico Berasategui Jul 15 '13 at 20:56
  • That sounds great - can you please give me some code to work with? For example, can you show me how use cell styles to modify an existing `DataGridTextColumn` such that it displays text along with an image next to it? – Tekito Jul 15 '13 at 21:03

2 Answers2

1

can you show me how use cell styles to modify an existing DataGridTextColumn such that it displays text along with an image next to it?

Here:

 <DataGridTextColumn Binding="{Binding LastName}">
     <DataGridTextColumn.CellStyle>
         <Style TargetType="DataGridCell">
             <Setter Property="Template">
                 <Setter.Value>
                     <ControlTemplate TargetType="DataGridCell">
                         <Grid>
                             <Grid.ColumnDefinitions>
                                 <ColumnDefinition/>
                                 <ColumnDefinition Width="16"/>
                             </Grid.ColumnDefinitions>

                             <ContentPresenter ContentSource="Content"/>
                             <Image Source="/Images/Homer.jpg" Grid.Column="1"/>
                        </Grid>
                     </ControlTemplate>
                 </Setter.Value>
             </Setter>
         </Style>
     </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

With some more work, you can define these templates and style as Resources in your application and reuse them every time.

With regards to the image source, you could use an Attached Property to define it in the DataGridTextColumn, or even the Tag property.

Edit: Example with an attached property:

 <DataGrid ...>
            <DataGrid.Resources>
                <ControlTemplate TargetType="DataGridCell" x:Key="TextAndImageDataGridCellTemplate">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="16"/>
                        </Grid.ColumnDefinitions>

                        <ContentPresenter ContentSource="Content"/>
                        <Image Source="{Binding Column.(local:GridColumnProperties.ImageSource), RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="1"/>
                    </Grid>
                </ControlTemplate>
            </DataGrid.Resources>
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding LastName}"
                                    local:GridColumnProperties.ImageSource="/Images/Homer.jpg">
                    <DataGridTextColumn.CellStyle>
                        <Style TargetType="DataGridCell">
                            <Setter Property="Template" Value="{StaticResource TextAndImageDataGridCellTemplate}"/>
                        </Style>
                    </DataGridTextColumn.CellStyle>
                </DataGridTextColumn>
            </DataGrid.Columns>
    </DataGrid>

Code:

   public static class GridColumnProperties
    {
        public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.RegisterAttached("ImageSource", typeof(ImageSource), typeof(GridColumnProperties), new PropertyMetadata());

        public static void SetImageSource(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(ImageSourceProperty, value);
        }

        public static ImageSource GetImageSource(DependencyObject obj)
        {
            return obj.GetValue(ImageSourceProperty) as ImageSource;
        }
    }
Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
  • Why does the cell display as blank white when it's selected? Any way to make it still display contents with blue background? – Tekito Jul 15 '13 at 21:21
  • @Tekito Since you have overriden the default `Template`, you lost the default visual behavior. Take a look at the last XAML shown [here](http://msdn.microsoft.com/en-us/library/cc278066(v=vs.95).aspx) for the default style. You can copy that template and modify it to include my code too. – Federico Berasategui Jul 15 '13 at 21:27
  • I will give it a shot. A very sincere thanks for posting this. My sanity was all but gone trying to figure this out. I've no doubt WPF is a powerful tool, but it's also one of the most intractable tools I've come across, unless you are able to spend a serious amount of time mastering it. – Tekito Jul 15 '13 at 21:36
  • @Tekito It's worth it's weight in gold. Trust me, you'll love it. – Federico Berasategui Jul 15 '13 at 21:37
  • you're probably no longer reading this, but I'm stuck on using AttachedProperty to define image source. Is it something quick and easy, or more involved? – Tekito Jul 15 '13 at 23:50
  • @Tekito it's really easy. I'm going to sleep right now, tomorrow I can show you an example of how to do it. – Federico Berasategui Jul 16 '13 at 04:13
  • Thanks much again. I've added the attached property, and set it in the `datagridtextcolumn` (I think). But I think it's still failing in cellstyle. I'm binding the image by searching for `"{Binding Path=(e:GridColumnProperties.ImageSource), RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridTextColumn}}"/>` but I've heard columns aren't part of the "tree", so maybe it can't find a column parent...? – Tekito Jul 16 '13 at 20:21
  • @Tekito see how I'm doing the binding. It works that way. I'm binding against the `Column` property of the `DataGridCell`. I tested it and it works fine. – Federico Berasategui Jul 16 '13 at 20:23
  • sorry I missed that part, you're right that does work. However, I can only get it to work if the `ImageSource` is a set path. If I want to instead bind the `ImageSource` to a property inside the viewmodel of the row (using a image converter), it can't seem to find it. I want to do this because the image could change depending upon state of the row VM. Sorry maybe I should have made that clear earlier, and maybe it's not possible. But your examples are great - would vote you up 10x if I could. – Tekito Jul 16 '13 at 20:53
  • @Tekito yes, its definitely possible. Everything is possible, except to raise the dead (for now). But Maybe you can post a separate question and include the ViewModel code and all that? – Federico Berasategui Jul 16 '13 at 20:57
  • new question link below. I swapped from binded image to a textblock binded to a double, because I was attempting several variations trying to get things to work. I think the concept is the same regardless. I will switch back to image eventually. http://stackoverflow.com/questions/17687305/wpf-cell-controltemplate-binding-to-attachedproperty-binding-to-property-of-vi – Tekito Jul 16 '13 at 21:27
-3

Create a library project, add the code and markup, and the reference this new dll from the projects.

Oscar
  • 13,594
  • 8
  • 47
  • 75
  • 1
    That's a little vague for me. A library project that contains what? A data template? An inherited datagrid template column? I am clearly not a WPF wizard, despite my best efforts. – Tekito Jul 15 '13 at 20:53
  • Well, yo have some reading work: http://msdn.microsoft.com/en-us/library/bb514641(v=vs.90).aspx – Oscar Jul 15 '13 at 21:23