如何在XamDataGrid中实现导致多列记录过滤的搜索? [英] How to implement a search that causes multi-column record filtering in XamDataGrid?

查看:59
本文介绍了如何在XamDataGrid中实现导致多列记录过滤的搜索?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在XamDataGrid上实现搜索(Ctrl + F)功能.如何在网格上以编程方式调用记录过滤,以跨多个列搜索内容并仅显示与搜索匹配的列?

I am trying to implement a search (Ctrl+F) feature on a XamDataGrid. How can I programmatically invoke record filtering on the grid that searches across content in multiple columns and displays only columns that match the search?

推荐答案

DataPresenter 中的记录过滤就是这样-一种基于某些指定条件过滤记录的方法.通常,通过内置ui之一提供标准-使用 LabelIcons (仅是过滤值的下拉列表)或通过 FilterRecord (专用)特殊记录,该记录与每列的单元格一起显示,以允许选择/输入运算符和值.

The record filtering in the DataPresenter is just that - a means of the filtering the records based on some specified criteria. Normally that criteria is provided via one of the built in ui's - either using the LabelIcons which is just a drop down list of the filtered values or via the FilterRecord which is a dedicated special record that is displayed with cells for each column to allow choosing/entering an operator and value.

话虽如此,如果您愿意,可以使用代码来操纵记录过滤. FieldLayout 公开一个 RecordFilters 集合,其中RecordFilter提供条件(即匹配条件)和应对其进行匹配的字段.它也暴露在 RecordManager 之外,但这确实是为了在分层情况下促进筛选,在这种情况下,子记录的每个岛"的过滤条件都不同.

That being said the record filtering can be manipulated in code if you wish. The FieldLayout exposes a RecordFilters collection where a RecordFilter provides the conditions (i.e. the match criteria) and the field for which the match should be performed. It is also exposed off the RecordManager but this is really to facilitate filtering in hierarchical situations where the filter criteria is different for each "island" of child records.

由于要在多个字段中搜索相同的条件,因此需要为FieldLayout的 Fields 中的每个 Field 创建一个 RecordFilter 集合(或您希望对其应用的字段的任何子集).由于要进行文本搜索,因此您可能希望使用CompareCondition,其中您使用的CompareOperator为Contains,并且值将为要搜索的文本.现在,如果要在任何字段(为您创建了RecordFilter的字段)中找到该值,您都希望一条记录匹配,您还需要将FieldLayoutSettings的 RecordFiltersLogicalOperator 属性设置为 Or (默认情况下,它解析为And,因为当所有条件均与输入值匹配时,通常要与一条记录匹配).

Since you want to search multiple fields for the same criteria, you would need to create a RecordFilter for each Field in the FieldLayout's Fields collection (or whatever subset of the Fields to which you want this to be applied). Since you want to do a text search you probably want to use a ComparisonCondition where the ComparisonOperator you use is Contains and the value would be the text to search for. Now since you want a record to match if the value is found in any of the fields (for which you have created a RecordFilter) you will also need to set the FieldLayoutSettings' RecordFiltersLogicalOperator property to Or (by default this resolves to And since one typically wants to match a record when all the criteria matches the entered values).

因此,以下是基本的附加行为(在这种情况下,您将在要过滤的DataPresenter上设置名为 FilterText 的属性).此行为/属性将考虑FilterText属性的文本值来操纵DefaultFieldLayout的RecordFilter.

So to that end the following is a basic attached behavior (in this case a property named FilterText that you would set on the DataPresenter to be filtered). This behavior/property will manipulate the RecordFilters of the DefaultFieldLayout considering the text value of the FilterText property.

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名称空间.

You can then do something like the following to hook up the value of a TextBox to this attached property. Note, you would need to define an xmlns mapping for local to be whatever clr namespace you put the above class into.

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

现在,您问题的另一部分是关于仅显示包含匹配值的列.作为记录过滤器本身的一部分,这实际上是不可能的,因为记录过滤是基于某些指定条件过滤出记录,并且与隐藏/显示列/字段无关.话虽这么说,也许可以帮助最终用户了解匹配文本所在位置的一种方法是在单元格中突出显示该文本.

Now the other part of your question was about only showing the columns which contained matching values. This isn't really possible as part of the record filters itself since record filtering is about filtering out records based on some specified criteria and has no relation to hiding/showing columns/fields. That being said perhaps one way to help the end user understand where the matching text resides would be to highlight that text within the cells.

为了提供这种功能,我定义了一个派生的TextBlock,称为 HighlightTextBlock .它公开了几个属性:

To provide such functionality I defined a derived TextBlock called HighlightTextBlock. It exposes several properties:

  • RawText -这是将显示的源文本.它不能使用Text属性,因为此元素将操纵TextBlock的Inlines,并且将设置Text属性,这种属性在单向绑定的情况下会破坏绑定,而在双向绑定的情况下会将值推回源./li>
  • FilterText -用于指示要在RawText中搜索的文本.
  • FilterTextComparisonType -用于指示匹配项的字符串比较(例如,区分大小写等).
  • FilterTextForeground -用于突出显示匹配文本的前景.
  • FilterTextBackground -用于突出显示匹配文本的背景.
  • RawText - This is the source text that will be displayed. It cannot use the Text property because this element will manipulate the Inlines of the TextBlock and that will set the Text property which would break bindings in the case of one way bindings or push back values to the source in the case of two way bindings.
  • FilterText - This is used to indicate the text that is to be sought within the RawText.
  • FilterTextComparisonType - This is used to indicate the string comparison for the match (i.e. case sensitive, etc.).
  • FilterTextForeground - The foreground to be used to highlight the matching text.
  • FilterTextBackground - The background to be used to highlight the matching text.

这是该课程的代码:

    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
}

因此,您可以为要使用的编辑器更改模板,以在其渲染模板中使用此模板.例如

So then you could change the templates for the editors you are using to use this in their render template. e.g.

<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>

这篇关于如何在XamDataGrid中实现导致多列记录过滤的搜索?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆