AnnaBauer21
Grünschnabel
Hallo ihr Lieben,
ich stehe vor einem Problem und komme nicht mehr weiter.
Ich habe die Hoffnung, dass ihr mir helfen könnt.
Kurze Erklärung:
Ich habe ein DataGrid (EnableRowVirtualization=true) & eine Suchleiste. Gebe ich in der Suchleiste etwas ein wird die Liste entsprechend gefiltert & über ein AttachedProperty der entsprechende Text markiert. Das markieren erfolgt, indem der Text aus einem TextBlock in mehrere Inlines zerlegt wird. In den Inlines, die den Suchtext beinhalten ist dann eine entsprechende Background-Farbe gesetzt.
--> Funktioniert wunderbar
Problem:
Sobald ich runter und wieder rauf scrolle, enthalten die Elemente, die den sichtbaren Bereich verlassen haben, plötzlich den gleichen Text wie das 2. Element. Gleiches passiert auch am Ende der Liste. Das komische ist, wenn ich erneut runter und wieder rauf scrolle ist der Text teilweise wieder richtig.
Ich habe mich am Grid auf das Event LoadingRow gehangen und gesehen, dass die Row, die dann in den sichtbaren Bereich kommt, zwar im DataContext die richtigen Daten enthält, aber die Texte in den TextBlöcken sich nicht aktualisiert haben.
Mein Gedanke war, dass evtl. durch das Manipulieren der Inlines das Binding kaputt gegangen ist, aber das scheint nicht der Fall zu sein.
Wenn EnableRowVirtualization auf false gesetzt wird, funktioniert es, aber leider ist die Virtualisierung zwingend nötig denn die Liste kann im Grunde n Einträge besitzen, aktuell ist die Einschätzung bis zu 5000.
Update 1:-----
In der Methode HighlightTextBlock(TextBlock) direkt beim Einstieg liefert txtBlock.GetBindingExpression(TextBlock.TextProperty) die entsprechenden Daten, aber am Ende der Methode, nachdem die Inlines gesetzt wurden, liefert txtBlock.GetBindingExpression(TextBlock.TextProperty) null.
Ist das Binding dadurch irgendwie kaputt gegangen? Das würde erklären, warum in GridOnLoadingRow der DataContext die neuen Daten enthält, die TextBlöcke aber noch die alten.
-----
Ich hoffe ihr könnt mir helfen, nachfolgend der Code zu meinem Testprojekt.
Formular.xaml
Formular.xaml.cs
Highlighter.cs
MyListItem.cs
ich stehe vor einem Problem und komme nicht mehr weiter.
Ich habe die Hoffnung, dass ihr mir helfen könnt.
Kurze Erklärung:
Ich habe ein DataGrid (EnableRowVirtualization=true) & eine Suchleiste. Gebe ich in der Suchleiste etwas ein wird die Liste entsprechend gefiltert & über ein AttachedProperty der entsprechende Text markiert. Das markieren erfolgt, indem der Text aus einem TextBlock in mehrere Inlines zerlegt wird. In den Inlines, die den Suchtext beinhalten ist dann eine entsprechende Background-Farbe gesetzt.
--> Funktioniert wunderbar
Problem:
Sobald ich runter und wieder rauf scrolle, enthalten die Elemente, die den sichtbaren Bereich verlassen haben, plötzlich den gleichen Text wie das 2. Element. Gleiches passiert auch am Ende der Liste. Das komische ist, wenn ich erneut runter und wieder rauf scrolle ist der Text teilweise wieder richtig.
Ich habe mich am Grid auf das Event LoadingRow gehangen und gesehen, dass die Row, die dann in den sichtbaren Bereich kommt, zwar im DataContext die richtigen Daten enthält, aber die Texte in den TextBlöcken sich nicht aktualisiert haben.
Mein Gedanke war, dass evtl. durch das Manipulieren der Inlines das Binding kaputt gegangen ist, aber das scheint nicht der Fall zu sein.
Wenn EnableRowVirtualization auf false gesetzt wird, funktioniert es, aber leider ist die Virtualisierung zwingend nötig denn die Liste kann im Grunde n Einträge besitzen, aktuell ist die Einschätzung bis zu 5000.
Update 1:-----
In der Methode HighlightTextBlock(TextBlock) direkt beim Einstieg liefert txtBlock.GetBindingExpression(TextBlock.TextProperty) die entsprechenden Daten, aber am Ende der Methode, nachdem die Inlines gesetzt wurden, liefert txtBlock.GetBindingExpression(TextBlock.TextProperty) null.
Ist das Binding dadurch irgendwie kaputt gegangen? Das würde erklären, warum in GridOnLoadingRow der DataContext die neuen Daten enthält, die TextBlöcke aber noch die alten.
-----
Ich hoffe ihr könnt mir helfen, nachfolgend der Code zu meinem Testprojekt.
Formular.xaml
XML:
<DataGrid
Grid.Row="0"
local:Highlighter.Filter="{Binding Filter, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="true"
ColumnWidth="100"
ItemsSource="{Binding Path=DisplayedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
RowHeight="30"
SelectionMode="Single" />
<WrapPanel Grid.Row="1">
<Label Content="Filter: " />
<TextBox Width="100" Text="{Binding Path=Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</WrapPanel>
Formular.xaml.cs
C#:
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<MyListItem>()
{
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());
}
}
Highlighter.cs
C#:
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<DataGridRow>().ToList();
foreach (var row in gridRows)
{
HighlightRow(row);
}
}
private static void HighlightRow(DataGridRow row)
{
// Get TextBlocks
var txtBlocks = row.GetDescendants<TextBlock>().ToList();
if (!txtBlocks.Any())
{
return;
}
// Check filter
var check = CheckFilter(txtBlocks);
if (check)
{
foreach (var txtBlock in txtBlocks)
{
HighlightTextBlock(txtBlock);
}
}
}
private static bool CheckFilter(List<TextBlock> txtBlocks)
{
if (string.IsNullOrWhiteSpace(filter))
{
return false;
}
// Check whether one of the text blocks in the row contains the filter text
var contains = false;
foreach (var txtBlock in txtBlocks)
{
contains |= txtBlock.Text.ToLower().Contains(filter.ToLower());
}
return contains;
}
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<Inline>();
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<TextBlock>().Select(t => t.Text).ToList());
}
}
MyListItem.cs
C#:
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;
}
}
Zuletzt bearbeitet: