4
\$\begingroup\$

I need better logic that would allow this search to perform quicker and more efficiently.

In XAML, I have a DataGrid and a TextBox. Both need to be present.

I'd prefer that as the user types in the TextBox, the Grid is filtered by one or more columns. Not that the user needs to click a "Submit" button (unless this is the only way).

My current logic takes many seconds to complete, likely because of the binding/rebinding of the UI.

Here is my logic:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
    var textBox = sender as TextBox;
    _filteredView = CollectionViewSource.GetDefaultView(MainGrid.DataContext);
    _filteredView.Filter = x => ((QueueData)x).ImageCode.Contains(textBox.Text) || ((QueueData)x).Product.Contains(textBox.Text);

    MainGrid.DataContext = ((System.Windows.Data.ListCollectionView)_filteredView).SourceCollection;
}

Below is my xaml - txtSearch is the TextBox:

<UserControl x:Class="Wip.Presentation.ManagerWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             TextElement.FontWeight="Medium"
             TextElement.FontSize="14"
             TextOptions.TextFormattingMode="Ideal" 
             TextOptions.TextRenderingMode="Auto"
             TextElement.Foreground="{DynamicResource MaterialDesignBody}"
             Background="{DynamicResource MaterialDesignPaper}"
             FontFamily="{StaticResource MaterialDesignFont}"
             mc:Ignorable="d" Height="auto" Width="auto">
    <UserControl.Resources>
        <Style x:Key="LeftCellStyle" TargetType="{x:Type DataGridCell}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridCell}">
                        <Grid Background="{TemplateBinding Background}">
                            <ContentPresenter HorizontalAlignment="Left"
                                              VerticalAlignment="Center"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>

    <ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Auto" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
        <Grid Background="Transparent" FocusManager.FocusedElement="{Binding ElementName=txtSearch}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <!-- SEARCH -->
            <Label HorizontalAlignment="Left" Grid.Row="0" Name="lblSearch" Height="30" Width="150" Margin="10,0,0,0" >SEARCH (F5 to clear)</Label>
            <TextBox HorizontalAlignment="Left" Grid.Row="0" Name="txtSearch" Height="30" Width="200" Margin="160,0,0,0" TextChanged="TextBox_TextChanged" CharacterCasing="Upper"/>

            <DataGrid Grid.Row="1" Grid.ColumnSpan="2" Name="MainGrid" ItemsSource="{Binding}"
              Sorting="MainGrid_Sorting"
              SelectionChanged="MainGrid_SelectionChanged"
              MouseDoubleClick="MainGrid_MouseDoubleClick"
              ColumnWidth="Auto"
              AlternationCount="2"
              AlternatingRowBackground="DimGray"
              AutoGenerateColumns="False"
              AreRowDetailsFrozen="True" Margin="0,0,0,0">

                <DataGrid.Columns>
                    <DataGridTextColumn Header="CMP" Width="50" Binding="{Binding Company, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}"  SortMemberPath="(Company)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="DIV" Width="50"  Binding="{Binding Division, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Division)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="CUS" Width="75" Binding="{Binding CustomerNumber, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(CustomerNumber)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="FORMAT" Width="75" Binding="{Binding ImageCode, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(ImageCode)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="SEQ" Width="50" Binding="{Binding Sequence, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Sequence)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="ORDER #" Width="75"  Binding="{Binding OrderNumber, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(OrderNumber)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="LOGO" Width="225" Binding="{Binding Logo, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Logo)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="Hold Code" Width="100" Binding="{Binding HoldCode, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(HoldCode)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="Order Type" Width="85" Binding="{Binding Dhotp, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Dhotp)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="Comments" Width="175" Binding="{Binding Comments, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Comments)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="IMPORT DATE" Width="160" Binding="{Binding ImportDate, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(ImportDate)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="PRODUCT" Width="110" Binding="{Binding Product, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Product)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="WORKCNTR" Width="110" Binding="{Binding Product, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(WorkCenter)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="PROCESS TYPE" Width="100"  Binding="{Binding ProcessType, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(ProcessType)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn Header="PIM EMB JOB" Width="105"  Binding="{Binding PimEmbJobDisplay, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(PimEmbJob)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn x:Name="dgcThreadColor" Header="THREAD COLOR" Width="105"  Binding="{Binding Ink, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Ink)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn x:Name="dgcStitchCnt" Header="STITCH CNT" Width="105"  Binding="{Binding StitchCount, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(StitchCount)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                    <DataGridTextColumn x:Name="dgcPieces" Header="PIECES" Width="105"  Binding="{Binding Pieces, Mode=OneWay}" CellStyle="{StaticResource LeftCellStyle}" SortMemberPath="(Pieces)" IsReadOnly="True">
                        <DataGridTextColumn.HeaderStyle>
                            <Style TargetType="DataGridColumnHeader">
                                <Setter Property="Background" Value="#FF666666" />
                                <Setter Property="VerticalAlignment" Value="Center" />
                            </Style>
                        </DataGridTextColumn.HeaderStyle>
                    </DataGridTextColumn>

                </DataGrid.Columns>
                <DataGrid.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Mark SLC" Name="mnuMarkSLC" Click="mnuMarkSLC_Click">

                        </MenuItem>
                    </ContextMenu>
                </DataGrid.ContextMenu>
            </DataGrid>
        </Grid>
    </ScrollViewer>
</UserControl>

Below is QueueData.cs:

using System;

namespace Wip.ViewModels {
    public class QueueData {
        #region Public Properties
        public string Company { get; set; }
        public string Division { get; set; }
        public string CustomerNumber { get; set; }
        public string ImageCode { get; set; }
        public int Sequence { get; set; }
        public string OrderNumber { get; set; }
        public string Logo { get; set; }
        public int LineNumber { get; set; }
        public string HoldCode { get; set; }
        public string Dhotp { get; set; }
        public string Comments { get; set; }
        public string WorkCenter { get; set; }

        public DateTime ImportDate { get; set; }

        // Due Date

        public string Product { get; set; }

        // Ink

        public int ProcessType { get; set; }
        public bool ProductionEdit { get; set; }
        public bool PimEmbJob { get; set; }
        public string PimEmbJobDisplay { get { return (PimEmbJob == true ? "Yes" : "No"); } }
        public string Ink { get; set; }
        public int StitchCount { get; set; }
        public int Pieces { get; set; }
        // Work Center
        #endregion
    }
}
\$\endgroup\$
  • \$\begingroup\$ Are you sure you need to rebind the data context when using CollectionViewSource.Filter? \$\endgroup\$ – dfhwze Sep 24 at 17:46
  • \$\begingroup\$ I am not sure at all. I hacked through the code I found on StackExchange. Are you suggesting filteredView is bound implicitly? \$\endgroup\$ – Mike Sep 24 at 17:49
  • \$\begingroup\$ here's a tutorial: you need to bind to the source collection and use the collectionview to filter and call refresh. but you should never rebind the data context: grantwinney.com/… This tutorial is better than my answer was :) \$\endgroup\$ – dfhwze Sep 24 at 18:11
  • \$\begingroup\$ My TextChanged now has CollectionViewSource.GetDefaultView(MainGrid.DataContext).Filter = x => ((QueueData)x).ImageCode.Contains(textBox.Text) || ((QueueData)x).Product.Contains(textBox.Text); It still lags. Is that to be expected? \$\endgroup\$ – Mike Sep 24 at 18:15
  • \$\begingroup\$ Try the pattern from tutorial, it sets the Filter once, and refreshes on text changed. \$\endgroup\$ – dfhwze Sep 24 at 18:17
2
\$\begingroup\$

I would handle the data source a little different than you:

In your code behind for the control, you can initialize in the following way:

public ManagerWindow()
{
  InitializeComponent();

  _viewSource = new CollectionViewSource();
  _viewSource.Source = InitializeView();
  _viewSource.Filter += ViewSource_Filter;
  MainGrid.ItemsSource = _viewSource.View;
}

private void ViewSource_Filter(object sender, FilterEventArgs e)
{
  string text = txtSearch.Text;
  QueueData data = e.Item as QueueData;
  e.Accepted = string.IsNullOrWhiteSpace(text) || (data != null && (data.ImageCode.Contains(text) || data.Product.Contains(text)));
}

CollectionViewSource _viewSource;

Where InitializeView() is a method defined by you, that must return a collection (List<QueueData>) or something like that. But as you probably know it can be set in many other ways.

Notice that the DataContext for the data grid is not set - only the ItemsSource.

The change event for the text box should then look like:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
  _viewSource.View.Refresh();
}

The performance problem is mainly a render/layout-problem caused by "Auto" setting for the grid row containing the data grid, so in the xaml for the control you must remove the ScrollViewer and define the grid as follows:

  <Grid Background="Transparent" FocusManager.FocusedElement="{Binding ElementName=txtSearch}">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"></RowDefinition>
      <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"></ColumnDefinition>
      <ColumnDefinition Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>

Notice the second row definition:

<RowDefinition Height="*"></RowDefinition>

Setting Height="*" instead of "Auto" is important because it will eliminate the need for the outer ScrollViewer and speed up the calculations of the dimensions of the data grid, when rendering (You may set it to a static value, but not "Auto")


The data grid should then be defined as:

    <DataGrid Grid.Row="1" Grid.ColumnSpan="2" Name="MainGrid"
              ScrollViewer.VerticalScrollBarVisibility="Auto"
              ScrollViewer.HorizontalScrollBarVisibility="Auto"
              ScrollViewer.CanContentScroll="True"
              EnableRowVirtualization="True"
          Sorting="MainGrid_Sorting"
          SelectionChanged="MainGrid_SelectionChanged"
          MouseDoubleClick="MainGrid_MouseDoubleClick"
          AutoGenerateColumns="False"
              AlternationCount="2"
          AlternatingRowBackground="DimGray"
          Margin="0,0,0,0">

Notice the missing Binding for ItemsSource.

With these changes everything should work smoothly and efficiently.

You may want to experiment with EnableRowVirtualization="True/False" to see which state is most smooth.


You should remove the column header style on every column because they are the same and then define it in the UserControl.Resources as:

<Style TargetType="DataGridColumnHeader">
  <Setter Property="Background" Value="#FF666666" />
  <Setter Property="VerticalAlignment" Value="Center" />
</Style>
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.