XamDataGrid に検索 (Ctrl+F) 機能を実装しようとしています。複数の列のコンテンツを検索し、検索に一致する列のみを表示するグリッドでレコード フィルタリングをプログラムで呼び出すにはどうすればよいですか?
4 に答える
DataPresenterでのレコード フィルタリングはまさにそれであり、特定の基準に基づいてレコードをフィルタリングする手段です。通常、その基準は組み込みの ui の 1 つを介して提供されます -フィルタリングされた値の単なるドロップダウン リストであるLabelIconsを使用するか、選択/選択できるように各列のセルで表示される専用の特別なレコードであるFilterRecordを使用します。演算子と値を入力します。
そうは言っても、レコードのフィルタリングは、必要に応じてコードで操作できます。FieldLayoutは、RecordFilterが条件 (一致基準など) と一致を実行するフィールドを提供するRecordFiltersコレクションを公開します。RecordManagerからも公開されますが、これは実際には、子レコードの「島」ごとにフィルター条件が異なる階層的な状況でのフィルター処理を容易にするためのものです。
同じ条件で複数のフィールドを検索する必要があるため、FieldLayout のFieldsコレクション (またはこれを適用する Fields の任意のサブセット)の各Fieldに対してRecordFilterを作成する必要があります。テキスト検索を実行したいので、使用する ComparisonOperator が Contains で、値が検索対象のテキストである ComparisonCondition を使用することをお勧めします。値がいずれかのフィールド (RecordFilter を作成したフィールド) で見つかった場合にレコードを一致させる必要があるため、FieldLayoutSettings のRecordFiltersLogicalOperatorプロパティをOrに設定する必要もあります。(デフォルトでは、これは And に解決されます。これは、通常、すべての条件が入力された値と一致する場合にレコードを照合する必要があるためです)。
そのため、以下は添付された基本的な動作です (この場合、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>
質問の他の部分は、一致する値を含む列のみを表示することでした。レコード フィルタリングは、指定された基準に基づいてレコードを除外することであり、列/フィールドの非表示/表示とは関係がないため、これはレコード フィルター自体の一部として実際には不可能です。そうは言っても、一致するテキストがどこにあるかをエンド ユーザーが理解できるようにする 1 つの方法は、セル内のそのテキストを強調表示することです。
このような機能を提供するために、 HighlightTextBlockという派生 TextBlock を定義しました。いくつかのプロパティを公開します。
- RawText - これは、表示されるソース テキストです。この要素は TextBlock の Inlines を操作し、Text プロパティを設定するため、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 つの列にフィルターを設定したかっただけです。ここにあります:
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}"/>
これは私にとって完璧に機能します。
この問題に対処する方法は、検索ボタンのクリック イベント (またはその他のトリガー メカニズム) にコードを追加し、linq またはレコードごとの比較を使用してコード内で直接検索を実行することです。列と行がどのように取り込まれているかを知るのに役立ちます)。
適切な結果を持つ一連のレコードや列を取得したら、ヒットのない列を非表示にします。プロセスを合理化するために、非表示にする列のコレクションを作成し、各列を処理するメソッドを用意します。このメソッドは、列データを検索し、最初の一致の符号で、非表示にする列のリストから列を削除し、列内のレコードの処理を停止します。
プロセスの最後に、リストには非表示にする列のリストが含まれます。これを使用して、実際の非表示を実行できます。
このメカニズムは、複数の行があると仮定して、一致しない行を非表示にするように拡張することもできます。最初の列の処理中に一致しない各行を非表示にし、一致が見つかった後続の列でそれらを再表示することで、これをサポートします。
私は非常に基本的な解決策を探していたので、うまくいったことを共有します。列にフィルターを適用する 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です。Nameというフィールド (つまり、列)があり、指定されたconnectionNameでフィルター処理するContainsフィルターを追加しています。
何らかの理由で、 Contains比較演算子を使用すると、XamDataGrid の UI にのみフィルター テキストが表示されます。それがその列のデフォルトのフィルター演算子であるためだと思います。そのため、私が正しく行っていないことがありますが、とにかくContains演算子を使用したかったので、目的のために機能しました.