2011-10-21 10 views
3

我想在XamDataGrid上实现一个搜索(Ctrl + F)功能。如何以编程方式在网格上调用记录过滤,以跨多列中的内容进行搜索并仅显示与搜索匹配的列?如何实现在XamDataGrid中导致多列记录过滤的搜索?

+0

假设所有的列都是DataGridTextColumns是否安全? – philt5252

+0

不,他们不是,但访问字段的内容并不像访问代码中的记录过滤那样重要。 – Kevin

回答

9

DataPresenter中的记录筛选就是这样 - 根据某些指定条件过滤记录的方法。通常,标准是通过内置的UI之一提供的 - 要么使用LabelIcons(这只是已过滤值的下拉列表),要么通过FilterRecord这是一个专用的特殊记录,显示每列的单元格以允许选择/输入运营商和价值。

如此说来记录过滤可以在代码中,如果你想进行操作。所述FieldLayout暴露出RecordFilters集合,其中一个RecordFilter的提供条件(即匹配标准)和所述匹配应该执行领域。它也暴露在RecordManager但这实际上是为了便于在分层情况下过滤,其中过滤标准对于子记录的每个“岛”都是不同的。

既然你想搜索多个字段相同的标准,你就需要在FieldLayout的领域创造一个RecordFilter的每个现场集合(或任何你想要的字段的子集到这被应用)。由于您想要进行文本搜索,因此您可能希望在使用ComparisonOperator的Contains中使用ComparisonCondition,并且该值将是要搜索的文本。现在既然你想要一个记录匹配,如果在任何一个字段中找到该值(为此你创建了一个RecordFilter),你还需要将FieldLayoutSettings'RecordFiltersLogicalOperator属性设置为(默认情况下这将解决为并且因为当所有标准匹配输入的值时通常都希望匹配记录)。

因此,为此目的,下面是一个基本的附加行为(在这种情况下,您将在DataPresenter上设置要过滤的属性FilterText)。考虑到FilterText属性的文本值,此行为/属性将操纵DefaultFieldLayout的RecordFilters。

public static class DataPresenterHelpers 
{ 
    #region FilterText 

    /// <summary> 
    /// FilterText Attached Dependency Property 
    /// </summary> 
    public static readonly DependencyProperty FilterTextProperty = 
     DependencyProperty.RegisterAttached("FilterText", typeof(string), typeof(DataPresenterHelpers), 
      new FrameworkPropertyMetadata((string)null, 
       new PropertyChangedCallback(OnFilterTextChanged))); 

    /// <summary> 
    /// Gets the text to be used to filter the DataPresenter on which the property was set. 
    /// </summary> 
    public static string GetFilterText(DependencyObject d) 
    { 
     return (string)d.GetValue(FilterTextProperty); 
    } 

    /// <summary> 
    /// Sets the filter text on the DataPresenter that should be used to manipulate the RecordFilters of the specified DataPresenter 
    /// </summary> 
    public static void SetFilterText(DependencyObject d, string value) 
    { 
     d.SetValue(FilterTextProperty, value); 
    } 

    /// <summary> 
    /// Handles changes to the FilterText property. 
    /// </summary> 
    private static void OnFilterTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var dp = d as DataPresenterBase; 

     if (dp.DefaultFieldLayout != null) 
     { 
      dp.DefaultFieldLayout.RecordFilters.Clear(); 
      dp.DefaultFieldLayout.Settings.RecordFiltersLogicalOperator = LogicalOperator.Or; 

      foreach (var field in dp.DefaultFieldLayout.Fields) 
      { 
       var filter = new RecordFilter(); 
       filter.Field = field; 
       filter.Conditions.Add(new ComparisonCondition(ComparisonOperator.Contains, e.NewValue)); 
       dp.DefaultFieldLayout.RecordFilters.Add(filter); 
      } 
     } 
    } 

    #endregion //FilterText 
} 

然后,您可以执行如下操作,将TextBox的值连接到此附加属性。请注意,您需要为local定义一个xmlns映射,无论您将上述类放入哪个clr命名空间。

<TextBox DockPanel.Dock="Top" x:Name="txtFilter" /> 
    <igDP:XamDataGrid 
     x:Name="grid" 
     BindToSampleData="True" 
     local:DataPresenterHelpers.FilterText="{Binding ElementName=txtFilter, Path=Text}"> 
    </igDP:XamDataGrid> 

现在问题的另一部分是关于只显示包含匹配值的列。由于记录过滤是根据某些指定的标准过滤掉记录,并且与隐藏/显示列/字段无关,所以这不是真正可以作为记录过滤器本身的一部分。也就是说,可能帮助最终用户了解匹配文本所在位置的一种方法是突出显示单元格内的文本。

为了提供这样的功能,我定义了派生的TextBlock,名为HighlightTextBlock。它暴露了几个属性:

  • RawText - 这是将要显示的源文本。它不能使用Text属性,因为这个元素将操纵TextBlock的Inline,并且将设置Text属性,在双向绑定的情况下,它将在单向绑定或将值推回到源的情况下破坏绑定。
  • FilterText - 用于指示要在RawText中查找的文本。
  • FilterTextComparisonType - 用于指示匹配的字符串比较(即区分大小写等)。
  • FilterTextForeground - 用于突出显示匹配文本的前景。
  • FilterTextBackground - 用于突出显示匹配文本的背景。

下面是类代码:

public class HighlightTextBlock 
    : TextBlock 
{ 
    #region Member Variables 

    private DispatcherOperation _pendingUpdate; 

    #endregion //Member Variables 

    #region Constructor 
    static HighlightTextBlock() 
    { 
    } 

    /// <summary> 
    /// Initializes a new <see cref="HighlightTextBlock"/> 
    /// </summary> 
    public HighlightTextBlock() 
    { 

    } 
    #endregion //Constructor 

    #region Base class overrides 

    #region OnInitialized 
    protected override void OnInitialized(EventArgs e) 
    { 
     if (_pendingUpdate != null) 
      this.UpdateInlines(null); 

     base.OnInitialized(e); 
    } 
    #endregion //OnInitialized 

    #endregion //Base class overrides 

    #region Properties 

    #region FilterText 

    /// <summary> 
    /// Identifies the <see cref="FilterText"/> dependency property 
    /// </summary> 
    public static readonly DependencyProperty FilterTextProperty = DependencyProperty.Register("FilterText", 
     typeof(string), typeof(HighlightTextBlock), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCriteriaChanged))); 

    /// <summary> 
    /// Returns or sets the text that should be highlighted 
    /// </summary> 
    /// <seealso cref="FilterTextProperty"/> 
    [Description("Returns or sets the text that should be highlighted")] 
    [Category("Behavior")] 
    [Bindable(true)] 
    public string FilterText 
    { 
     get 
     { 
      return (string)this.GetValue(HighlightTextBlock.FilterTextProperty); 
     } 
     set 
     { 
      this.SetValue(HighlightTextBlock.FilterTextProperty, value); 
     } 
    } 

    #endregion //FilterText 

    #region FilterTextBackground 

    /// <summary> 
    /// Identifies the <see cref="FilterTextBackground"/> dependency property 
    /// </summary> 
    public static readonly DependencyProperty FilterTextBackgroundProperty = DependencyProperty.Register("FilterTextBackground", 
     typeof(Brush), typeof(HighlightTextBlock), new FrameworkPropertyMetadata(Brushes.Yellow, new PropertyChangedCallback(OnCriteriaChanged))); 

    /// <summary> 
    /// Returns or sets the background of the matching text. 
    /// </summary> 
    /// <seealso cref="FilterTextBackgroundProperty"/> 
    [Description("Returns or sets the background of the matching text.")] 
    [Category("Behavior")] 
    [Bindable(true)] 
    public Brush FilterTextBackground 
    { 
     get 
     { 
      return (Brush)this.GetValue(HighlightTextBlock.FilterTextBackgroundProperty); 
     } 
     set 
     { 
      this.SetValue(HighlightTextBlock.FilterTextBackgroundProperty, value); 
     } 
    } 

    #endregion //FilterTextBackground 

    #region FilterTextComparisonType 

    /// <summary> 
    /// Identifies the <see cref="FilterTextComparisonType"/> dependency property 
    /// </summary> 
    public static readonly DependencyProperty FilterTextComparisonTypeProperty = DependencyProperty.Register("FilterTextComparisonType", 
     typeof(StringComparison), typeof(HighlightTextBlock), new FrameworkPropertyMetadata(StringComparison.CurrentCultureIgnoreCase, new PropertyChangedCallback(OnCriteriaChanged))); 

    /// <summary> 
    /// Returns or sets the StringComparison when locating the FilterText within the RawText. 
    /// </summary> 
    /// <seealso cref="FilterTextComparisonTypeProperty"/> 
    [Description("Returns or sets the StringComparison when locating the FilterText within the RawText.")] 
    [Category("Behavior")] 
    [Bindable(true)] 
    public StringComparison FilterTextComparisonType 
    { 
     get 
     { 
      return (StringComparison)this.GetValue(HighlightTextBlock.FilterTextComparisonTypeProperty); 
     } 
     set 
     { 
      this.SetValue(HighlightTextBlock.FilterTextComparisonTypeProperty, value); 
     } 
    } 

    #endregion //FilterTextComparisonType 

    #region FilterTextForeground 

    /// <summary> 
    /// Identifies the <see cref="FilterTextForeground"/> dependency property 
    /// </summary> 
    public static readonly DependencyProperty FilterTextForegroundProperty = DependencyProperty.Register("FilterTextForeground", 
     typeof(Brush), typeof(HighlightTextBlock), new FrameworkPropertyMetadata(Brushes.Black, new PropertyChangedCallback(OnCriteriaChanged))); 

    /// <summary> 
    /// Returns or sets the brushed used for the foreground of the matching text. 
    /// </summary> 
    /// <seealso cref="FilterTextForegroundProperty"/> 
    [Description("Returns or sets the brushed used for the foreground of the matching text.")] 
    [Category("Behavior")] 
    [Bindable(true)] 
    public Brush FilterTextForeground 
    { 
     get 
     { 
      return (Brush)this.GetValue(HighlightTextBlock.FilterTextForegroundProperty); 
     } 
     set 
     { 
      this.SetValue(HighlightTextBlock.FilterTextForegroundProperty, value); 
     } 
    } 

    #endregion //FilterTextForeground 

    #region RawText 

    /// <summary> 
    /// Identifies the <see cref="RawText"/> dependency property 
    /// </summary> 
    public static readonly DependencyProperty RawTextProperty = DependencyProperty.Register("RawText", 
     typeof(string), typeof(HighlightTextBlock), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCriteriaChanged))); 

    /// <summary> 
    /// Returns or sets the base string that will be displayed by the element. 
    /// </summary> 
    /// <seealso cref="RawTextProperty"/> 
    [Description("Returns or sets the base string that will be displayed by the element.")] 
    [Category("Behavior")] 
    [Bindable(true)] 
    public string RawText 
    { 
     get 
     { 
      return (string)this.GetValue(HighlightTextBlock.RawTextProperty); 
     } 
     set 
     { 
      this.SetValue(HighlightTextBlock.RawTextProperty, value); 
     } 
    } 

    #endregion //RawText 

    #endregion //Properties 

    #region Methods 

    #region OnCriteriaChanged 
    private static void OnCriteriaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var instance = d as HighlightTextBlock; 

     if (instance._pendingUpdate == null) 
     { 
      instance._pendingUpdate = instance.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new SendOrPostCallback(instance.UpdateInlines), new object[] { null }); 
     } 
    } 
    #endregion //OnCriteriaChanged 

    #region UpdateInlines 
    private void UpdateInlines(object param) 
    { 
     _pendingUpdate = null; 

     string filterText = this.FilterText; 
     string text = this.RawText; 
     var inlines = this.Inlines; 

     try 
     { 
      inlines.Clear(); 

      if (string.IsNullOrEmpty(filterText)) 
      { 
       inlines.Add(text); 
       return; 
      } 

      var foreground = this.FilterTextForeground; 
      var background = this.FilterTextBackground; 
      var comparison = this.FilterTextComparisonType; 
      var newInlines = new List<Inline>(); 
      int filterTextLen = filterText.Length; 

      int start = 0; 

      do 
      { 
       int end = text.IndexOf(filterText, start, comparison); 

       string substr = text.Substring(start, (end < 0 ? text.Length : end) - start); 
       newInlines.Add(new Run(substr)); 

       if (end < 0) 
        break; 

       var run = new Run(text.Substring(end, filterTextLen)); 

       // note we could bind and not rebuild when the background/foreground 
       // changes but that doesn't seem likely to happen and would add more 
       // overhead than just referencing the value directly 
       if (null != foreground) 
        run.Foreground = foreground; 

       if (null != background) 
        run.Background = background; 

       newInlines.Add(run); 

       start = end + filterTextLen; 
      } while (true); 

      inlines.AddRange(newInlines); 
     } 
     finally 
     { 
      if (_pendingUpdate != null) 
      { 
       _pendingUpdate.Abort(); 
       _pendingUpdate = null; 
      } 
     } 
    } 
    #endregion //UpdateInlines 

    #endregion //Methods 
} 

,那么你可以改变你使用他们的渲染模板用这个编辑模板。例如

<Window x:Class="WpfApplication6.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:igDP="http://infragistics.com/DataPresenter" 
    xmlns:igEditors="http://infragistics.com/Editors" 
    xmlns:local="clr-namespace:WpfApplication6" 
    Title="MainWindow" Height="350" Width="525"> 
<DockPanel> 
    <TextBox DockPanel.Dock="Top" x:Name="txtFilter" /> 
    <igDP:XamDataGrid 
     x:Name="grid" 
     BindToSampleData="True" 
     local:DataPresenterHelpers.FilterText="{Binding ElementName=txtFilter, Path=Text}"> 
     <igDP:XamDataGrid.Resources> 
      <Style TargetType="igEditors:XamTextEditor"> 
       <Setter Property="Template"> 
        <Setter.Value> 
         <ControlTemplate TargetType="igEditors:XamTextEditor"> 
          <Border x:Name="MainBorder" 
           Background="{TemplateBinding Background}" 
           BorderBrush="{TemplateBinding BorderBrush}" 
           BorderThickness="{TemplateBinding BorderThickness}" 
           SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
           > 
           <local:HighlightTextBlock 
            Margin="{TemplateBinding Padding}" 
            FilterText="{Binding Path=Host.DataPresenter.(local:DataPresenterHelpers.FilterText), RelativeSource={RelativeSource TemplatedParent}}" 
            RawText="{TemplateBinding DisplayText}" 
            TextWrapping="{TemplateBinding TextWrapping}" 
            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
            VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
            TextAlignment="{TemplateBinding TextAlignmentResolved}" 
            /> 
          </Border> 
         </ControlTemplate> 
        </Setter.Value> 
       </Setter> 
      </Style> 
     </igDP:XamDataGrid.Resources> 
    </igDP:XamDataGrid> 
</DockPanel> 

1

,我会解决这个问题。将代码添加到搜索按钮单击事件(或其他触发机制),并直接在代码中执行搜索,使用LINQ或记录被记录比较,无论在效果最好的方式你的情况(这将有助于知道如何填充列和行)。

一旦你有一组记录和/或具有相应结果列,隐瞒没有命中列。为了简化流程,我将创建一个应该隐藏的列集合,然后有一个处理每列的方法。该方法将搜索列数据,并在第一个匹配的符号处,从将要隐藏的列列表中移除列,并停止处理列中的记录。

在过程结束时,列表将包含列隐藏起来,然后你可以用它来执行实际隐藏的名单。

这种机制还可以扩展到隐藏没有任何匹配行,假设有多个行。我会通过在处理第一列时隐藏每个没有匹配的行并支持这一点,然后在发现匹配的后续列中取消隐藏它们。

1

谢谢你的分享。我也会分享。 我用这个例子来处理一个更简单的情况。 我只是想在一列上设置一个过滤器。 这就是:

Imports Infragistics.Windows.DataPresenter 
Imports Infragistics.Windows.Controls 

Public Class DataPresenterFilter 

Public Shared Function GetTitleFilter(ByVal element As DependencyObject) As String 
    If element Is Nothing Then 
     Throw New ArgumentNullException("element") 
    End If 
    Return element.GetValue(TitleFilter) 
End Function 

Public Shared Sub SetTitleFilter(ByVal element As DependencyObject, 
     ByVal value As String) 
    If element Is Nothing Then 
     Throw New ArgumentNullException("element") 
    End If 
    element.SetValue(TitleFilter, value) 
End Sub 

Public Shared ReadOnly TitleFilter As _ 
     DependencyProperty = DependencyProperty.RegisterAttached("TitleFilter", _ 
    GetType(String), GetType(DataPresenterFilter), _ 
    New FrameworkPropertyMetadata(String.Empty, 
    New PropertyChangedCallback(AddressOf OnTitleFilterChanged))) 

Private Shared Sub OnTitleFilterChanged(d As DependencyObject, 
     e As DependencyPropertyChangedEventArgs) 
    Dim dp As DataPresenterBase = CType(d, DataPresenterBase) 

    If (Not dp.DefaultFieldLayout Is Nothing) Then 
     Dim Filter As RecordFilter = New RecordFilter() 
     Filter.FieldName = "Title" 
     Filter.Conditions.Add(
      New ComparisonCondition(ComparisonOperator.Contains, e.NewValue)) 
     dp.DefaultFieldLayout.RecordFilters.Remove(
      dp.DefaultFieldLayout.RecordFilters.Item("Title")) 
     dp.DefaultFieldLayout.RecordFilters.Add(Filter) 
    End If 
End Sub 

End Class 

而XAML:

xmlns:Inv="clr-namespace:InventoryApp" 

<TextBox Height="23" Margin="0,2,0,2" x:Name="tbTitle" /> 

<igDP:XamDataPresenter Name="xamDataPresenterPublicationCollection" 
    DataSource="{Binding Source={StaticResource PublicationCollectionViewSource}, 
    UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True" 
    ActiveDataItem="{Binding Path=PublicationModel, Mode=OneWay}" 
    Inv:DataPresenterFilter.TitleFilter="{Binding ElementName=tbTitle, Path=Text}"/> 

这完全适用于我。

0

我正在寻找一个非常基本的解决方案,所以我会分享一下为我工作的东西。这里是我的C#代码隐藏代码过滤器应用于列:

grdConnectionManagerEntries.FieldLayouts[0].RecordFilters.Clear(); // Clear any existing filters before applying this one. 
grdConnectionManagerEntries.FieldLayouts[0].RecordFilters.Add(new RecordFilter(new Field("Name"))); 
grdConnectionManagerEntries.FieldLayouts[0].RecordFilters[0].Conditions.Add(new Infragistics.Windows.Controls.ComparisonCondition(Infragistics.Windows.Controls.ComparisonOperator.Contains, connectionName)); 

这里grdConnectionManagerEntries是我XamDataGrid,我有一个字段(即列)称为名称,而我加入包含筛选器,它将按给定的筛选连接名称

由于某些原因,过滤器文本只在XamDataGrid的用户界面中显示,当我使用包含比较运算符;我猜是因为那是该列上的默认筛选器运算符。所以有些事情我做得不对,但是因为我想使用Contains运算符,所以它已经用于我的目的。

相关问题