WPF DataGrid Virtualization – Seltsamer Fehler nach dem Scrollen
Posted: 14 Jan 2025, 11:18
Ich stehe vor einem Problem und komme nicht weiter.
Ich hoffe, Sie können mir helfen.
Kurze Erklärung:
Ich habe ein DataGrid (VirtualizingPanel.IsVirtualizing="true") und eine Suchleiste. Wenn ich etwas in die Suchleiste eingebe, wird die Liste entsprechend gefiltert und der Suchtext mithilfe einer AttachedProperty hervorgehoben.
Die Hervorhebung erfolgt durch Aufteilen des TextBlock-Textes in mehrere Inlines. In den Inlines, die den Suchtext enthalten, wird dann eine entsprechende Hintergrundfarbe gesetzt.
Funktioniert einwandfrei:
[img]https://i.sstatic .net/ULYSVnED.png[/img]
Ich habe das LoadingRow-Ereignis des Grids überprüft und festgestellt, dass die Zeile, die in den sichtbaren Bereich kommt, die richtigen Daten im DataContext enthält, aber die Texte im TextBlocks wurden nicht aktualisiert.
Mein Gedanke war, dass vielleicht die Bindung durch Manipulation der Inlines gebrochen wurde, aber das scheint nicht das Problem zu sein.

Wenn EnableRowVirtualization auf false gesetzt ist, ist es funktioniert, aber leider ist eine Virtualisierung zwingend erforderlich, da die Liste grundsätzlich n Einträge haben kann, die aktuelle Schätzung liegt bei bis zu 5000.
Zu Beginn der Methode < strong>HighlightTextBlock(TextBlock) Der Aufruf txtBlock.GetBindingExpression(TextBlock.TextProperty) gibt die entsprechenden Daten zurück, aber am Ende der Methode, nachdem die Inlines gesetzt wurden, txtBlock.GetBindingExpression(TextBlock.TextProperty) gibt null zurück.
Hat dies die Bindung unterbrochen? Das würde erklären, warum in GridOnLoadingRow der DataContext die neuen Daten enthält, die TextBlocks jedoch immer noch die alten.
Ich hoffe, Sie können mir helfen, unten ist der Code für mein Testprojekt.
Formular.xaml
Formular.xaml.cs
Highlighter.cs
MyListItem.cs
Ich weiß nicht, was ich tun kann.
Ich habe die anderen Antworten überprüft, aber ich habe den Eindruck, dass nichts für mein Problem geeignet ist.
Ich hoffe, Sie können mir helfen.
Kurze Erklärung:
Ich habe ein DataGrid (VirtualizingPanel.IsVirtualizing="true") und eine Suchleiste. Wenn ich etwas in die Suchleiste eingebe, wird die Liste entsprechend gefiltert und der Suchtext mithilfe einer AttachedProperty hervorgehoben.
Die Hervorhebung erfolgt durch Aufteilen des TextBlock-Textes in mehrere Inlines. In den Inlines, die den Suchtext enthalten, wird dann eine entsprechende Hintergrundfarbe gesetzt.
Funktioniert einwandfrei:
[img]https://i.sstatic .net/ULYSVnED.png[/img]
Ich habe das LoadingRow-Ereignis des Grids überprüft und festgestellt, dass die Zeile, die in den sichtbaren Bereich kommt, die richtigen Daten im DataContext enthält, aber die Texte im TextBlocks wurden nicht aktualisiert.
Mein Gedanke war, dass vielleicht die Bindung durch Manipulation der Inlines gebrochen wurde, aber das scheint nicht das Problem zu sein.

Wenn EnableRowVirtualization auf false gesetzt ist, ist es funktioniert, aber leider ist eine Virtualisierung zwingend erforderlich, da die Liste grundsätzlich n Einträge haben kann, die aktuelle Schätzung liegt bei bis zu 5000.
Zu Beginn der Methode < strong>HighlightTextBlock(TextBlock) Der Aufruf txtBlock.GetBindingExpression(TextBlock.TextProperty) gibt die entsprechenden Daten zurück, aber am Ende der Methode, nachdem die Inlines gesetzt wurden, txtBlock.GetBindingExpression(TextBlock.TextProperty) gibt null zurück.
Hat dies die Bindung unterbrochen? Das würde erklären, warum in GridOnLoadingRow der DataContext die neuen Daten enthält, die TextBlocks jedoch immer noch die alten.
Ich hoffe, Sie können mir helfen, unten ist der Code für mein Testprojekt.
Formular.xaml
Code: Select all
Code: Select all
public partial class Formular : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = (sender, args) => { };
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public ICollectionView DisplayedItems { get; set; }
private string filter;
public string Filter
{
get => this.filter;
set
{
this.filter = value;
this.DisplayedItems.Refresh();
this.RaisePropertyChanged();
}
}
public Formular()
{
InitializeComponent();
this.DataContext = this;
var listItems = new ObservableCollection()
{
new MyListItem("Alpha", "Mission1"),
new MyListItem("Beta1", "Mission1"),
new MyListItem("Beta1", "Mission2"),
new MyListItem("Beta1", "Mission3"),
new MyListItem("Beta1", "Mission4"),
new MyListItem("Beta1", "Mission5"),
new MyListItem("Beta1", "Mission6"),
new MyListItem("Beta1", "Mission7"),
new MyListItem("Beta1", "Mission8"),
new MyListItem("Beta1", "Mission9"),
new MyListItem("Beta2", "Mission2"),
};
this.DisplayedItems = CollectionViewSource.GetDefaultView(listItems);
this.DisplayedItems.Filter = this.FilterCallback;
}
public bool FilterCallback(object obj)
{
var item = (MyListItem) obj;
return string.IsNullOrEmpty(this.Filter)
|| item.Name.ToUpper().Contains(Filter.ToUpper())
|| item.MissionName.ToUpper().Contains(Filter.ToUpper());
}
}
Code: Select all
public static class Highlighter
{
private static string filter;
static Highlighter(){}
#region Filter
public static readonly DependencyProperty FilterProperty =
DependencyProperty.RegisterAttached("Filter", typeof(string), typeof(Highlighter), new PropertyMetadata("", PropertyChangedCallback));
public static void SetFilter(DependencyObject obj, string value)
{
obj.SetValue(FilterProperty, value);
}
public static string GetFilter(DependencyObject obj)
{
return (string)obj?.GetValue(FilterProperty);
}
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => DoAction(d)));
}
#endregion
private static void DoAction(DependencyObject d)
{
filter = GetFilter(d);
if (filter == null)
{
return;
}
var grid = (DataGrid)d;
grid.LoadingRow += GridOnLoadingRow;
// Get DataGridRows
var gridRows = grid.GetDescendants().ToList();
foreach (var row in gridRows)
{
HighlightRow(row);
}
}
private static void HighlightRow(DataGridRow row)
{
// Get TextBlocks
var txtBlocks = row.GetDescendants().ToList();
if (!txtBlocks.Any())
{
return;
}
foreach (var txtBlock in txtBlocks)
{
HighlightTextBlock(txtBlock);
}
}
private static void HighlightTextBlock(TextBlock txtBlock)
{
var text = txtBlock.Text;
if (string.IsNullOrEmpty(text))
{
return;
}
// Check whether the text contains the filter text
var index = text.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase);
if (index < 0)
{
// Filter text not found
return;
}
// Generate Inlines with highlighting information
var inlines = new List();
while (true)
{
// Text from beginning to filter text
inlines.Add(new Run(text.Substring(0, index)));
// Text that corresponds to the filter text
inlines.Add(new Run(text.Substring(index, filter.Length))
{
Background = Brushes.Yellow
});
// Text from filter text to ending
text = text.Substring(index + filter.Length);
// Check whether the remaining text also contains the filter text
index = text.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase);
if (index < 0)
{
// If not, add remaining text and exit loop
inlines.Add(new Run(text));
break;
}
}
// Replace Inlines
txtBlock.Inlines.Clear();
txtBlock.Inlines.AddRange(inlines);
}
private static void GridOnLoadingRow(object sender, DataGridRowEventArgs e)
{
var dataContext = (MyListItem) e.Row.DataContext;
var newData = $"{dataContext.Name}_{dataContext.MissionName}";
var oldData = string.Join("_", e.Row.GetDescendants().Select(t => t.Text).ToList());
}
}
Code: Select all
public class MyListItem : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = (sender, args) => { };
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public string name;
public string Name
{
get => name;
set
{
this.name = value;
this.RaisePropertyChanged();
}
}
public string missionName;
public string MissionName
{
get => missionName;
set
{
this.missionName = value;
this.RaisePropertyChanged();
}
}
public MyListItem(string name, string missionName)
{
this.Name = name;
this.MissionName = missionName;
}
}
Ich habe die anderen Antworten überprüft, aber ich habe den Eindruck, dass nichts für mein Problem geeignet ist.