Highlight all text blocks that have the same text

99 Views Asked by At

I have a rather complex form containing several text blocks. I'd like to add a binding to these block so that, when the mouse is hovered over them, all of the other blocks whose text match the run I'm hovering over get highlighted.

This is the same effect you would see in Visual Studio or notepad++ if you selected a word (all of the same words highlight in the editor window).

This is what I have so far:

class TestViewModel
{
    public string TextToMatch { get; set; }
}

public partial class Test : UserControl
{
    TestViewModel _viewModel;
    public Test()
    {
        _viewModel = new TestViewModel();
        DataContext = _viewModel;
        InitializeComponent();
    }

    private void Test_MouseEnter(object sender, MouseEventArgs e)
    {
        var text = ((TextBlock)sender).Text;
        _viewModel.TextToMatch = text;
    }

    private void Test_MouseLeave(object sender, MouseEventArgs e)
    {
        _viewModel.TextToMatch = "";
    }
}

Partial XAML:

    <StackPanel>
        <TextBlock
            Name="Test1"
            Background="{Binding TextToMatch Converter={StaticResource converter}}"
            MouseEnter="Test_MouseEnter"
            MouseLeave="Test_MouseLeave">
            This matches
        </TextBlock>
        <TextBlock
            Name="Test2"
            Background="{Binding TextToMatch Converter={StaticResource converter}}"
            MouseEnter="Test_MouseEnter"
            MouseLeave="Test_MouseLeave">
            This matches
        </TextBlock>
        <TextBlock
            Name="Test3"
            Background="{Binding TextToMatch Converter={StaticResource converter}}"
            MouseEnter="Test_MouseEnter"
            MouseLeave="Test_MouseLeave">
            Some other text
        </TextBlock>
        <TextBlock
            Name="Test4"
            Background="{Binding TextToMatch Converter={StaticResource converter}}"
            MouseEnter="Test_MouseEnter"
            MouseLeave="Test_MouseLeave">
            This matches
        </TextBlock>
        <TextBlock
            Name="Test5"
            Background="{Binding TextToMatch Converter={StaticResource converter}}"
            MouseEnter="Test_MouseEnter"
            MouseLeave="Test_MouseLeave">
            Some other text
        </TextBlock>
    </StackPanel>

What seems clear is that I need a value converter for those bindings. That's not the problem; the problem is how to get the current text value of each text block to the value converter so that it can perform the necessary text comparison and output the correct background color.

How would I go about doing this? Or is there a better way I haven't thought of?

1

There are 1 best solutions below

1
nosale On BEST ANSWER

You need a IMultiValueConverter and a MultiBinding for this

Note: You may have to adjust the converter to better fit your needs

Option 1:
Downside: You have to return your "default" Background too

Converter:

public class DistinctBrushMultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Distinct().Count() == 1)
        {
            return Brushes.Orange; //Brush you want for highlight 
        }

        return null; //Or your default Brush
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Usage:

<TextBlock
    Name="Test1"
    MouseEnter="Test_MouseEnter"
    MouseLeave="Test_MouseLeave">
    <TextBlock.Background>
        <MultiBinding Converter="{StaticResource DistinctBrushMultiValueConverter}">
            <Binding Path="TextToMatch" />
            <Binding RelativeSource="{RelativeSource Self}" Path="Text" />
        </MultiBinding>
    </TextBlock.Background>
    This matches
</TextBlock>

Option 2: Use Trigger inside a Style

Converter:

public class DistinctValuesMultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values.Distinct().Count() == 1;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Style:

<Style x:Key="HighlightTextBlockStyle" TargetType="{x:Type TextBlock}">
    <Style.Triggers>
        <DataTrigger Value="True">
            <DataTrigger.Binding>
                <MultiBinding Converter="{StaticResource DistinctValuesMultiValueConverter}">
                    <Binding Path="TextToMatch" />
                    <Binding Path="Text" RelativeSource="{RelativeSource Self}" />
                </MultiBinding>
            </DataTrigger.Binding>
            <DataTrigger.Setters>
                <Setter Property="Background" Value="Orange" />
            </DataTrigger.Setters>
        </DataTrigger>
    </Style.Triggers>
</Style>

Usage:

<TextBlock
    Name="Test1"
    MouseEnter="Test_MouseEnter"
    MouseLeave="Test_MouseLeave"
    Style="{StaticResource HighlightTextBlockStyle}">
    This matches
</TextBlock>

Option 3 (Just for your information): : You may also use a Behavior