Programmatically click custom button

Hello!

I am trying to programmatically click a boolean button that is on a custom grid created by an Indicator.

I’ve tried just changing the boolean variable that clicking the button sets, but that leaves the button in the wrong state, and then it has to be clicked twice to get back to its proper functionality. I want the button to actually be clicked, and then have that action trigger all of the things that clicking the button does (which includes setting the variable, but also changes the appearance of the button).

Here’s what doesn’t work:

		if (goStop == true && orderButtonClicked == false)
		{
			OnMyButtonClick(null, new RoutedEventArgs());
		}

This crashes the Indicator, saying “Object reference not set to an instance of an object.”

Something else that doesn’t work is:

		if (goStop == true && orderButtonClicked == false)
		{
			ChartControl.Dispatcher.InvokeAsync((Action)(() =>
			{
				OnMyButtonClick(null, new RoutedEventArgs());
			}));
		}

This doesn’t crash the Indicator, but it doesn’t do anything else, either. There is no change in the appearance of the button, nor is there any output from the Prints that trigger when the button is clicked.

I have seen some references to people auto-clicking buttons on the Chart Trader Panel, but I don’t know how that would translate to a custom button that I created.

Any help would be greatly appreciated. Thank you!

OnMyButton() click is throwing an exception for some reason. When run directly the exception is in the ninja script context. When run via invoke the exception occurs on the UI thread and is probably being caught and suppressed.

What is in your OnMyButton() handler? does it use the sender argument, because you are passing null?

This is borrowed code. I changed the name and appearance of the button, but otherwise I cut-and-pasted. It works fine as long as I manually click the button.

	private void OnMyButtonClick(object sender, RoutedEventArgs rea)
	{
		System.Windows.Controls.Button button = sender as System.Windows.Controls.Button;
		if (button != null)
		{
			orderButtonClicked = !orderButtonClicked;
		}
		if (orderButtonClicked)
		{
			button.Content		= "Auto Order";
			button.Background	= Brushes.Yellow;
			button.Foreground	= Brushes.Black;
			button.FontStyle	= FontStyles.Oblique;
			button.FontWeight	= FontWeights.DemiBold;
			autoOrderOn			= true;
			Print(button.Name + " Clicked, autoOrderOn = " + autoOrderOn.ToString());
		}
		else
		{
			button.Content		= "Manual Order";
			button.Background	= Brushes.DarkGreen;
			button.Foreground	= Brushes.White;
			button.FontStyle	= FontStyles.Normal;
			button.FontWeight	= FontWeights.Normal;
			autoOrderOn			= false;
			Print(button.Name + " Clicked, autoOrderOn = " + autoOrderOn.ToString());
		}
		
	}

That looks like the problem there:

System.Windows.Controls.Button button = sender as System.Windows.Controls.Button;
		if (button != null)
		{
			orderButtonClicked = !orderButtonClicked;
		}
		if (orderButtonClicked)
		{
                //do stuff with button

button is sender and sender is null. The “button !=null” condition just protects the orderButtonClicked assignment. It does not protect all the following code that tries to access button properties. Because button is null in this case, the call to button.Content will throw.
You’ll need to either pass in a reference to the button as sender when you call OnMyButtonClick(), or change your handler code the be fully null sender/button safe.

“I’ve tried just changing the boolean variable that clicking the button sets, but that leaves the button in the wrong state, and then it has to be clicked twice to get back to its proper functionality. I”

For me I set Triggers on the buttonStyle, then this deals with all the styling of various states of the button, example:

customTemplate.Triggers.Add(HoverTrigger(_config, _buttonStyle, btnBrushes));|
customTemplate.Triggers.Add(ToggleTrigger(_config, _buttonStyle, btnBrushes));|
customTemplate.Triggers.Add(DisabledTrigger(_config, _buttonStyle, btnBrushes));|

inside ToggleTrigger

		var toggleEffect = new Trigger();
		toggleEffect.Property = Button.TagProperty; // watches the Tag property of the Button
		toggleEffect.Value = true;  
		toggleEffect.Setters.Add(new Setter(System.Windows.Controls.Border.BackgroundProperty, btnBGColor, "ButtonBorder")); // Button Background
		toggleEffect.Setters.Add(new Setter(System.Windows.Controls.Border.BorderBrushProperty, btnBorderColor, "ButtonBorder")); // Button Border
		toggleEffect.Setters.Add(new Setter(System.Windows.Controls.Control.ForegroundProperty, btnTextColor)); // Button Text Color (contrast brush from config)

		return toggleEffect;

Then the click handler:

	private void OnButtonClick(ButtonConfig _config)
	{
		// Flip state
		_config.IsActive = !_config.IsActive;
		_config.myButton.Tag = _config.IsActive; // This alone triggers ToggleTrigger()

		// Perform generic action
		if (_config.IsActive)
			Print($"{_config.myButtonText} ON");
		else
			Print($"{_config.myButtonText} OFF");

		// Perform button specific action
			switch (_config.myButtonText)
			{
				case "Long":
					AutoOrder_Enable_Long = _config.IsActive;
					break;
				case "Short":
					AutoOrder_Enable_Short = _config.IsActive;
					break;
}

So the toggle state of the button is locked to the Tag of the button and you can do this from anywhere and the button changes toggle state:

				ChartControl.Dispatcher.InvokeAsync(() => 
				{
					BtnConfig_LONG.myButton.Tag = false; // triggers WPF style change
				});

I ended up succeeding with the separate-event-handler method.

	private void ButtonClickCode()
	{
		if (autoOrder != null)
		{
			orderButtonClicked = !orderButtonClicked;
		}
		if (orderButtonClicked)
		{
			autoOrder.Content		= "Auto Order";
			autoOrder.Background	= Brushes.Yellow;
			autoOrder.Foreground	= Brushes.Black;
			autoOrderOn				= true;
			Print(autoOrder.Name + " Clicked, autoOrderOn = " + autoOrderOn.ToString());
		}
		else
		{
			autoOrder.Content		= "Manual Order";
			autoOrder.Background	= Brushes.DarkGreen;
			autoOrder.Foreground	= Brushes.White;
			autoOrderOn				= false;
			Print(autoOrder.Name + " Clicked, autoOrderOn = " + autoOrderOn.ToString());
		}
	}
	
	private void OnMyButtonClick(object sender, RoutedEventArgs rea)
	{
		ButtonClickCode();						
	}

	protected override void OnBarUpdate()
	{	
		if (goStop == false && orderButtonClicked == true)
		{
			Print("Stop.  goStop = " + goStop.ToString());
			ChartControl.Dispatcher.InvokeAsync((Action)(() =>
			{
				ButtonClickCode();
			}));
		}
	}

Thank you for all your help!

Might try something like this…

OrdDataButton.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));

The code I included in my last post works. It works very well, in fact. I am just ironing out some last few bugs in my system, but right now it’s already working quite well.

Thank you for contributing!