トップ回答者
TextBlockのTextの一部だけ色を変更する方法

質問
-
TreeViewをテキスト検索させて、そのテキストと一致する部分だけ色を変えたいと考えております。
TextBlockの文字列の一部だけ色を変えることは可能でしょうか?
ご存じの方がいれば教えていただきたいです。
(例)
黒文字で書かれたテスト12というツリーがあった場合、
「テスト」という文字列で検索すると、テストという文字だけ赤くなるイメージです。
C#
namespace TreeViewSample { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.treeView.ItemsSource = new List<test> { new test { Name = "テスト 12", }, }; } } }
namespace TreeViewSample { class test { public string Name { get; set; } } }
XAML
<Window x:Class="TreeViewSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <TreeView Name="treeView"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="local:Test"> <TextBlock Text="{Binding Name}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </Window>
回答
-
こんな
using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); List<test> list = new List<test>(); for (int i = 0; i < 100; i++) { list.Add(new test() { Name = "テスト " + i.ToString() }); } this.treeView.ItemsSource = list; } private void Button_Click(object sender, RoutedEventArgs e) { foundTextBlock = Find("テスト", foundTextBlock); if (foundTextBlock != null) { TreeViewItem tvi = ToTreeViewItem(foundTextBlock); tvi.BringIntoView(); } } TextBlock foundTextBlock; /// <summary> /// TreeViewから文字列を含むTextBlockを探して色づけする /// </summary> /// <param name="text">探す文字</param> /// <param name="skip">このTextBlockの次から検索をする</param> /// <returns>見つけたTextBlock</returns> private TextBlock Find(string text, TextBlock skip) { //(注意)このコードでは一度も展開していないTreeViewItemは検索できません if (skip != null) { //前に検索されたTreeViewItemの文字の色を戻す string s = skip.Text; skip.Inlines.Clear(); skip.Inlines.Add(new Run(s)); } foreach (TextBlock block in EnumChildren<TextBlock>(treeView)) { if (skip != null) { if (skip == block) { skip = null; } } else { string s = block.Text; if (s.Contains(text)) { //文字列を含むTextBlockが見つかったら //文字列を分解して黒色のRunと赤色のRunを作る int p1; int p2; p1 = 0; List<Run> runs = new List<Run>(); while (p1 >= 0) { p2 = s.IndexOf(text, p1); if (p2 < 0) { Run run = new Run(); run.Text = s.Substring(p1); runs.Add(run); p1 = -1; } else if (p2 == p1) { Run runRed = new Run(); runRed.Text = text; runRed.Foreground = new SolidColorBrush(Colors.Red); runs.Add(runRed); p1 = p2 + text.Length; } else if (p2 > p1) { Run run = new Run(); run.Text = s.Substring(p1, p2 - p1); runs.Add(run); Run runRed = new Run(); runRed.Text = text; runRed.Foreground = new SolidColorBrush(Colors.Red); runs.Add(runRed); p1 = p2 + text.Length; } } //TextBlockのInlineを色つきのRunに差し替える block.Inlines.Clear(); block.Inlines.AddRange(runs); return block; } } } return null; } /// <summary> /// 指定したDependencyObjectの子要素からT型のDependencyObjectを抽出する /// </summary> /// <typeparam name="T">抽出する型</typeparam> /// <param name="d">ルート要素</param> /// <returns>T型の列挙</returns> public IEnumerable<T> EnumChildren<T>(DependencyObject d) where T : DependencyObject { int count = VisualTreeHelper.GetChildrenCount(d); for (int i = 0; i < count; i++) { DependencyObject child = VisualTreeHelper.GetChild(d, i); if (child is T) { yield return (T)child; } else { foreach (T t in EnumChildren<T>(child)) { yield return t; } } } } /// <summary>TextBlockがあるTreeViewItemを取得する</summary> /// <param name="block"></param> /// <returns></returns> public TreeViewItem ToTreeViewItem(TextBlock block) { DependencyObject parent = block; while (block != null) { parent = VisualTreeHelper.GetParent(parent); if (parent is TreeViewItem) { return (TreeViewItem)parent; } } return null; } } class test { public string Name { get; set; } } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
すべての返信
-
こんな
using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); List<test> list = new List<test>(); for (int i = 0; i < 100; i++) { list.Add(new test() { Name = "テスト " + i.ToString() }); } this.treeView.ItemsSource = list; } private void Button_Click(object sender, RoutedEventArgs e) { foundTextBlock = Find("テスト", foundTextBlock); if (foundTextBlock != null) { TreeViewItem tvi = ToTreeViewItem(foundTextBlock); tvi.BringIntoView(); } } TextBlock foundTextBlock; /// <summary> /// TreeViewから文字列を含むTextBlockを探して色づけする /// </summary> /// <param name="text">探す文字</param> /// <param name="skip">このTextBlockの次から検索をする</param> /// <returns>見つけたTextBlock</returns> private TextBlock Find(string text, TextBlock skip) { //(注意)このコードでは一度も展開していないTreeViewItemは検索できません if (skip != null) { //前に検索されたTreeViewItemの文字の色を戻す string s = skip.Text; skip.Inlines.Clear(); skip.Inlines.Add(new Run(s)); } foreach (TextBlock block in EnumChildren<TextBlock>(treeView)) { if (skip != null) { if (skip == block) { skip = null; } } else { string s = block.Text; if (s.Contains(text)) { //文字列を含むTextBlockが見つかったら //文字列を分解して黒色のRunと赤色のRunを作る int p1; int p2; p1 = 0; List<Run> runs = new List<Run>(); while (p1 >= 0) { p2 = s.IndexOf(text, p1); if (p2 < 0) { Run run = new Run(); run.Text = s.Substring(p1); runs.Add(run); p1 = -1; } else if (p2 == p1) { Run runRed = new Run(); runRed.Text = text; runRed.Foreground = new SolidColorBrush(Colors.Red); runs.Add(runRed); p1 = p2 + text.Length; } else if (p2 > p1) { Run run = new Run(); run.Text = s.Substring(p1, p2 - p1); runs.Add(run); Run runRed = new Run(); runRed.Text = text; runRed.Foreground = new SolidColorBrush(Colors.Red); runs.Add(runRed); p1 = p2 + text.Length; } } //TextBlockのInlineを色つきのRunに差し替える block.Inlines.Clear(); block.Inlines.AddRange(runs); return block; } } } return null; } /// <summary> /// 指定したDependencyObjectの子要素からT型のDependencyObjectを抽出する /// </summary> /// <typeparam name="T">抽出する型</typeparam> /// <param name="d">ルート要素</param> /// <returns>T型の列挙</returns> public IEnumerable<T> EnumChildren<T>(DependencyObject d) where T : DependencyObject { int count = VisualTreeHelper.GetChildrenCount(d); for (int i = 0; i < count; i++) { DependencyObject child = VisualTreeHelper.GetChild(d, i); if (child is T) { yield return (T)child; } else { foreach (T t in EnumChildren<T>(child)) { yield return t; } } } } /// <summary>TextBlockがあるTreeViewItemを取得する</summary> /// <param name="block"></param> /// <returns></returns> public TreeViewItem ToTreeViewItem(TextBlock block) { DependencyObject parent = block; while (block != null) { parent = VisualTreeHelper.GetParent(parent); if (parent is TreeViewItem) { return (TreeViewItem)parent; } } return null; } } class test { public string Name { get; set; } } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
-
gekkaさま
ご回答ありがとうございました。
返信が遅くなり申し訳ございません。
TextBlockのTextを検索して、Runオブジェクトを複数作成し、
コレクションとして、TextBlockにセットすることで、
Textの色を一部変更することができるとのことありがとうございました。
ソースの記載までありがとうございます。
RichTextBoxを使って実現するなどいろいろ模索しておりましたが、
思った通りの表現にならずに悩んでおりました。
これで機能が実現できそうです。
ありがとうございました。
- 編集済み sasagaki 2014年1月21日 0:00