Highlight Search String in WPF C1DataGrid and C1Flexgrid

Users often have the requirement to change the Foreground of the partial text inside a certain cell in a grid. This could be for a scenario where it is required to highlight some text being searched. In this blog implementation, we use the grids WPF C1FlexGrid and C1Datagrid. Logic remains the same for both the grid; However, implementation is bit different. Lets begin with the UI design in XAML.


<Grid>  
 <Grid.RowDefinitions >  
   <RowDefinition Height="40" />  
   <RowDefinition Height="30"/>  
   <RowDefinition Height="Auto" />  
   <RowDefinition Height="30"/>  
   <RowDefinition Height="Auto" />  
 </Grid.RowDefinitions>  

 <Button Content="Find" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="button1" VerticalAlignment="Top" Width="75" Grid.Row="0" />  
 <TextBox Height="24" HorizontalAlignment="Left" Margin="148,9,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />  
 <TextBlock Height="23" HorizontalAlignment="Center" Margin="10,10,0,0" Name="textBlock1" Text="C1Flexgrid" VerticalAlignment="Center" Grid.Row="1" FontSize="15" FontWeight="Bold"  />  
 <c1:C1FlexGrid Name="c1FlexGrid1" Grid.Row="2"/>  
 <TextBlock Height="23" HorizontalAlignment="Center" Margin="10,10,0,0" Name="textBlock2" Text="C1DataGrid" VerticalAlignment="Center" Grid.Row="3" FontSize="15" FontWeight="Bold"  />  
 <c1:C1DataGrid Grid.Row="4" Name="c1DataGrid1" />  
</Grid>  

Now create a class that inherits from CellFactory, with a constructor that accepts a string value. Then override its CreateCellContent method.


public class MyCellFactory : C1.WPF.FlexGrid.CellFactory  
{  
  string _text;  

  public MyCellFactory()  
  { }  

  public MyCellFactory(string text)  
  {  
   _text = text;  
  }  

  public override void CreateCellContent(C1.WPF.FlexGrid.C1FlexGrid grid, Border bdr, C1.WPF.FlexGrid.CellRange range)  
  {  
    base.CreateCellContent(grid, bdr, range);  
    if (range.Row >= grid.Rows[0].Index && range.Column >= grid.Columns[0].Index)  
    {  
     //Fetch text from TextBlock  
     var tb = bdr.Child as TextBlock;  
     if (tb != null)  
     {  
      if (tb.Text.Contains(_text))  
       {  
        //Call method from static class to change text Foreground  
        Range.Find(_text, tb);  
       }  
     }  
    }  
  }  
}  

Here's code for Find() method that's from a static class. It not at all necessary to create a static class for this; I simply did it to make it reusable with C1DataGrid, later.


public static class Range  
{  
  public static void Find(string text, TextBlock _tb)  
   {  
     var tb = _tb as TextBlock;  
     var si = tb.Text.IndexOf(text);  
     // TextPointer to the position indicated by the specified offset  
     // from the beginning of the current TextPointer  
     var sp = tb.ContentStart.GetPositionAtOffset(si + 1);  
     var ep = tb.ContentStart.GetPositionAtOffset(si + text.Length + 1);  

     //Change ForeGround and FontWeight of the specified TextRange  
     TextRange tr = new TextRange(sp, ep);  
     tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);  
     tr.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);  
  }  
}  

Once done, the customized class created above needs to be set as C1Flexgrid's CellFactory. This cell factory object has to be assigned when user clicks a button after putting in some text in a textbox, to be searched.


string _text = string.Empty;  
button1.Click += (s, e) =>  
{  
  _text = this.textBox1.Text;  
  c1FlexGrid1.CellFactory = new MyCellFactory(_text);  
};  

This is all we need to do with C1Flexgrid. However C1Datagrid, doesn't have the concept of CellFactory. So, we use its LoadedCellPresenter event to fetch the TextBlock, pass this and the text being searched, to the same Range.Find() method. LoadedCellPresenter, however, gets triggered only when the cell is rendered. Hence, to make sure that the Foreground of text changes every time the user put in some different text, we need to refresh the grid, on button click.


c1DataGrid1.LoadedCellPresenter += (s, e) =>  
 {  
  //Get TextBlock using DataGridCellPresenter  
  var presenter = ((C1.WPF.DataGrid.DataGridCellPresenter)(e.Cell.Presenter));  
  var textblock = ((System.Windows.Controls.TextBlock)(((System.Windows.FrameworkElement)(presenter.Content))));  
  if (textblock != null && _text != null)  
  {  
   if (textblock.Text.Contains(_text))  
   {  
    //Call method from static class to change text Foreground  
    Range.Find(_text, textblock);  
   }  
  }  
};  

string _text = string.Empty;  

button1.Click += (s, e) =>  
 {  
  _text = this.textBox1.Text;  
  //refresh both Grids each time button is clicked for a new search  
  c1FlexGrid1.CellFactory = new MyCellFactory(_text);  
  c1DataGrid1.Refresh();  
 };  

Now, with "Web", not "Webb", being searched in both grids, this is how they look like. Download the attached samples for complete implementation. CS Sample VB Sample

GrapeCity

GrapeCity Developer Tools
comments powered by Disqus