-1

Say I have a class that receives data over a TCP stream, parses it and changes it's properties accordingly.

public static class SomeClass
{
    static bool myBool;
    static string myMessage;

    public static void ToggleBool()
    {
        myBool = !myBool;
        // Do some other stuff here
    }

    public static UpdateMessage(string message)
    {
        System.Diagnostics.Debug.WriteLine(message);
        ProcessMessage(message);
        myMessage = message;
    }
}

Now what I want to do is have a WPF "Debugging Window" that will visually display the settings. I want to basically run a loop that updates parts of the window accordingly.

Something like:

public partial class LogWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public Async Task UpdateUI()
    {
        while(checkForUpdates)
        {
            myCheckbox.IsChecked = await SomeClass.UpdatedBoolValue();

            string newMessage = await SomeClass.NewMessageRCVD();
            txtBox.Append(newMessage);
        }
    }
}

But that has 2 obvious issues. One, I have no idea how I would make a function that doesn't burn CPU by constantly checking with a while loop. I imagine I could use a getter/setter approach though. Two, I have to update both in order for that loop to run again.

What's the best approach to this? How do update just the parts of the UI that need to be updated?

EDIT: Similar question: Write an Async method that will await a bool

Community
  • 1
  • 1
Xander Luciano
  • 3,753
  • 7
  • 32
  • 53

2 Answers2

0

Depends on how complex a implementation/your needs are.

From your example if you made SomeClass implement INotifyPropertyChanged you could easily attach a WPF window to it, and through binding the window would update automatically without any form of a loop.

If your talking about multiple classes and you want to have them all display the property information in the same window, your best bet would probably be to create a queue. In each property you wish to keep track of have the setter write to the queue. (global or singleton) Then you can easily front that information in a window, or multiple via an Observer pattern. Can also set it up to it never writes to the queue in production, or with conditional compile statements production wouldn't even have the code if that is your desire.

Kelly
  • 6,992
  • 12
  • 59
  • 76
  • Thanks! Figured it out and typed up an answer with some example code to help others get started, but you have some good info here as well. – Xander Luciano Feb 22 '17 at 07:45
0

The best way to do this is with data binding.

So we need to first define where our data is coming from. This is called the Context. This is going to come from a ViewModel which is an MVVM term. If you aren't aware of MVVM, don't worry, this can just come from any class you have. In the backend .xaml.cs code we need to add the class to our windows's DataContext. Here's what that looks like:

public partial class DebugView : Window
{
    public DebugView()
    {
        InitializeComponent();
        DataContext = new DebugViewModel();
    }
}

And in our WPF's XAML file for the window we will have a label and textbox that is defined as such:

<Label Content="{Binding ClientCount, FallbackValue='Clients: 00'}" ... />
<TextBox Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}" ... />

The text of a label is it's "content" while the text of a textbox is just "text." We add the binding keyword in there and now the text for each will be linked to the variables ClientCount and Port, repstively. So our DebugViewModel class will look like this at first:

    private string _ClientCount;
    public string ClientCount
    {
        get { return _ClientCount; }
        set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
    }
    private string _Port;
    public string Port
    {
        get { return _Port; }
        set { _Port= value; RaisePropertyChanged("Port"); }
    }

Now you don't have a Function called RaisePropertyChanged() so what I did (and I think is common practice) was I made a base class that implements the INotifyPropertyChanged and handles all the work there.

So our base class, called BaseViewModel will inherit from the INotifyPropertyChanged class, and setup everything for us. That just looks like this (feel free to just copy paste and use as is):

using System.ComponentModel;

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    internal void RaisePropertyChanged(string prop)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }

   // Other functions we want all ViewModels to have
}

and so then our DebugViewModel class will look like such:

public class ServerViewModel : BaseViewModel
{
    private string _ClientCount;
    public string ClientCount
    {
        get { return _ClientCount; }
        set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
    }
    private string _Port;
    public string Port
    {
        get { return _Port; }
        set { _Port= value; RaisePropertyChanged("Port"); }
    }

    public DebugViewModel()
    {
        // Initialize to default values
        ClientCount = $"Clients {server.clientCount}";
        Port = $"{server.port}";
    }

    // Rest of code
}

And then when you start your program it will autopopulate the fields and you when you change the data in the textbox, the string will change, and vice versa. The UpdateSourceTrigger=PropertyChanged part of our XAML declaration makes it so that the variable is updated as soon as the data in the textbox is changed (default behavior is when the textbox loses focus. e.g. you tab to the next textbox or you click away).

This is pretty cool because you can validate input dynamically as it's typed, as well as not having to worry about switching to the UI thread to update the UI, and IMO makes the code look simpler just by having it bound like this.

Xander Luciano
  • 3,753
  • 7
  • 32
  • 53