Avatar of johnny_device
johnny_device
 asked on

Modify Windows Form from another after Application::Run() C++

This question relates to 'Microsoft Visual C++ Express'
Windows Forms application
-------------------------------------------------------------------------
I have a program with two Forms.  For Form1 to be able to modify Form2, I have instantiated an object of the Form2 Class within the Form1 Class, like this:
.........................................................................................
 public ref class Form1 : public System::Windows::Forms::Form
  {
    static Form2 ^ Form2Obj = gcnew Form2();
    public:      
      Form1(void)
      {
        InitializeComponent();
        .......... etc.
.........................................................................................
(thanks to RV for telling me about this, but I clearly still don't "get it")

I thought I'd be high and dry then, and be able to modify Form2 from Form1 like this:
Form2Obj->label1->Text = "TEST 1"; (for example).
However, this is only fine so long as I don't need to modify Form2 after it is run as an application in a separate thread, or after the dialogue is shown.  For example, if I do this:
Form2Obj->label1->Text = "TEST 1";
Form2Obj->ShowDialog();
Form2Obj->label1->Text = "TEST 2";
, then the Form2 window shows 'TEST 1' ( 'TEST 2' does not show up on the label Text).

How do I update the fields on Form2 dynamically, after Application::Run or ShowDialog?  Do I need to set up some event-driven dialogue between the two Forms?

What I want to achieve is to have Form1 take care of network connections; Form2 to run in a separate Thread and accept user input; functions in Form1 to be able to update the Form2 GUI dynamically as network events occur.


Microsoft Development.NET ProgrammingVisual C++.NET

Avatar of undefined
Last Comment
johnny_device

8/22/2022 - Mon
disrupt

Neun123

When accessing a control's property (i.e. label1.Text), you need to do it from the Form's Thread.

You need to use the invoke method on a Control to change thread. (have a look here: http://msdn.microsoft.com/en-us/library/a1hetckb.aspx)

I'd recommend using the Invoke method on your Form2 instance.

Hopefully this will help you.
johnny_device

ASKER
Thanks you for your prompt reply, disrupt (short and sweet).

The structure I have is already substantially the same as described at that URL, except for the fact that ShowDialog() is replaced with Show().  It is true that, in a simple scenario, invoking a modeless windows does allow Form1 to continue to modify Form2.

However, I am not sure how to apply this to the situation I'm asking about.  The link you've given doesn't go into how it works mutually, nor how to achieve this in separate Threads.

I can call Form2Obj->ShowDialog(); in a separate Thread function, but not Show(), which just hangs up.  I am also losing some graphic elements if I use Show() outside a separate Thread, rather than ShowDialog() - not sure why this is, yet.  Therefore, if I can modify a second Form started with ShowDialog(), or Application::Run(), so much the better - perhaps this is a contradiction in terms ... .

The other angle is how a button click on Form2 modifies Form1, as opposed to the other way round.  This click needs to call a public function in Form1, but I'm not sure how the linker picks this up; I've made a forward declaration at the top of Form2.h, but the linker does not make the connection, even in a simple setup without separate Threads.

Hi Neun123 - I've just read your post.  I'll need a bit of time to look into it ...thanks ...


jd


Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
johnny_device

ASKER
After a bit of experimentation, I have discovered that I can change Form2 if I place ShowDialog() in a new Thread, and then use the simple assignment  'Form2Obj->label1->Text = "TEST 1 "' after starting the Thread.  However, this is, presumably, not "safe" for the reasons Neun123 mentioned, so I have tried the following code to change it via an Invoke method:

delegate void SetTextDelegate(String^ text);
private: void SetText(String^ text) {
  if (Form2Obj->label1->InvokeRequired) {
    SetTextDelegate^ d = gcnew SetTextDelegate(this, &Form1::SetText);
    this->Invoke(d, gcnew array<Object^> { text });
  }
  else {
    Form2Obj->label1->Text = text;
  }

If I call SetText() straight after starting the ShowDialog() Thread, then the application hangs.  Any idea what's wrong?


}

Neun123

You should use

 
Form2Obj->label1->Invoke(d, gcnew array<Object^> { text });

Open in new window


to replace

this->Invoke(d, gcnew array<Object^> { text });

because "this" might not be on the right thread.

Also, you could add a try {} catch{} in SetText to make sure you don't have an exception popping out silently on a background thread (and see if you have one when the application hangs).
johnny_device

ASKER
Much obliged, Neun123, thank you.

Could you help me out with the syntax for modifying Form1 from Form2?  As shown above, I have the Form2 Class encapsulated within the Form1 Class, and it looks like I can change Form2 from Form1; however, I'm a bit stuck with GUI elements (in Form2) invoking functions based in the Form1 Class.  I'm setting up the relevant '_Click' function in Form2, and then calling a Form1 function from within it; I've got a forward declaration for the Form1 function, but the linker does not recognise it.  I figured that if it was forward declared, and given that it's in the same namespace, it would be okay, but evidently not.

jd.

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Neun123

A couple of ideas spring to mind:

1. Adding a reference to Form1 in Form2 (any reason Form2's variable is declared static?)
 --> That would require having a property in Form2 of type Form1, which you would set when you create your Form1 object, and then using that property to call Form1's functions.

2. Handling events from Form2 to Form1, which would allow for multiple consumers, but would require some work to create those events in Form2, register handlers in Form1 and handle the disposal of those handlers

3. Add a class in between Form1 and Form2, which references both objects, and exposes methods that both can call (each of those method would redirect the call to the correct FormX instance (1 or 2)) Both objects (Form1 and Form2) would need a reference (Property) that points to that in-between object.
Depending on your needs, you can add a level of abstraction (using Interfaces instead of classes in the in-between class). You could also handle multiple Form1 instances for a single Form2 instance (if required)

Option 3 seems the simplest to implement, but the choice is all yours (depending on your requirements)
johnny_device

ASKER


Thank you again, Neun123.  I'll need a bit of time to mull over your answer ... .
johnny_device

ASKER

Hello again, Neun123.

re. solution 1., Form2 is declared as static because otherwise it will not compile:
'only static data members can be initialized inside a ref class or value type'.
As you can see, an instance of Form2 is created when Form1 is initialized.  This was a step I did in trying to get the Forms to inter-communicate.  I'm not sure in practice how your first solution would work with the arrangement I've got at the moment.  When you say "add a reference to Form1 in Form2", are you talking about a slightly different scenario, where each Form is independent, rather than one encapsulated in another?  If not, I'm not sure how to avoid recursive header declaration problems.

Unfortunately, I'm not clear how to implement any of your solutions in practice.  The meat of the code is relatively easy to implement, and seems to work okay, but just this inter-communication between Forms is holding me up.  I can envisage the solutions you suggest, but can't get it to work syntactically.  Do you know of any online code examples of implementations in .NET managed C++ where this mutual Form interaction is enabled?    It would help a lot to see the actual code.
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
ASKER CERTIFIED SOLUTION
Neun123

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
johnny_device

ASKER
Neun123 -

You are a star!  I cannot really ask for more.  I will need a bit of time to get to grips with implementing this fully, but if any other new questions arise, I will post fresh threads (you've earnt your points!).

Best Regards,



jd