简体   繁体   中英

Why isnt my RichTextBox not changing foreground color databinding

So I am currently trying to print out the messages from a process to a richtextbox, and everything is working great besides that I want it to show the error messages in red text and the normal messages in green.

For some reason it's all green and I would assume it's becasue I am binding the foreground and not Appending to the RTB but I am not sure.

How do I properly make it to where the error messages turns red and the normal messages turn green.

MainWindow.cs

 public partial class MainWindow : Window
    {
        static Server server = new Server();
        private readonly Thread ConsoleThread = new Thread(() => { server.Start("Start.bat"); });

        public MainWindow()
        {
            InitializeComponent();
            DataContext = server;
        }

        private void ButtonStart(object sender, RoutedEventArgs e)
        {
            ConsoleThread.Start();
        }

        private void tbConsole_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
            => tbConsole.ScrollToEnd();
    }

Server.cs

class Server : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private Process pServer = new Process();

        private Brush color;

        public Brush MessageColor
        {
            get { return color; }
            set
            {
                color = value;
                OnPropertyChanged("MessageColor");
            }
        }

        private string outStream;
        public string OutputStream
        {
            get { return outStream; }
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    outStream += value + Environment.NewLine;
                    OnPropertyChanged("OutputStream");
                }
            }
        }


        public void Start(string Path)
        {

            pServer.StartInfo.FileName = Path;
            pServer.StartInfo.UseShellExecute = false;
            pServer.StartInfo.RedirectStandardOutput = true;
            pServer.StartInfo.RedirectStandardError = true;


            pServer.OutputDataReceived += OKDataReceived;
            pServer.ErrorDataReceived += ErrorDataReceived;


            pServer.Start();

            pServer.BeginOutputReadLine();
            pServer.BeginErrorReadLine();
        }

        private void OKDataReceived(object sender, DataReceivedEventArgs e)
        {
            MessageColor = Brushes.Green;
            OutputStream = e.Data;
        }

        private void ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            MessageColor = Brushes.Red;
            OutputStream = e.Data;
        }

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handle = PropertyChanged;
            if (handle != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

XAML

 <Grid>
        <Button Click="ButtonStart" Content="Start" Margin="325,63,353,319"/>
        <xctk:RichTextBox FontSize="5" Name="tbConsole" Margin="143,149,164,81" BorderBrush="Gray" Padding="10" ScrollViewer.VerticalScrollBarVisibility="Auto" Text="{Binding Path=OutputStream, Mode=TwoWay}" Foreground="{Binding Path=MessageColor}" />
    </Grid>

Yes, I think you're right: the Foreground property refers to all text in the RichTextBox , not to individual items.

I can suggest a solution to your question, but bear with me...

Collect the messages individually

When I say "message", what I mean is anything being passed to OKDataReceived and ErrorDataReceived .

There's an issue in your architecture in that you don't distinguish between "good" messages and "bad" messages from your process. OutputStream contains both, and when MessageColor changes it will change for all previous messages, not just the latest one.

I would suggest changing creating a Message class with the message text and a flag for whether it's a "good" message or a "bad" message. When you get new data from the process you create the Message and set the flag for "good" or "bad" accordingly.

Then replace OutputStream with a property of type ObservableCollection<Message> . After creating a new Message from the process you can add it to this ObservableCollection<Message> . This is the property that will ultimately get bound to your view. Note that when a new item is added to an ObservableCollection it will automatically refresh the binding.

Now, I haven't forgotten your original question as to how to display text on the view with different colours. I can give you a suggestion for this, but it depends on having an ObservableCollection<Message> in the model.

Convert the messages to a FlowDocument

Bind the ObservableCollection<Message> to the RichTextBox control but use an IValueConverter to convert all the Messages into a FlowDocument . Each Message would be converted into a Paragraph with a Run which would contain the text. You would then set the Foreground property on the Paragraph to the correct colour for the flag in the Message . You would also need to bind the property to the RichTextBox.Document property. The MSDN article for RichTextBox has a nice little example that I think you'll be able to figure out from there.

The main disadvantage to this approach is that whenever the process emits a new message, it would require the entire ObservableCollection to be converted to a FlowDocument again, not just the new item. But I think the performance impact from that would be minimal.

I think this would be the best approach. At the end you would have a view model with distinguishable messages, a converter that can be modified as needed, and only a few changes to the view itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM