2

I am binding some property into my TextBlock:

<TextBlock 
    Text="{Binding Status}" 
    Foreground="{Binding RealTimeStatus,Converter={my:RealTimeStatusToColorConverter}}" 
    />

Status is simple text and RealTimeStatus is enum. For each enum value I am changing my TextBlock Foreground color.

Sometimes my Status message contains numbers. That message gets the appropriate color according to the enum value, but I wonder if I can change the colors of the numbers inside this message, so the numbers will get different color from the rest of the text.

Edit.

XAML

<TextBlock my:TextBlockExt.XAMLText="{Binding Status, Converter={my:RealTimeStatusToColorConverter}}"/>

Converter:

public class RealTimeStatusToColorConverter : MarkupExtension, IValueConverter
{
    // One way converter from enum RealTimeStatus to color. 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is RealTimeStatus && targetType == typeof(Brush))
        {
            switch ((RealTimeStatus)value)
            {
                case RealTimeStatus.Cancel:
                case RealTimeStatus.Stopped:
                    return Brushes.Red;

                case RealTimeStatus.Done:
                    return Brushes.White;

                case RealTimeStatus.PacketDelay:
                    return Brushes.Salmon;

                default:
                    break;
            }
        }

        return null;
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public RealTimeStatusToColorConverter()
    {
    }

    // MarkupExtension implementation
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}
david hol
  • 1,272
  • 7
  • 22
  • 44
  • It's complicated. One method is to separate your Status message into multiple messages, with the numeric content in a separate viewmodel property. But that's horrible. A more pure MVVM way would be to write another `ValueConverter` for `Status`, which would parse the message text and dynamically create multiple XAML `Run` instances with appropriate attributes. But that's a fair amount of work. – 15ee8f99-57ff-4f92-890c-b56153 Dec 18 '15 at 15:47
  • That's sound fine, can i have code example please ? (i am new developer...) – david hol Dec 18 '15 at 18:05

2 Answers2

0

You can use the Span, although it will take a bit more work to set up your TextBlock.

Take a look at this page, which I find to be fairly simple but comprehensive, and from which I've pulled this snippet:

        <TextBlock Margin="10" TextWrapping="Wrap">
                This <Span FontWeight="Bold">is</Span> a
                <Span Background="Silver" Foreground="Maroon">TextBlock</Span>
                with <Span TextDecorations="Underline">several</Span>
                <Span FontStyle="Italic">Span</Span> elements,
                <Span Foreground="Blue">
                        using a <Bold>variety</Bold> of <Italic>styles</Italic>
                </Span>.
        </TextBlock>
Wonko the Sane
  • 10,623
  • 8
  • 67
  • 92
0

Here's an attached property which parses arbitrary text as XAML TextBlock content, including Run, Span, Bold, etc. This has the advantage of being generally useful.

I recommend you write a ValueConverter which replaces the numbers in your Status text with appropriate markup, such that when you give it this text...

Error number 34: No custard for monkey kitty.

...it would convert that into this text:

Error number <Span Foreground="Red">34</Span>: No custard for monkey kitty.

You already know how to do value converters, and text substitution with regular expressions is a different subject entirely.

XAML usage:

<TextBlock
    soex:TextBlockExt.XAMLText={Binding Status, Converter={my:redNumberConverter}}"
    />

If it were me I'd go hog wild and make the color a ConverterParameter.

Here's the C# for that attached property:

using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace StackOverflow.Examples
{
    public static class TextBlockExt
    {
        public static String GetXAMLText(TextBlock obj)
        {
            return (String)obj.GetValue(XAMLTextProperty);
        }

        public static void SetXAMLText(TextBlock obj, String value)
        {
            obj.SetValue(XAMLTextProperty, value);
        }

        /// <summary>
        /// Convert raw string from ViewModel into formatted text in a TextBlock: 
        /// 
        /// @"This <Bold>is a test <Italic>of the</Italic></Bold> text."
        /// 
        /// Text will be parsed as XAML TextBlock content. 
        /// 
        /// See WPF TextBlock documentation for full formatting. It supports spans and all kinds of things. 
        /// 
        /// </summary>
        public static readonly DependencyProperty XAMLTextProperty =
            DependencyProperty.RegisterAttached("XAMLText", typeof(String), typeof(TextBlockExt),
                                                 new PropertyMetadata("", XAMLText_PropertyChanged));

        private static void XAMLText_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is TextBlock)
            {
                var ctl = d as TextBlock;

                try
                {
                    //  XAML needs a containing tag with a default namespace. We're parsing 
                    //  TextBlock content, so make the parent a TextBlock to keep the schema happy. 
                    //  TODO: If you want any content not in the default schema, you're out of luck. 
                    var strText = String.Format(@"<TextBlock xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">{0}</TextBlock>", e.NewValue);

                    TextBlock parsedContent = System.Windows.Markup.XamlReader.Load(GenerateStreamFromString(strText)) as TextBlock;

                    //  The Inlines collection contains the structured XAML content of a TextBlock
                    ctl.Inlines.Clear();

                    //  UI elements are removed from the source collection when the new parent 
                    //  acquires them, so pass in a copy of the collection to iterate over. 
                    ctl.Inlines.AddRange(parsedContent.Inlines.ToList());
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(String.Format("Error in Ability.CAPS.WPF.UIExtensions.TextBlock.XAMLText_PropertyChanged: {0}", ex.Message));
                    throw;
                }
            }
        }

        public static Stream GenerateStreamFromString(string s)
        {
            MemoryStream stream = new MemoryStream();
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }
    }
}
  • Thanks a lot for your answer, can you post redNumberConverter code ? i think that someting is missing also usage of this TextBlockExt class. – david hol Dec 18 '15 at 18:59
  • @davidhol What problem are you having with the usage? What's the trouble you're having with the ValueConverter you wrote? It should be trivial. – 15ee8f99-57ff-4f92-890c-b56153 Dec 18 '15 at 19:01
  • Since i am new developer ans barely familiar with WPF this converter isn't trivial for me at this moment. – david hol Dec 18 '15 at 19:30
  • @davidhol It's customary on SO to ask people to help you finish tasks you got stuck on. People here turn unhelpful when you're not even willing to make an effort to write the code yourself. – 15ee8f99-57ff-4f92-890c-b56153 Dec 18 '15 at 19:33
  • I didn't understand whats Error number 34: No custard for monkey kitty. means. – david hol Dec 18 '15 at 19:39
  • @davidhol It's an arbitrary string that has a number in the middle. If it would help, you could give me the actual text of one of your status messages. – 15ee8f99-57ff-4f92-890c-b56153 Dec 18 '15 at 19:49
  • OK after add everything this compiled but the message status is missing, what i need to change in my converter ? – david hol Dec 18 '15 at 19:55
  • @davidhol Add the code to your question, I'll take a look. – 15ee8f99-57ff-4f92-890c-b56153 Dec 18 '15 at 19:57
  • @davidhol That's the converter that converts your RealTimeStatus enum to a color. You'll need to write an additional converter to add markup to your Status string. If you want to have the number color depend on the RealTimeStatus value, you'll need a MultiValueConverter: https://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter(v=vs.110).aspx. SO should have plenty of examples of writing and using MultiValueConverters. To add the markup to the text, you'll want to use System.Text.RegularExpression. Again, plenty of examples on SO. – 15ee8f99-57ff-4f92-890c-b56153 Dec 18 '15 at 20:06
  • @davidhol I should have been more clear in my original comment: "A fair amount of work" didn't quite get the idea across. This is a complicated task for a novice to take on, just to make a number pink. It might not be worth the effort. But writing code is the only way to learn to write code. – 15ee8f99-57ff-4f92-890c-b56153 Dec 18 '15 at 20:07