דפים

12/24/12

Parse TabDelimited text file

public static System.Data.DataTable ReadTabDelimeted(string path)
        {
            StreamReader sr = new StreamReader(path);
            System.Data.DataTable dt = new System.Data.DataTable();
            try
            {               
                if (sr.Peek() > 0)
                {
                    string[] headers = sr.ReadLine().Split('\t');
                    foreach (string header in headers)
                        dt.Columns.Add(header);
                }              
                while (sr.Peek() > 0)
                {
                    string[] cells = sr.ReadLine().Split('\t');
                    DataRow dr = dt.NewRow();
                    for (int i = 0; i < cells.Length; i++)
                    {
                        dr[i] = cells[i];
                    }
                    dt.Rows.Add(dr);
                }
            }
            finally
            {
                sr.Close();
            }
         
            return dt;
        }

3/14/12

WPF DataGrid - Styling Column Headers the simple way

WPF DataGrid has a built in column sorting feature that includes a display of sort arrows inside the sorted column's header.

Unfortunately once the the column's header default background is defined using style the sorting arrows are not renderd.
This is a known issue with WPF datagrid.

To solve this issue we have to define a ControlTemplate to the column's header.

Searching for solutions across the internet i found some several rellevant samples.
These samples where big, complex and confusing, often referencing external resources and sometimes refering to the .Net 3.5 WPF  Toolkit's DataGrid rather then the the .Net 4 WPF DataGrid.
Getting those samples to actually work was quite difficult.

I finally suceeded after modifying  xaml code from Jaime Rordriguez's article http://blogs.msdn.com/b/jaimer/archive/2009/01/20/styling-microsoft-s-wpf-datagrid.aspx

In this post i intend to present a minimalist solution, which will be easy to extend.

The most basic template:

<Style x:Key="DatagridColumnHeaderStyle_Basic"            
         TargetType="{x:Type DataGridColumnHeader}">
            <Setter Property="Background" Value="Azure" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                        <Grid Name="HedearGrid" Background="AliceBlue" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>                         
                            <ContentPresenter  Margin="6,3,6,3" VerticalAlignment="Center" Grid.Column="0" />
                            <Path x:Name="SortArrow" Visibility="Collapsed" Data="M0,0 L1,0 0.5,1 z" Stretch="Fill"
                            Grid.Column="1" Width="8" Height="6" Fill="Blue" Margin="0,0,8,0"
                            VerticalAlignment="Center" RenderTransformOrigin="0.5,0.4" />
                            <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Cursor="SizeWE"
                                   Grid.Column="1"  >
                                <Thumb.Style>
                                    <Style TargetType="{x:Type Thumb}">
                                        <Setter Property="Width" Value="2" />


                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate TargetType="{x:Type Thumb}">
                                                    <Border Background="Transparent"/>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Thumb.Style>
                            </Thumb>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="SortDirection" Value="Ascending">
                                <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                                <Setter TargetName="SortArrow" Property="RenderTransform">
                                    <Setter.Value>
                                        <RotateTransform Angle="180" />
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                            <Trigger Property="SortDirection" Value="Descending">
                                <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>



Note: A real world scenario will of course include predefined brushes instead of named colors for backgrounds.

We basically have 3 controls inside the template:
  1. ContentPresenter  that automatically stores the header text.
  2. Sort arrow.
  3. Thumb object.
Without the Thumb will lose the resizing capablity of the header.
For fixed headers it is not necessary.

Notice that i added a ControlTemplate to the thumb.
It is needed to hide the graphical appearence of the thumb without affecting it's functionality.
The thumb is ugly and usually does not fit the look and feel of the datagrid.

Basic Template With Border:

One problem with former template is the lack of border lines in the column header.
This might be bothering if we use gridlines inside the datagrid - expecially vertical gridlines.
Vertical border lines inside the header also act as visual resizer bars.

We add a border to the template very simplly:

<Border x:Name="BackgroundBorder" BorderThickness="0,0,1,1" BorderBrush="Black" Grid.ColumnSpan="2" />

The complete style:

 <Style x:Key="DatagridColumnHeaderStyle_Border" 
         TargetType="{x:Type DataGridColumnHeader}">
            <Setter Property="Background" Value="Azure" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                        <Grid Name="HedearGrid" Background="AliceBlue" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>


                            <Border x:Name="BackgroundBorder" BorderThickness="0,0,1,1" BorderBrush="Black" Grid.ColumnSpan="2" />
                            <ContentPresenter  Margin="6,3,6,3" VerticalAlignment="Center" Grid.Column="0" />


                            <Path x:Name="SortArrow" Visibility="Collapsed" Data="M0,0 L1,0 0.5,1 z" Stretch="Fill" 
                            Grid.Column="1" Width="8" Height="6" Fill="Blue" Margin="0,0,8,0" 
                            VerticalAlignment="Center" RenderTransformOrigin="0.5,0.4" />
                            <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Cursor="SizeWE" Grid.Column="1" >
                                <Thumb.Style>
                                    <Style TargetType="{x:Type Thumb}">
                                        <Setter Property="Width" Value="2" />
                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate TargetType="{x:Type Thumb}">
                                                    <Border Background="Transparent"/>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Thumb.Style>
                            </Thumb>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="SortDirection" Value="Ascending">
                                <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                                <Setter TargetName="SortArrow" Property="RenderTransform">
                                    <Setter.Value>
                                        <RotateTransform Angle="180" />
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                            <Trigger Property="SortDirection" Value="Descending">
                                <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
 </Style>


Basic Template With Mouse Effects:


Final enhancements to the basic sample include mouseover and mouseclick effects.
These are added by adding triggers to the ControlTemplate

<Trigger Property="IsMouseOver" Value="true">
          <Setter Property="Background" TargetName="HedearGrid"  Value="LightBlue" />
 </Trigger>


<Trigger Property="IsPressed" Value="true">
           <Setter Property="Background" TargetName="HedearGrid"   Value="#21ffaabb" />
</Trigger>

In this code i am switching the column backgrounds on mousever and mouseclick.
Of course other properties such as the border color of font color can be changed in the same manner.

The complete enhanced style:

<Style x:Key="DatagridColumnHeaderStyle_Effects" 
         TargetType="{x:Type DataGridColumnHeader}">
           
            <Setter Property="Cursor" Value="Hand" />
            <Setter Property="Background" Value="Azure" />
          
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                        <Grid Name="HedearGrid" Background="AliceBlue" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <Border x:Name="BackgroundBorder" BorderThickness="0,0,1,1"  BorderBrush="Black" Grid.ColumnSpan="2" />
                            <ContentPresenter  Margin="6,3,6,3" VerticalAlignment="Center" Grid.Column="0" />
                            <Path x:Name="SortArrow" Visibility="Collapsed" Data="M0,0 L1,0 0.5,1 z" Stretch="Fill" 
                            Grid.Column="1" Width="8" Height="6" Fill="Blue" Margin="0,0,8,0" 
                            VerticalAlignment="Center" RenderTransformOrigin="0.5,0.4" />                            
                            <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Cursor="SizeWE" 
                                   Grid.Column="1" >
                                <Thumb.Style>
                                    <Style TargetType="{x:Type Thumb}">
                                        <Setter Property="Width" Value="2" />


                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate TargetType="{x:Type Thumb}">
                                                    <Border Background="Transparent"/>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Thumb.Style>
                            </Thumb>
                        </Grid>
                        <ControlTemplate.Triggers>                          
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" TargetName="HedearGrid"  Value="LightBlue" />
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Background" TargetName="HedearGrid"   Value="#21ffaabb" />
                            </Trigger>


                            <Trigger Property="SortDirection" Value="Ascending">
                                <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                                <Setter TargetName="SortArrow" Property="RenderTransform">
                                    <Setter.Value>
                                        <RotateTransform Angle="180" />
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                            <Trigger Property="SortDirection" Value="Descending">
                                <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>