Column.CellStyle Bug ?

Jul 1, 2009 at 1:28 AM

The reproducible behavior is from the code below;

In short:

  • a list of objects
  • connect this list to ItemsSource of DataGrid
  • use default column generation
  • the only customization is CellStyle using a static resource style that is a TextBox and a Binding of the TextBlock.Text to RelativeSource Mode="TemplatedParent" using a custom converter to extract data from my objects
  • other important thing is that the objects contains one object and a string; the problem happens on the object when I set a style on its column

Maybe that the system used by me to accomplish the styling was not the minimal right way (please check code below) or maybe that I have some mistake in my code; please feel free to let me know if there is a similar but more correct way to do the thing.

The code: ( when you see the bug try to set ShowBug var to false to show the correct behavior but unstyled )

===> Item.cs :

namespace WpfApplication1
{
    public class DummyItem
    {
        public string Name { get; set; }
    }

    public class Item
    {
        public DummyItem DummyItem { get; set; }
        public string Msg { get; set; }     
    }
}

===> MyCvt.cs :

namespace WpfApplication1
{
    public class MyCvt : IValueConverter
    {
        static Style CellStyleDummyItem;
        static Style CellStyleMsg;

        static bool ShowBug = true; // TODO : set to true to see the bug in DummyItem column

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null) return "";

            #region INIT CELL STATIC STYLE

            if (CellStyleDummyItem == null)
            {
                CellStyleDummyItem = new Style(typeof(DataGridCell));
                CellStyleDummyItem.Setters.Add(new Setter(TextBlock.FontWeightProperty, FontWeights.Bold));                
            }

            if (CellStyleMsg == null)
            {
                CellStyleMsg = new Style(typeof(DataGridCell));                
                CellStyleMsg.Setters.Add(new Setter(TextBlock.FontWeightProperty, FontWeights.Bold));                
            }

            #endregion

            DataGridCell dgc = (value as DataGridCell);

            Item item = dgc.DataContext as Item;

            if (item == null) return "";

            string property = (((value as DataGridCell).Column as DataGridBoundColumn).Binding as Binding).Path.Path;

            switch (property)
            {
                case "DummyItem":
                    if (ShowBug)
                    {
                        if (dgc.Column.CellStyle != CellStyleDummyItem)
                            dgc.Column.CellStyle = CellStyleDummyItem;
                    }

                    return item.DummyItem.Name;

                case "Msg":
                    if (dgc.Column.CellStyle != CellStyleMsg)
                        dgc.Column.CellStyle = CellStyleMsg;
                    return item.Msg;

                default:
                    return typeof(Item).GetProperty(property).GetValue(item, null);
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
        
    }

}

===> Window1.xaml :

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:wpftoolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"        
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" Height="300" Width="300">
    
    <Window.Resources>
        <local:MyCvt x:Key="myCvt"></local:MyCvt>

        <Style x:Key="cellStyle" TargetType="{x:Type wpftoolkit:DataGridCell}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <TextBlock Margin="5"
                                   TextWrapping="WrapWithOverflow">
                            <TextBlock.Text>
                                <Binding Path="." Converter="{StaticResource myCvt}">
                                    <Binding.RelativeSource>
                                        <RelativeSource Mode="TemplatedParent"></RelativeSource>
                                    </Binding.RelativeSource>
                                </Binding>
                            </TextBlock.Text>
                        </TextBlock>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>

        </Style>
    </Window.Resources>
    
    <Grid>
        <wpftoolkit:DataGrid CellStyle="{StaticResource cellStyle}"  x:Name="dg"></wpftoolkit:DataGrid>
    </Grid>
</Window>

===> Window1.xaml.cs :

namespace WpfApplication1
{
   
    public partial class Window1 : Window
    {               

        public Window1()
        {
            InitializeComponent();

            List<Item> lst = new List<Item>()
            {                
                new Item() { DummyItem = new DummyItem() { Name = "NAME01" }, Msg = "MSG01" },
                new Item() { DummyItem = new DummyItem() { Name = "NAME02" }, Msg = "MSG02" }
            };

            dg.ItemsSource = lst;
        }
    }
    
}
Jul 1, 2009 at 1:18 PM

found this workaround :

  • remove MyCvt.cs
  • replace Window1.xaml with below code

I would to like to know if there is a better mode to accomplish this simple task; in details these two :

  • it's correct switch with a datatrigger to get the column name ?
  • it's possible to avoid the trigger of the msg ( having the default content binding )

note that all this should work using a datagrid where AutoGenerateColumns == true

My target is to make the code .cs or .xaml shortest as possible.

Thanks in advice for any alternative approach.

===> Window1.xaml :

<Window.Resources>
        
        <Style x:Key="cellStyle" TargetType="{x:Type wpftoolkit:DataGridCell}">

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <TextBlock Margin="5" TextWrapping="WrapWithOverflow">
                            
                            <TextBlock.Style>
                                <Style TargetType="TextBlock">                                                                       
                                                                       
                                    <Style.Triggers>   
                                        
                                        <!-- DummyITem -->
                                        <DataTrigger Value="DummyItem">
                                            <DataTrigger.Binding>
                                                <Binding Path="Column.Binding.Path.Path">
                                                    <Binding.RelativeSource>
                                                        <RelativeSource Mode="TemplatedParent"></RelativeSource>
                                                    </Binding.RelativeSource>
                                                </Binding>
                                            </DataTrigger.Binding>
                                            <DataTrigger.Setters>
                                                <Setter Property="FontWeight" Value="Bold"></Setter>
                                                <Setter Property="Text" Value="{Binding DummyItem.Name}"></Setter>
                                            </DataTrigger.Setters>
                                        </DataTrigger>
                                        
                                        <!-- Msg -->
                                        <DataTrigger Value="Msg">
                                            <DataTrigger.Binding>
                                                <Binding Path="Column.Binding.Path.Path">
                                                    <Binding.RelativeSource>
                                                        <RelativeSource Mode="TemplatedParent"></RelativeSource>
                                                    </Binding.RelativeSource>
                                                </Binding>
                                            </DataTrigger.Binding>
                                            <DataTrigger.Setters>                                                
                                                <Setter Property="Text" Value="{Binding .}"></Setter>
                                            </DataTrigger.Setters>
                                        </DataTrigger>
                                                                                                      
                                    </Style.Triggers>
                                    
                                </Style>
                            </TextBlock.Style>

                        </TextBlock>

                    </ControlTemplate>
                </Setter.Value>
            </Setter>

        </Style>
              
    </Window.Resources>
    
    <Grid>
        <wpftoolkit:DataGrid IsReadOnly="True" CellStyle="{StaticResource cellStyle}"  x:Name="dg"></wpftoolkit:DataGrid>
    </Grid>