איך לייצר 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))); } }
שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.
נמאס לכם לגלול בין אותם הפוסטים בכל ביקור? כשנרשמים לחשבון, תמיד תחזרו בדיוק למקום שבו הייתם קודם, ותוכלו לבחור לקבל התראות על תגובות חדשות (בין אם במייל, ובין אם בהתראת פוש). תוכלו גם לשמור סימניות ולפרגן ב-upvote לפוסטים כדי להביע הערכה לחברי קהילה אחרים.
בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗
הרשמה התחברות