You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF实现DataGrid选中行联动RichTextBox公式内容格式变更

实现WPF中DataGrid选中项与RichTextBox公式对应部分联动高亮

这个需求我之前做过类似的,核心思路是给公式里的每个源项片段打上标记,然后在DataGrid选中行时,通过标记定位到RichTextBox里对应的文本并修改样式。咱们一步步来实现:

1. 先定义数据模型

首先创建一个CalculationItem类,用来表示DataGrid里的源项,实现INotifyPropertyChanged方便后续绑定更新:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class CalculationItem : INotifyPropertyChanged
{
    private string _id;
    public string Id
    {
        get => _id;
        set { _id = value; OnPropertyChanged(); }
    }

    private string _displayName;
    public string DisplayName
    {
        get => _displayName;
        set { _displayName = value; OnPropertyChanged(); }
    }

    private decimal _value;
    public decimal Value
    {
        get => _value;
        set { _value = value; OnPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. 解析公式并加载到RichTextBox

我们需要把公式字符串中的[MR:xxx]片段提取出来,在插入RichTextBox时给每个片段的Run附加一个Tag(存储对应的源项ID),这样后续就能通过ID快速定位到对应的文本:

using System.Text.RegularExpressions;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

private void LoadFormulaIntoRichTextBox(string formulaText)
{
    richTextBox.Document.Blocks.Clear();
    var paragraph = new Paragraph();
    // 匹配[MR:xxx]格式的正则,xxx可以是任意非]的字符
    var regex = new Regex(@"\[MR:([^]]+)\]");
    int lastTextPosition = 0;

    foreach (Match match in regex.Matches(formulaText))
    {
        // 添加匹配片段之前的普通文本
        if (match.Index > lastTextPosition)
        {
            var plainText = formulaText.Substring(lastTextPosition, match.Index - lastTextPosition);
            paragraph.Inlines.Add(new Run(plainText));
        }

        // 处理匹配到的源项片段
        var itemId = match.Groups[1].Value;
        var formulaRun = new Run(match.Value)
        {
            Tag = itemId, // 用Tag存储ID,方便后续查找
            Foreground = Brushes.Black,
            FontWeight = FontWeights.Normal
        };
        paragraph.Inlines.Add(formulaRun);

        lastTextPosition = match.Index + match.Length;
    }

    // 添加公式末尾剩余的文本
    if (lastTextPosition < formulaText.Length)
    {
        paragraph.Inlines.Add(new Run(formulaText.Substring(lastTextPosition)));
    }

    richTextBox.Document.Blocks.Add(paragraph);
}

3. 处理DataGrid选中事件

当DataGrid的选中项变化时,先把RichTextBox里所有高亮的文本恢复默认样式,再找到对应选中项ID的Run,修改它的样式(比如加粗+变色):

private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // 先重置所有Run的样式为默认
    ResetAllFormulaRunsStyle();

    // 获取当前选中的源项
    var selectedItem = dataGrid.SelectedItem as CalculationItem;
    if (selectedItem == null) return;

    // 遍历RichTextBox里的所有Run,找到对应ID的并高亮
    foreach (Block block in richTextBox.Document.Blocks)
    {
        if (block is Paragraph paragraph)
        {
            foreach (Inline inline in paragraph.Inlines)
            {
                if (inline is Run run && run.Tag?.ToString() == selectedItem.Id)
                {
                    run.Foreground = Brushes.DarkRed;
                    run.FontWeight = FontWeights.Bold;
                    // 可选:自动滚动到高亮位置
                    run.BringIntoView();
                    richTextBox.Focus();
                    break;
                }
            }
        }
    }
}

// 重置所有公式片段的样式
private void ResetAllFormulaRunsStyle()
{
    foreach (Block block in richTextBox.Document.Blocks)
    {
        if (block is Paragraph paragraph)
        {
            foreach (Inline inline in paragraph.Inlines)
            {
                if (inline is Run run && run.Tag != null) // 只重置带标记的公式片段
                {
                    run.Foreground = Brushes.Black;
                    run.FontWeight = FontWeights.Normal;
                }
            }
        }
    }
}

4. XAML界面布局

把DataGrid和RichTextBox放在界面上,绑定数据并关联选中事件:

<Window x:Class="FormulaHighlightDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="公式联动高亮演示" Height="450" Width="800">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <DataGrid x:Name="dataGrid" 
                  Grid.Column="0"
                  SelectionChanged="DataGrid_SelectionChanged"
                  AutoGenerateColumns="False"
                  Margin="0 0 10 0">
            <DataGrid.Columns>
                <DataGridTextColumn Header="源项ID" Binding="{Binding Id}" Width="80"/>
                <DataGridTextColumn Header="名称" Binding="{Binding DisplayName}" Width="*"/>
                <DataGridTextColumn Header="数值" Binding="{Binding Value}" Width="80"/>
            </DataGrid.Columns>
        </DataGrid>

        <RichTextBox x:Name="richTextBox" 
                     Grid.Column="1"
                     IsReadOnly="True"
                     Padding="5"/>
    </Grid>
</Window>

5. 初始化测试数据

在窗口构造函数里加载测试数据和公式:

using System.Collections.ObjectModel;

public MainWindow()
{
    InitializeComponent();

    // 初始化DataGrid的源项数据
    var sourceItems = new ObservableCollection<CalculationItem>
    {
        new CalculationItem { Id = "123", DisplayName = "原材料A", Value = 150 },
        new CalculationItem { Id = "456", DisplayName = "原材料B", Value = 300 }
    };
    dataGrid.ItemsSource = sourceItems;

    // 加载公式到RichTextBox
    var formula = "[MR:123] + [MR:456] * 0.8";
    LoadFormulaIntoRichTextBox(formula);
}

额外注意点

  • 如果你的公式格式有变化(比如[MR:XXX-YYY]),只需要调整正则表达式@"\[MR:([^]]+)\]"即可,这个正则能匹配[MR:和]之间的任意内容。
  • 如果需要支持多选,只需要修改SelectionChanged事件,遍历dataGrid.SelectedItems,把每个选中项对应的Run都高亮。
  • 要是公式很长,BringIntoView()可以确保高亮的部分在可视范围内。

内容的提问来源于stack exchange,提问作者Clint T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:11:45