Wednesday, June 8, 2016

WPF DataGrid Excel Comment Indicator

Here's how to add a comment indicator to a DataGridCell similar to an Excel functionality using MultiValueConverter. See codefiles and screenshot below.
XAML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<Window x:Class="WPFExcelCommentIndicator.DataGridCellCommentIndicator"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:WPFExcelCommentIndicator"
        Title="DataGridCellCommentIndicator"  Height="350" Width="500" WindowStartupLocation="CenterScreen" >
    <Window.Resources>
        <src:DataGridCellComment x:Key="DataGridCellComment" />
        <src:StudentList x:Key="StudentListData" />
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="225"/>
            <RowDefinition Height="50"/>
            <RowDefinition Height="225"/>
        </Grid.RowDefinitions>       
        <DataGrid Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" AutoGenerateColumns="False" CanUserResizeColumns="True"
                  CanUserAddRows="False" SelectionMode="Single" SelectionUnit="Cell" ItemsSource ="{Binding Path=StudentItems}" 
                  Name="dgStudents">
            <DataGrid.Resources>
                <DataTemplate x:Key="DateTemplate">
                    <Canvas x:Name="pnlContainer" Height="25">
                        <TextBlock x:Name="txtAge" IsEnabled="False" Foreground="Green" Text="{Binding Path=Age, Mode=TwoWay}" />
                        <Polygon IsEnabled="False" ClipToBounds="False" VerticalAlignment="Top" x:Name="polyExcel">
                            <Polygon.Fill>
                                <SolidColorBrush Color="Red" />
                            </Polygon.Fill>
                            <Polygon.Points>
                                <MultiBinding Converter="{StaticResource DataGridCellComment}">
                                    <Binding RelativeSource="{RelativeSource Mode=Self}"/>
                                    <Binding Path="Age"></Binding>
                                </MultiBinding>
                            </Polygon.Points>
                        </Polygon>
                    </Canvas>      
                </DataTemplate>
            </DataGrid.Resources>
            <DataGrid.CellStyle>
                <Style TargetType="DataGridCell">
                    <EventSetter Event="PreviewMouseDown" Handler="CheckPolygon"/>
                    <EventSetter Event="SizeChanged" Handler="Cell_SizedChanged" />
                </Style>
            </DataGrid.CellStyle>
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding Path=ID}" Width="100" IsReadOnly="True" />
                <DataGridTemplateColumn Header="Age" CellTemplate="{StaticResource DateTemplate}" Width="100" >
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" Width="100"  IsReadOnly="True" />
                <DataGridTextColumn Header="Address" Binding="{Binding Path=Address}" Width="*" IsReadOnly="True"/> 
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Code Behind
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public partial class DataGridCellCommentIndicator : Window
{
    public DataGridCellCommentIndicator()
    {
        InitializeComponent();
        this.DataContext = this;
    }
    
    public StudentList StudentItems
    {
        get { return new StudentList(); }
    }
    
    void CheckPolygon(object sender, RoutedEventArgs e)
    {
        
    }
    
    private void Cell_SizedChanged(object sender, SizeChangedEventArgs e)
    {
        DataGridCell cell = (DataGridCell)sender;
        if (cell.Column.Header.ToString().Equals("Age"))
        {
            Polygon poly = ControlExtensions.GetVisualChild<Polygon>(cell);
            TextBlock textAge = ControlExtensions.GetVisualChild<TextBlock>(cell);
 
            if (cell.ActualWidth != 100 && Convert.ToInt32(textAge.Text) >= 30)
            {
                PointCollection myPointCollection = new PointCollection();
                myPointCollection.Add(new Point(cell.ActualWidth - 13, cell.ActualHeight - (cell.ActualHeight + 1)));
                myPointCollection.Add(new Point(cell.ActualWidth - 2, cell.ActualHeight - (cell.ActualHeight + 1)));
                myPointCollection.Add(new Point(cell.ActualWidth - 2, cell.ActualHeight - 20));
                poly.Points = myPointCollection;
            }
        }
    }
}   

DataGridCell Comment Class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DataGridCellComment : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        DataGridCell cell = ((Polygon)values[0]).GetAncestor<DataGridCell>();
        TextBlock textAge = ControlExtensions.GetVisualChild<TextBlock>(cell);
        PointCollection myPointCollection = new PointCollection();
        myPointCollection.Add(new Point(cell.ActualWidth - 13, cell.ActualHeight - (cell.ActualHeight + 1)));
        myPointCollection.Add(new Point(cell.ActualWidth - 2, cell.ActualHeight - (cell.ActualHeight + 1)));
        myPointCollection.Add(new Point(cell.ActualWidth - 2, cell.ActualHeight - 20));
 
        if (System.Convert.ToInt32(textAge.Text) >= 30)
        {
            return myPointCollection;
        }
        else
        {
            return null;
        } 
    }
 
    public object[] ConvertBack(object value, Type[] targetTypes, 
          object parameter, CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }
}

Control Extension Method
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public static T GetVisualChild<T>(Visual parent) 
     where T : Visual
{
    T child = default(T);
    int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < numVisuals; i++)
    {
        Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null)
        {
            child = GetVisualChild<T>
            (v);
        }
        if (child != null)
        {
            break;
        }
    }
    return child;
}

StudentList and Student Classes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class StudentList : ObservableCollection<Student>
{
    public StudentList()
    {
        Add(new Student { ID = 1, Age = 29, Name = "Mike", Address = "Cebu" });
        Add(new Student { ID = 2, Age = 28, Name = "Phil", Address = "Cebu" });
        Add(new Student { ID = 3, Age = 23, Name = "Chelsy", Address = "Leyte" });
        Add(new Student { ID = 4, Age = 33, Name = "Jeff", Address = "Leyte" });
        Add(new Student { ID = 5, Age = 32, Name = "Greg", Address = "Cebu" });
        Add(new Student { ID = 6, Age = 28, Name = "Howard", Address = "Bohol" });
        Add(new Student { ID = 7, Age = 27, Name = "Grant", Address = "Bohol" });
    }
}
 
public class Student
{
    public int ID { get; set; }
    public int Age { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
}

Screenshot

0 comments:

Post a Comment