How to implement a search that causes multi-column record filtering in XamDataGrid?

How to implement a search that causes multi-column record filtering in XamDataGrid?



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

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>

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

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}"/>

Comments

Popular posts from this blog

How to build unlimited levels menu through PHP and MySQL

How to build a map of the frequency with which characters occur in a file in OCaml?

How to create timed pop-up windows using MVVM?