Updating UI Elements from button press

Hi, I have a application running on the SCM20260D board, I have a window containing stackpanels which in turn contain Text and other elements.

I want to update the displayed text in the Text elements when a hardware button is pressed.

I have the button handlers working fine and the window also displays correctly at the start. I know I need to use a dispatcher to update the UI fields but I cannot work out how to do it!!!

I have tried your examples which use timers and they work fine but I have no idea how to perform the invoke from a button handler??

To use the dispatcher in need the ‘Object’ which is the Text element, with the timer this is passed as a parameter when intantiating the Timer object along with the callback, for example:

Timer timer = new Timer(UpdUiElements, AQValue, 2000, 1000);

Where UpdUiElements if my callback method and AQValue is the name of the Text element to be updated.

My button handler looks like:

    private static void BtnApp_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs e)
    {
        if (e.Edge == GpioPinEdge.FallingEdge)
        {
            Debug.WriteLine("App button pressed");
            Application.Current.Dispatcher.Invoke(TimeSpan.FromMilliseconds(100), _ => {
                //Currently empty as I don't know what to do
                return null;
            }, null);
        }
    }

Any help would be appreciated

Thanks

There are many ways to do that, depending on how you want your code to look.

The easiest way is to set a flag when the button value changes. Then, a timer can check the flag and update whatever you want. If I remember correctly, the sample has a timer that updates every second in the top-right corner of the screen. Check how we update those values.

Or using another thread to listen the flag seems better.

Thanks, I will look at that.

I also noted that in some examples you instantiate the screen elements at a class level rather than local to a method which gives them a more global visibility.

I will investigate further

Thanks

Hi,

I have resolved this issue and thought I would share the method here as it may be useful to others…..

To make the UI elements accessible from both the window creation and update I initialised the UIElements at the class level

using GHIElectronics.TinyCLR.UI;
using GHIElectronics.TinyCLR.UI.Controls;
using GHIElectronics.TinyCLR.UI.Threading;
using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Threading;

namespace FBBAA_Emulator
{
/// 
/// Partial class that extends the main Program class
/// purely to make the code more managable
/// 
internal partial class Program : Application
{
          public static Text AQValue = new Text();
          public static Text ThresholdValue = new Text();
...

Then when I create the window I instantiate the elements along with anything else on screen. In my case I have a panel the full size of the screen and inside that I have two panels, one on the left and one on the right. These panels will hold the UI elements I want (I have only included some here, and for the purposes of simplicity the right panel is empty).

Note that I also add a button on the screen with it’s event handler

private static UIElement Elements(DisplayController display)
{
//First create a panel for the entire screen
var fullPanel = new StackPanel(Orientation.Horizontal);
fullPanel.HorizontalAlignment = HorizontalAlignment.Center;

//Now create a left side inner panel
var leftPanel = new StackPanel(Orientation.Vertical);
leftPanel.Width = display.ActiveConfiguration.Width / 2;
leftPanel.HorizontalAlignment = HorizontalAlignment.Left;

//Now create a right side inner panel
var rightPanel = new StackPanel(Orientation.Vertical);
rightPanel.Width = display.ActiveConfiguration.Width / 2;
rightPanel.HorizontalAlignment = HorizontalAlignment.Right;

//Now build the elements in the left panel
AQValue = new Text()
{
     Font = font10pnt,
     TextContent = "AQ: 100%",
     ForeColor = Colors.Gray,
     Height = 20,
     Width = 240,
     HorizontalAlignment = HorizontalAlignment.Left,
     VerticalAlignment = VerticalAlignment.Center,
};

ThresholdValue = new Text
{
    Font = font10pnt,
    TextContent = “Threshold: 75%”,
    ForeColor = Colors.Gray,
    Height = 20,
    Width = leftPanel.Width,
    HorizontalAlignment = HorizontalAlignment.Left,
    VerticalAlignment = VerticalAlignment.Center,
};

var txtSettings = new Text(font10pnt, “Change Settings”)
{
     VerticalAlignment = VerticalAlignment.Center,
     HorizontalAlignment = HorizontalAlignment.Center,
};

//Add a button to allow settings change
var Settingsbutton = new Button()
{
    Child = txtSettings,
    Width = 100,
    Height = 40
};
Settingsbutton.Click += Settingsbutton_Click;

//And assemble the left panel fields
leftPanel.Children.Add(AQValue);
leftPanel.Children.Add(ThresholdValue);
leftPanel.Children.Add(Settingsbutton);

fullPanel.Children.Add(leftPanel);
fullPanel.Children.Add(rightPanel);

return fullPanel;

}

Button click handler:

private static void Settingsbutton_Click(object sender, RoutedEventArgs e)
{

         Debug.WriteLine(“click”);
         //Test code to set the AQ back to 100% 
         app.UpdateAQ(100);
}

In Addition I have a hardware button setup by:

var BtnApp = GpioController.GetDefault().OpenPin(SC20260.GpioPin.PB7);
BtnApp.SetDriveMode(GpioPinDriveMode.InputPullUp);
BtnApp.ValueChanged += BtnApp_ValueChanged;

and the button event handler which increments AQ by one for each press:

private static void BtnApp_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs e)
    {
        if (e.Edge == GpioPinEdge.FallingEdge)
        {
            Debug.WriteLine("App button pressed");
            app.UpdateAQ(app.aq);
            app.aq++;

            if (app.aq > 100)
            {
                app.aq = 50;
            }

            app.UpdateThresold(30);
        }
    }

Now we get to the main part; how to change the value of a UIElement on screen during execution. I created a method called UpdateAQ() which sets a new value for the UIElement

public void UpdateAQ(int NewValue)
{
         Text txt = (Text)Program.AQValue;
         Application.Current.Dispatcher.Invoke(TimeSpan.FromMilliseconds(10), _ =>
         {
                  txt.TextContent = "AQ: " + NewValue.ToString() + “%”;
                  txt.Invalidate();

                  return null;
         }, null);

}

This will change the AQ Text UI element to the value passed in NewValue

I also created a method to change the value of a second UI element (called ThresholdValue)

    public void UpdateThresold(int NewValue)
    {
        Text txt = (Text)Program.ThresholdValue;

        if(Application.Current.Dispatcher.CheckAccess())
        {
            Debug.WriteLine("Thread not ready");
        }
        else
        {
            Application.Current.Dispatcher.Invoke(TimeSpan.FromMilliseconds(100), _ =>
            {
                try
                {
                    txt.TextContent = "Threshold: " + NewValue.ToString() + "%";
                    txt.Invalidate();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Invoke cuased exception " + ex.Message);
                }

                return null;
            }, null);
        }
    } 

Note that in this second method I check access to the UI thread before updating the value and also put try/catch around the update, actually these are not necessary but might be considered good practice.

So far, this seems to work well

3 Likes

For simplicity, you can update the Text elements directly from the Button handler as that runs on the UI thread. For background updates, the dispatcher time is the way to do this.

Thanks Dave

Use UI V3 that includes some fixed and improvment

1 Like