-1

I am trying to create a function for an app but trying it out first in a test app. Basically I have one app with three buttons when a user presses say a green button i want the button to send a string to another app which is displaying an image. This app will read the string and display the green image. What is the best way for sending string data from one app to another ?

I have looked into multiple ways of going about app communication but i havent found a good explanation

o0JAC0B0o
  • 7
  • 2

1 Answers1

0

I propose a different method to let the Applications communicate (in this case, it's a on-way interaction: only one app talks to the other).

Using UI Automation, you can get or set the value of Controls' properties in another application in a semi-transparent way. Also receive or cause events, detect when an application is run or closed an other fun activities. General documentation here:

Windows Accessibility API reference - UI Automation
.Net Framework UI Automation Fundamentals

You have two simple applications with a simple requirement, so this task is quite straightforward:

  • One application awaits commands (or it's simply there).
  • Another applications sends commands that the first application needs to interpret and act upon.

Since you need to send some strings that change the color of a PictureBox, we can use a TextBox control which will receive the strings and convert the command in a Color or trigger another, predefined, behaviour.

Using UI Automation, the two applicatons can be run independently, then acknowledge the existence of the ohter in different ways. For example, using a WindowPattern.WindowOpenedEvent, we can detect when an application is run and determine if it's interesting in different ways. See these questions for an implementation:

Run event when any Form loads
This other question, to identify an application based on the content of a child control.

Here (to keep it short), I'm simply enumerating the running application which have an interface and choose one using a ComboBox as selector.

private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e)
{
    var window = AutomationElement.FromHandle((IntPtr)comboBox1.SelectedValue);
    if (window != null) {
        GetCommElement(window, ControlType.Edit);
    }
}

private void GetCommElement(AutomationElement parent, ControlType controlType)
{
    element = parent.FindFirst(TreeScope.Subtree, 
        new PropertyCondition(AutomationElement.ControlTypeProperty, controlType));
}

If the TextBox of the selected application is found, we get its ValuePattern (a UI Automation pattern that allows to set the value of a control) and set its Text property to a string that corrensponds to the name of a Color:
(Note that only a non-multiline Edit control (a WinForms TextBox) supports the ValuePattern. A multiline Edit control does not, just the TextRangePattern)

private void btnColor_Click(object sender, EventArgs e)
{
    if (element == null) return;
    var ctrl = sender as Control;
    if (element.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern)) {
        (pattern as ValuePattern).SetValue(ctrl.Text);
        this.Activate();
    }
}

The application that receives the commands, uses its TextBox control's TextChanged event to receive the strings from the other application and decide what to do:
(Note that the TextBox can be offscreen, but it's Visible property must be set to true)

private void textBox1_TextChanged(object sender, EventArgs e)
{
    var color = Color.FromName((sender as Control).Text);
    pictureBox1.BackColor = (color.IsKnownColor) ? color: Color.White;
}

Sample functionality:

UI Automation ValuePattern


Complete source code of the two applications:
UI Automation require a reference to these assemblies: UIAutomationClient and UIAutomationTypes

UIAClientApp:

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Automation;

public partial class UIAClientApp : Form
{
    AutomationElement element = null;
    private void comboBox1_DropDown(object sender, EventArgs e)
    {
        var dict = new Dictionary<IntPtr, string>();
        foreach(var proc in Process.GetProcesses().Where(p => p.Id > 4 && 
            p.MainWindowHandle != this.Handle && 
            !string.IsNullOrEmpty(p.MainWindowTitle)).ToList())
        {
            dict.Add(proc.MainWindowHandle, proc.MainWindowTitle);
        }
        comboBox1.DisplayMember = "Value";
        comboBox1.ValueMember = "Key";
        comboBox1.DataSource = dict.ToList();
    }

    private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e)
    {
        lblCurrentApp.Text = comboBox1.SelectedItem.ToString();
        var window = AutomationElement.FromHandle((IntPtr)comboBox1.SelectedValue);
        if (window != null) {
            GetCommElement(window, ControlType.Edit);
        }
    }

    private void GetCommElement(AutomationElement parent, ControlType controlType)
    {
        element = parent.FindFirst(TreeScope.Subtree, 
            new PropertyCondition(AutomationElement.ControlTypeProperty, controlType));
    }

    private void btnColor_Click(object sender, EventArgs e)
    {
        if (element is null) return;
        var ctrl = sender as Control;
        if (element.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern)) {
            (pattern as ValuePattern).SetValue(ctrl.Text);
            this.Activate();
        }
    }
}

UIATestApp:

using System.Drawing;
using System.Windows.Forms;

public partial class UIATestApp : Form
{
    public UIATestApp() => InitializeComponent();

    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        var color = Color.FromName((sender as Control).Text);
        pictureBox1.BackColor = (color.IsKnownColor) ? color: Color.White;
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61