如何在WPF中使用IValue转换器仅为ListBox项目的部分文本着色?

时间:2015-11-29 17:17:08

标签: c# wpf xaml listbox

说我有一个名字集合

arMahesh Nagar    
arPriyank arSark

我需要的是,通过使用IValue转换器,我想只着色'ar' s

输出应该是什么(如果放在HTML中..但我在WPF中工作。所以只是为了证明我的要求)

Mahesh Nag<font color='blue'>ar</font>
Priyanka S<font color='blue'>ar</font>k<font color='blue'>ar</font>

目前我的IValue转换器正在突出显示全文(相反,我不知道如何做到这一点)

public class HighlightNames : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            SolidColorBrush _scb = new SolidColorBrush();

            var personName = ((Person)value).PersonName;

            //I know the below is wrong
            if(personName.StartsWith("N")) _scb = Brushes.DarkGoldenrod;
            else _scb = Brushes.Aqua;
            return _scb;


        }

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

而Xaml是

<Window x:Class="WpfApplication1.MainWindow"
        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"        
        mc:Ignorable="d"
        Title="Android Search in WPF" Height="350" Width="525"
        Loaded="Window_Loaded"
        xmlns:local="clr-namespace:WpfApplication1.Converters">

    <Window.Resources>
        <local:HighlightNames x:Key="colorrootnode"/>
    </Window.Resources>

    <Grid>        
        <ListBox 
            x:Name="lstNames" HorizontalAlignment="Left" 
            Height="207" Margin="37,64,0,0" 
            VerticalAlignment="Top" Width="350"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding PersonList}">

            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding PersonName}"
                               Background="{Binding Converter={StaticResource colorrootnode}}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

    </Grid>
</Window>

修改 IValueConverter现在是

namespace WpfApplication1.Converters
{
    public class HighlightNames : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var personName = value.ToString();// ((Person)value).PersonName;

            var textBlock = new TextBlock();
            textBlock.TextWrapping = TextWrapping.Wrap;

            List<string> lstNames = new List<string>();
            lstNames.Add(personName);

            string toSearch = "ar";

            string[] stringSeparators = new string[] { " " };
            string matchedStrings = "";

            foreach (var l in lstNames)
            {
                if (l.Split(stringSeparators, StringSplitOptions.None)

                          .Any(i => i.StartsWith(toSearch)))
                {
                    matchedStrings = l;
                }
            }

            var s1 = matchedStrings.Split(stringSeparators, StringSplitOptions.None);
            var firstPart = s1[0];
            var secondPart = s1[1];
            textBlock.Inlines.Add(new Run(firstPart));
            Run run = new Run(secondPart);
            run.Background = Brushes.Aqua; ;
            textBlock.Inlines.Add(run);
            return textBlock;


        }

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

而Xaml是

<Window x:Class="WpfApplication1.MainWindow"
        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"        
        mc:Ignorable="d"
        Title="Android Search in WPF" Height="350" Width="525"
        Loaded="Window_Loaded"
        xmlns:local="clr-namespace:WpfApplication1.Converters">

    <Window.Resources>
        <local:HighlightNames x:Key="colorrootnode"/>
    </Window.Resources>

    <Grid>        
        <ListBox 
            x:Name="lstNames" HorizontalAlignment="Left" 
            Height="207" Margin="37,64,0,0" 
            VerticalAlignment="Top" Width="350"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding PersonList}">

            <ListBox.ItemTemplate>
                <DataTemplate>                   
                    <ContentControl Content="{Binding PersonName, Converter={StaticResource colorrootnode}}">
                    </ContentControl>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

    </Grid>
</Window>

输出

enter image description here

但如果匹配任何一个名字,它应该只突出显示姓名中的&#39;

因此,在第一种情况下,只会突出显示 arMahesh Nagar 的起始 ar

对于案例2, arPriyank arSark ,只会突出显示两个&#39;&#39;

因为他们搜索字符串以。开头。

提前致谢。

2 个答案:

答案 0 :(得分:1)

如果您需要MVVM解决方案,我建议您考虑字符串是字符集合。您可以将字符串和字符视为模型,因此必须使用viewmodel将它们包装起来。我不知道你的整个项目,所以我只提出一个样本。 让我们看看从XAML开始的代码:

<Window x:Class="HighlightableSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:HighlightableSample"
        Title="MainWindow" Height="300" Width="300">

    <Window.Resources>
        <local:BoolBrushConverter x:Key="BoolBrushConverter" />
        <local:BoolFontWeightConverter x:Key="BoolFontWeightConverter" />
    </Window.Resources>


    <StackPanel>
        <TextBox HorizontalAlignment="Stretch" Margin="4" Text="{Binding SearchFor, UpdateSourceTrigger=PropertyChanged}" />

        <ListBox ItemsSource="{Binding HlStrings}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <ItemsControl ItemsSource="{Binding Mode=OneTime}" VerticalAlignment="Center">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal" />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=SourceChar, Mode=OneWay}"
                               Background="{Binding Path=IsHighlighted, Mode=OneWay, Converter={StaticResource BoolBrushConverter}}"
                               FontWeight="{Binding Path=IsHighlighted, Mode=OneWay, Converter={StaticResource BoolFontWeightConverter}}" />
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

    </StackPanel>
</Window>

Window DataContext设置了一个MainViewModel类的新实例:

public class MainViewModel : NotifyPropertyChangedImpl
{
    private string searchFor;
    private ObservableCollection<HighlightableString> hlStrings =
        new ObservableCollection<HighlightableString>();

    public MainViewModel()
    {
        hlStrings.Add(new HighlightableString("arMahesh Nagar"));
        hlStrings.Add(new HighlightableString("arPriyank arSark"));
    }

    public ObservableCollection<HighlightableString> HlStrings
    {
        get
        {
            return hlStrings;
        }
    }

    public string SearchFor
    {
        get
        {
            return searchFor;
        }
        set
        {
            searchFor = value;
            OnPropertyChanged("SearchFor");

            foreach (HighlightableString hls in hlStrings)
            {
                hls.Highlight(searchFor);
            }
        }
    }
}

NotifyPropertyChangedImpl是一个只实现INotifyPropertyChanged接口的基类。 正如我所说,现在我们需要一个ViewModel作为字符串。我称之为HighlightableString

public class HighlightableString : List<HighlightableChar>
{
    private readonly string sourceString;

    public HighlightableString(string value)
    {
        sourceString = value;
        foreach (char currentChar in sourceString)
        {
            Add(new HighlightableChar(currentChar));
        }
    }

    public void Highlight(string searchString)
    {
        int index = sourceString.IndexOf(searchString);
        if (index > -1)
        {
            for (int i = 0; i < Count; i++)
            {
                if (i < index)
                {
                    this[i].IsHighlighted = false;
                }
                else if (i >= index && i < index + searchString.Length)
                {
                    this[i].IsHighlighted = true;
                }
                else
                {
                    index = sourceString.IndexOf(searchString, index + 1);
                    this[i].IsHighlighted = false;
                }
            }
        }
        else
        {
            foreach (HighlightableChar c in this)
            {
                c.IsHighlighted = false;
            }
        }
    }
}

我以类似的方式编写了HighlightableChar类:

public class HighlightableChar : NotifyPropertyChangedImpl
{
    private readonly char sourceChar;
    private bool isHighlighted;

    public HighlightableChar(char value)
    {
        sourceChar = value;
    }

    public char SourceChar
    {
        get
        {
            return sourceChar;
        }
    }

    public bool IsHighlighted
    {
        get
        {
            return isHighlighted;
        }
        set
        {
            if (isHighlighted != value)
            {
                isHighlighted = value;
                OnPropertyChanged("IsHighlighted");
            }
        }
    }
}

现在我们只需要两个 - 非常简单 - 转换器:

public class BoolBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool boolValue = (bool)value;
        return boolValue ? Brushes.Yellow : Brushes.Transparent;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

public class BoolFontWeightConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool boolValue = (bool)value;
        return boolValue ? FontWeights.Bold : FontWeights.Normal;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

运行项目你将看到Window:只需在第一个TextBox中写一个文本(你可以尝试输入“ar”),你会看到ListBox中的字符串中突出显示了一些字符。我希望这个样本可以帮到你。

答案 1 :(得分:0)

首先,您应该更改转换器以使用多次运行更改返回textBlock。

public class HighlightNames : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                           var personName = ((Person)value).PersonName;

                var textBlock = new TextBlock();
                textBlock.TextWrapping = TextWrapping.Wrap;

                // Add logic to split the string into multiple string to split based on string to highlight. For ex in Mahesh Nagar, ar should be highlighted so string would split into two 1) Mahesh Nag 2) ar

    string toSearch = "ar";
    var regex = new Regex(toSearch);

    int currentIndex = 0;
    var matches = regex.Matches(inputString);
    for (int index = 0; index < matches.Count; index++)
    {
        Match match = matches[index];

        if (match.Index == 0)
        {
                Run run = new Run(personName.Substring(0, toSearch.Length));
                run.Background = Brushes.Aqua;;
                textBlock.Inlines.Add(run);
        }
        else
        {
                           textBlock.Inlines.Add(new Run(personName.Substring(currentIndex, match.Index - currentIndex)));                             
                Run run = new Run(personName.Substring(match.Index, toSearch.Length)
                run.Background = Brushes.Aqua;;
                textBlock.Inlines.Add(run);


        }
                }

    if (currentIndex < inputString.Length)
    {
                 textBlock.Inlines.Add(new Run(personName.Substring(currentIndex, inputString.Length - currentIndex))); 
    }
                 return textBlock;
            }

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

和xaml中的第二个定义ContentControl而不是TextBlock

<ContentControl Content="{Binding PersonName, Converter={StaticResource colorrootnode}">
</ContentControl>