איך לייצר TextBlock עם טקסט מעוצב ב-wpf שיכול לקבל binding
-
דרך xaml מאוד קל לעצב טקסט ב- textblock ב-wpf
כמתואר כאן בטוב טעםאבל איך עושים binding עבור זה?
ראיתי הרבה כתבות בנושא וכל אחד מציע את הזווית שלו הנה הניסיון שלי (הוא מוגבל כרגע לbold ן-italics אבל אפשר להרחיב את הרעיון, אשמח לקבל משוב:<Window x:Class="HighlightSample.HighlighterTest" 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" xmlns:local="clr-namespace:HighlightSample" mc:Ignorable="d" Title="HighlighterTest" Height="250" Width="250"> <Window.DataContext> <local:HighlighterTextViewModel x:Name="vm"/> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Button x:Name="PopulateButton" Command="{Binding PopulateResultsCommand}" Content="Populate" Margin="5"/> <ListBox ItemsSource="{Binding SearchResults}" Grid.Row="1"> <ListBox.ItemTemplate> <DataTemplate> <local:HighlighterTextBlock HighlightedText="{Binding SearchResult}" TextWrapping="Wrap"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>
using System; using System.Text.RegularExpressions; using System.Windows.Controls; using System.Windows; using System.Windows.Documents; using System.Collections.ObjectModel; using System.Windows.Input; using System.ComponentModel; using System.Runtime.CompilerServices; using PdfiumViewer.Demo.Annotations; namespace HighlightSample { internal class HighlighterTextViewModel : ViewModelBase { private ObservableCollection<TextModel> _searchResults = new ObservableCollection<TextModel> { }; public ICommand PopulateResultsCommand { get => new RelayCommand(PopulateResults); } public void PopulateResults() { for (int i = 0; i < 100000; i++) { SearchResults.Add(new TextModel()); } } public ObservableCollection<TextModel> SearchResults { get { return _searchResults; } set { if (_searchResults != value) { _searchResults = value; OnPropertyChanged(nameof(SearchResults)); } } } } internal class TextModel : ViewModelBase { private string _searchResult = "This is a <b>bold</b> and <i>italic</i> example."; public string SearchResult { get { return _searchResult; } set { if (_searchResult != value) { _searchResult = value; OnPropertyChanged(nameof(SearchResult)); } } } } public class HighlighterTextBlock : TextBlock { public static readonly DependencyProperty HighlightedTextProperty = DependencyProperty.Register("HighlightedText", typeof(string), typeof(HighlighterTextBlock), new PropertyMetadata(string.Empty, OnHighlightedTextChanged)); public string HighlightedText { get { return (string)GetValue(HighlightedTextProperty); } set { SetValue(HighlightedTextProperty, value); } } private static void OnHighlightedTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = d as HighlighterTextBlock; control?.UpdateText(); } private void UpdateText() { Inlines.Clear(); if (string.IsNullOrEmpty(HighlightedText)) { return; } string pattern = @"(<b>(.*?)</b>)|(<i>(.*?)</i>)"; // Example pattern for bold and italic tags int lastPos = 0; var matches = Regex.Matches(HighlightedText, pattern); foreach (Match match in matches) { // Add text before the match if (match.Index > lastPos) { Inlines.Add(new Run(HighlightedText.Substring(lastPos, match.Index - lastPos))); } // Add highlighted or formatted text if (match.Groups[1].Success) // Bold tag { Inlines.Add(new Bold(new Run(match.Groups[2].Value))); } else if (match.Groups[3].Success) // Italic tag { Inlines.Add(new Italic(new Run(match.Groups[4].Value))); } lastPos = match.Index + match.Length; } // Add remaining text after the last match if (lastPos < HighlightedText.Length) { Inlines.Add(new Run(HighlightedText.Substring(lastPos))); } } } public abstract class ICommandBase : ICommand { public event EventHandler CanExecuteChanged; public virtual bool CanExecute(object parameter) { return true; } public abstract void Execute(object parameter); protected void OnCanExecuteChanged() { CanExecuteChanged?.Invoke(this, new EventArgs()); } } public class RelayCommand : ICommandBase { private Action commandTask; public RelayCommand(Action action) { commandTask = action; } public override void Execute(object parameter) { commandTask(); } } public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; [NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string PropertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName)); [NotifyPropertyChangedInvocator] protected virtual bool Set<T>(ref T field, T value, [CallerMemberName] string PropertyName = null) { if (Equals(field, value)) return false; field = value; OnPropertyChanged(PropertyName); return true; } } }
-
לא בדקתי את הכל, אבל יש לי שיפור לרג'קס במתודה updateText
private void UpdateText() { Inlines.Clear(); string pattern = @"(<([bi])>(.*)</\1>)"; // Example pattern for bold and italic tags int lastPos = 0; var matches = Regex.Matches(HighlightedText, pattern); foreach (Match match in matches) { // Add text before the match if (match.Index > lastPos) { Inlines.Add(new Run(HighlightedText.Substring(lastPos, match.Index - lastPos))); } Inlines.Add(match.Groups[1].Value switch { "b" => new Bold(new Run(match.Groups[2].Value)), "i" => new Italic(new Run(match.Groups[2].Value)), }); lastPos = match.Index + match.Length; } // Add remaining text after the last match if (lastPos < HighlightedText.Length) { Inlines.Add(new Run(HighlightedText.Substring(lastPos))); } }