Windows Presentation Foundation
.NET Framework 的一部分,它提供统一的编程模型,用于在 Windows 上构建业务线桌面应用程序。
131 个问题
wpf有像浏览器那样全局搜索定位高亮文本的功能?
又比如点下一个,会定位下一个匹配文本,点上一个反之亦然。
如果有网格,包括定位网格内的内容定位。
你好,@hello. 想要实现这个功能有很多方式。我做了一个示例,先分享在这里。但是它当前还有些问题,如果我完善好功能的话会在下面继续更新。如果你可以完善它,也请在这里分享代码。
<Window x:Class="WpfApp2.PopWindow"
...
mc:Ignorable="d"
Title="PopWindow" Height="80" Width="500">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="SearchBox" Text="{Binding SearchTerm,UpdateSourceTrigger=PropertyChanged}" Width="200" Margin="5" TextChanged="SearchBox_TextChanged" />
<Button x:Name="PrevButton" Content="Previous" Width="75" Margin="5" Click="PrevButton_Click"/>
<Button x:Name="NextButton" Content="Next" Width="75" Margin="5" Click="NextButton_Click"/>
</StackPanel>
</Window>
PopWindow
public partial class PopWindow : Window, INotifyPropertyChanged
{
public event EventHandler<string> SearchTermChanged;
public event EventHandler NavigatePrevious;
public event EventHandler NavigateNext;
public PopWindow()
{
InitializeComponent();
DataContext = this;
}
private string _searchTerm;
public string SearchTerm
{
get { return _searchTerm; }
set
{
if (_searchTerm != value)
{
_searchTerm = value;
OnPropertyChanged(nameof(SearchTerm));
// OnSearchTermChanged();
}
}
}
private void PrevButton_Click(object sender, RoutedEventArgs e)
{
NavigatePrevious?.Invoke(this, EventArgs.Empty);
}
private void NextButton_Click(object sender, RoutedEventArgs e)
{
NavigateNext?.Invoke(this, EventArgs.Empty);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
{
SearchTermChanged?.Invoke(this, SearchTerm);
}
}
<Window x:Class="WpfApp2.MainWindow"
...
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Name="MainGrid">
<RichTextBox x:Name="RichTextBox" VerticalAlignment="Top" Height="150">
<FlowDocument>
<Paragraph>
This is some example text in the RichTextBox. It can be used for testing the search functionality.
</Paragraph>
<Paragraph>
Another paragraph of example text to ensure the search and highlight feature works correctly.
</Paragraph>
</FlowDocument>
</RichTextBox>
<DataGrid x:Name="DataGrid" AutoGenerateColumns="False" VerticalAlignment="Top" Height="250">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID}" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Description" Binding="{Binding Description}" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
MainWindow
public partial class MainWindow : Window
{
private PopWindow _searchPopup;
private List<MatchLocation> _matches;
private int _currentMatchIndex;
public MainWindow()
{
InitializeComponent();
DataGrid.ItemsSource = new List<Item>
{
new Item { ID = 1, Name = "Item 1", Description = "This is the first item." },
new Item { ID = 2, Name = "Item 2", Description = "This is the second item." },
new Item { ID = 3, Name = "Item 3", Description = "This is the third item." }
};
this.PreviewKeyDown += new KeyEventHandler(MainWindow_PreviewKeyDown);
}
private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.F && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
_searchPopup = new PopWindow();
_searchPopup.SearchTermChanged += OnSearchTermChanged;
_searchPopup.NavigatePrevious += OnNavigatePrevious;
_searchPopup.NavigateNext += OnNavigateNext;
_searchPopup.Owner = this;
_searchPopup.Show();
_searchPopup.SearchBox.Focus();
}
}
private void OnSearchTermChanged(object sender, string searchTerm)
{
_matches = FindMatches(searchTerm);
_currentMatchIndex = -1;
if (_matches.Any())
{
for (int i = 0; i < _matches.Count - 1; i++)
{
NavigateToMatch(_matches[i], Brushes.Yellow);
}
NavigateToNextMatch();
NavigateToMatch(_matches[_currentMatchIndex], Brushes.Orange);
}
}
private void OnNavigatePrevious(object sender, EventArgs e)
{
if (_matches.Any() && _currentMatchIndex > 0)
{
NavigateToMatch(_matches[_currentMatchIndex], Brushes.Yellow);
_currentMatchIndex--;
NavigateToMatch(_matches[_currentMatchIndex], Brushes.Orange);
}
}
private void OnNavigateNext(object sender, EventArgs e)
{
if (_matches.Any() && _currentMatchIndex < _matches.Count - 1)
{
NavigateToMatch(_matches[_currentMatchIndex], Brushes.Yellow);
_currentMatchIndex++;
NavigateToMatch(_matches[_currentMatchIndex], Brushes.Orange);
}
}
private List<MatchLocation> FindMatches(string searchTerm)
{
var matches = new List<MatchLocation>();
foreach (var control in FindVisualChildren<TextBlock>(this))
{
matches.AddRange(FindMatchesInTextBlock(control, searchTerm));
}
foreach (var control in FindVisualChildren<TextBox>(this))
{
matches.AddRange(FindMatchesInTextBox(control, searchTerm));
}
return matches;
}
private List<MatchLocation> FindMatchesInTextBlock(TextBlock textBlock, string searchTerm)
{
var matches = new List<MatchLocation>();
var regex = new Regex(Regex.Escape(searchTerm), RegexOptions.IgnoreCase);
foreach (Match match in regex.Matches(textBlock.Text))
{
matches.Add(new MatchLocation
{
Control = textBlock,
Match = match
});
}
return matches;
}
private List<MatchLocation> FindMatchesInTextBox(TextBox textBox, string searchTerm)
{
var matches = new List<MatchLocation>();
var regex = new Regex(Regex.Escape(searchTerm), RegexOptions.IgnoreCase);
foreach (Match match in regex.Matches(textBox.Text))
{
matches.Add(new MatchLocation
{
Control = textBox,
Match = match
});
}
return matches;
}
private void NavigateToMatch(MatchLocation matchLocation, Brush highlightColor)
{
if (matchLocation.Control is TextBlock textBlock)
{
HighlightTextBlockMatch(textBlock, matchLocation.Match, highlightColor);
textBlock.BringIntoView();
}
else if (matchLocation.Control is TextBox textBox)
{
HighlightTextBoxMatch(textBox, matchLocation.Match, highlightColor);
textBox.Focus();
textBox.CaretIndex = matchLocation.Match.Index;
textBox.ScrollToLine(textBox.GetLineIndexFromCharacterIndex(matchLocation.Match.Index));
}
}
private void HighlightTextBlockMatch(TextBlock textBlock, Match match, Brush highlightColor)
{
var text = textBlock.Text;
textBlock.Inlines.Clear();
var startIndex = 0;
foreach (Match m in Regex.Matches(text, Regex.Escape(match.Value), RegexOptions.IgnoreCase))
{
if (m.Index > startIndex)
{
textBlock.Inlines.Add(new Run(text.Substring(startIndex, m.Index - startIndex)));
}
var run = new Run(m.Value)
{
Background = highlightColor
};
textBlock.Inlines.Add(run);
startIndex = m.Index + m.Length;
}
if (startIndex < text.Length)
{
textBlock.Inlines.Add(new Run(text.Substring(startIndex)));
}
}
private void HighlightTextBoxMatch(TextBox textBox, Match match, Brush highlightColor)
{
var startIndex = textBox.Text.IndexOf(match.Value, StringComparison.CurrentCultureIgnoreCase);
if (startIndex >= 0)
{
textBox.Select(startIndex, match.Length);
textBox.SelectionBrush = highlightColor;
}
}
private void NavigateToNextMatch()
{
if (_matches.Any() && _currentMatchIndex < _matches.Count - 1)
{
_currentMatchIndex++;
NavigateToMatch(_matches[_currentMatchIndex], Brushes.Yellow);
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private class MatchLocation
{
public UIElement Control { get; set; }
public Match Match { get; set; }
}
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}