I have the following C# code:
using System;
using System.Windows.Forms;
namespace WinFormErrorExample
{
public partial class Form1 : Form
{
public static Form1 Instance;
public Form1()
{
Instance = this;
InitializeComponent();
}
public void ChangeLabel1Text(String msg)
{
if (InvokeRequired)
Invoke(new Action<String>(m => label1.Text = m), new object[] {msg});
else
label1.Text = msg;
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Instance.ChangeLabel1Text("cool");
}
}
}
}
When i'm calling the Instance.ChangeLabel1Text("cool"); nothing is happening in the GUI.
This is a small program i constructed to show my problem in a larger program.
Why is the GUI not being updated?
The call to
Application.Run(new Form1());
is blocking your application until the Form1 closes. So your subsequent line is not executed until you try to close
Of course, if you just want to test the functionality of the Instance call then remove that line after the Application.Run. Instead you need to create a separate thread that tries to call that method on the Form1 current instance
The Application.Run(new Form1()); method call performs running a standard application message loop on the current thread. So, this line Instance.ChangeLabel1Text("cool"); will be executed when the application is closed.
Why not change the text of the label inside the constructor? No static variables needed.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ChangeLabel1Text("Hello!");
}
}
This would do,
First set the text to textbox control and then Run()
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 form = new Form1();
form.Controls["ChangeLabel1Text"].Text = "cool";
Application.Run(form);
Related
Program.cs
namespace PerformanceMonitor
{
static class Program
{
private static int NumberOfCores;
private static List<int> CPULoadVals;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MonitorGUI());
NumberOfCores = getNumberOfCores();
CPULoadVals = getCoreLoadVals();
}
private static int getNumberOfCores()
{
int coreCount = 0;
foreach (var core in new ManagementObjectSearcher("SELECT * FROM Win32_Processor").Get())
{
coreCount += int.Parse(core["NumberOfCores"].ToString());
}
return coreCount;
}
...
MonitorGUI.cs
namespace PerformanceMonitor
{
public partial class MonitorGUI : Form
{
public static List<Label> labels;
private static List<int> CPULoadVals;
public MonitorGUI()
{
InitializeComponent();
}
public void Form1_Load(object sender, EventArgs e)
{
...
}
Debugging the app I can see that InitializeComponent() is invoked causing a new form to be created (Application.Run(new MonitorGUI());) but trying to step through after that and nothing is called. The method on form load is not even called even though I can visually see that it's loaded
Application.Run()
Begins running a standard application message loop on the current thread, and makes the specified form visible.
This method blocks and only returns when you close the Form passed as argument. So all calls after that are executed when you close your main window.
You might want to change the order:
[STAThread]
static void Main()
{
NumberOfCores = getNumberOfCores();
CPULoadVals = getCoreLoadVals();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MonitorGUI());
}
And Form1_Load() is only called if you subscribed to the Load event of the Form:
public MonitorGUI()
{
InitializeComponent();
Load += Form1_Load; // <--- subscribe to the event
}
But this can also be done in designer. Check if you have set this event correctly.
i have a console application that starts a windows form in a separate thread like this :
static void Main(string[] args)
{
Thread t = new Thread(StartForm);
t.Start();
}
static public void StartForm()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
Form Conatains Richtextbox Control .
My question : How do i write Text to the Richtextbox control in the form from my application (and from anyThread)?
PS: i also Need the Console to stay.
You can use Invoke to write text to RichTextBox from background thread.
In Form1 designer, change richTextBox1.Modifiers to public, in order to access it form other thread.
static void Main(string[] args)
{
Thread t = new Thread(StartForm);
t.Start();
string text = Console.ReadLine();
form.UIThread(() => form.richTextBox1.Text += text);
Console.ReadLine();
}
public static Form1 form;
public static void StartForm()
{
form = new Form1();
Application.EnableVisualStyles();
Application.Run(form);
}
public static void UIThread(this Control control, Action action)
{
if (control.InvokeRequired) // You're access from other thread
{
control.BeginInvoke(action); // Invoke to access UI element
}
else
{
action.Invoke();
}
}
You need to use Invoke or BeginInvoke if you want to update your form's content from another thread. You can check whether or not you need to do so by checking the InvokeRequired property. While you can pass any Delegate, you should pass a MethodInvoker delegate, which is a special delegate made for use with Windows Forms.
For example:
if(form.TheRichTextBox.InvokeRequired)
{
form.TheRichTextBox.Invoke(new MethodInvoker(() =>
{
form.TheRichTextBox.Text += "I had to be invoked!";
}));
}
else
{
form.TheRichTextBox.Text += "I didn't have to be invoked!";
}
So I tried to create a new form and reference it...the compiler didn't mind this but it clearly wasn't changing the visibility of my picturebox. this is how I was calling my method found in my form, FROM my c# script.
Form1 updateForm = new Form1();
updateForm.setLights();
It called the method, and seemed like it worked! Until I read a post about instancing forms, and how by creating a "new" instance of Form1, that anything referenced by my updateForm would not change what I would see on my Form1.
So what I need to do is to call the function in setLights() which is in my Form1, and get it to change the visibility of my image on that form, from my C# code. Please see below (i understand the issue of the instancing problem mentioned above, but I left it in so that hopefully it will give better insight into what I am "trying" to do :) ALSO, please keep in mind that setLightCall() is running in a separate thread. Thanks in advance!
This code is also in my main c# script, and is the main function that I use to call my threads
static void Main(string[] args)
{
Thread FormThread = new Thread(FormCall);
FormThread.Start();
Thread setLightThread = new Thread(setLightCall);
setLightThread.Start();
log4net.Config.XmlConfigurator.Configure();
StartModbusSerialRtuSlave();
}
This code is in my main C# script
public void setLightCall(Form1 parent)
{
Form1 updateForm = new Form1();
while(true)
{
updateForm.setLights();
}
}
The below code is in my form1
public void setLights()
{
Input1GreenLight.Visible = false;
}
Here is an example of what I think you are wanting to try. Note the use of Invoking and delegates to be able to access the PictureBox's Visible method. I had to add the System.Windows.Forms Namespace to the Console Application to be able to access the instance of the Form that was created in the FormThread Method, this is assuming that you only have 1 Form in your FormCollection.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Forms;
namespace ConsoleApplication59
{
class Program
{
static void Main(string[] args)
{
Thread FormThread = new Thread(FormCall);
FormThread.Start();
Thread.Sleep(2000); //Sleep to allow form to be created
Thread setLightThread = new Thread(setLightCall);
setLightThread.Start(Application.OpenForms[0]); //We can get by with this because just one form
Console.ReadLine();
}
public static void setLightCall(object parent)
{
Form1 updateForm = (Form1)parent;
while (true)
{
updateForm.Invoke(updateForm.setLights, new object[] { false });
}
}
public static void FormCall()
{
Application.Run(new Form1());
}
}
}
Form1
public partial class Form1 : Form
{
public delegate void Lights(bool state);
public Lights setLights;
public Form1()
{
InitializeComponent();
setLights = new Lights(setLightsDelegate);
}
public void setLightsDelegate(bool state)
{
Input1GreenLight.Visible = state;
}
}
Here is what i want to do
// pseudo code
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 myForm = new Form1();
Application.Run(myForm);
while(true)
{
string a = readline();
}
form1.show(a)
In other words , I need the form always show the input. but the code above will stop after 'Application.Run(myForm);'. The reason I don't write such code in the form1 class is the main part of code is run on a machine learning engine written in F#, and because F# doesn't have a good visual designer. So I am trying to create a simple form1.dll, and use it to plot the result over time.
So my problem is I only can initialise the form, but I can't update it over time.
Any hints will be appreciated.
You're trying to do 2 things at the same time, so your application should reflect that by using 2 threads. Next, the Form's Show() method does not accept a string, so you need to implement your own method.
Here's a C# 2.0 WinForms solution. The program runs the thread and processes the console input:
static class Program
{
[STAThread]
private static void Main()
{
// Run form in separate thread
var runner = new FormRunner();
var thread = new Thread(runner.Start) {IsBackground = false};
thread.Start();
// Process console input
while (true)
{
string a = Console.ReadLine();
runner.Display(a);
if (a.Equals("exit")) break;
}
runner.Stop();
}
}
The FormRunner takes care about thread invocation:
internal class FormRunner
{
internal Form1 form = new Form1();
internal void Start()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(form);
}
private delegate void StopDelegate();
public void Stop()
{
if (form.InvokeRequired)
{
form.Invoke(new StopDelegate(Stop));
return;
}
form.Close();
}
private delegate void DisplayDelegate(string s);
public void Display(string s)
{
if (form.InvokeRequired)
{
form.Invoke(new DisplayDelegate(form.Display), new[] {s});
}
}
}
And Form1 just needs something to display:
public void Display(string s)
{
textBox1.Multiline = true;
textBox1.Text += s;
textBox1.Text += Environment.NewLine;
}
I have a simple forms program that I have been fighting with for a while now. I simply want to be able to call a method from a different class file (when a certain step is triggered in the code in that class file) in order to insert a string in the listBox.
Here is my main method, pretty standard:
class Program
{
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
Here is the function which resides in my MainForm.cs file, which I can call just fine from that class file (via 'TextToBox(myString);'):
public partial class MainForm : Form
{
...
// Function to output results to main Listbox window
public void TextToBox(string aString)
{
// Place messages in Main Display list box window
this.listBox1.Items.Insert(0, aString);
}
...
}
But my problem is when I am in another class and I want to call 'TextToBox(myString);'. If I create another object reference of the MainForm, the code compiles fine but nothing will show up in the listBox. How do I do this? I cannot simply make TextToBox() static. I know I must create the object reference but I can't figure out how to reference the ORIGINAL MainForm, the one that was created in the Main method. Thanks in advance...
This will work, but only when you have one instans of MainForm.
public class MainForm : Form
{
public MainForm()
{
Instance = this;
}
public static MainForm Instance { get; private set; }
// Function to output results to main Listbox window
public void TextToBox(string aString)
{
// Place messages in Main Display list box window
this.listBox1.Items.Insert(0, aString);
}
}
public class Other
{
public void AddTextToListBox()
{
MainForm.Instance.TextToBox("Test");
}
}
...Edit...
Alternative:
class Program
{
public static MainForm MainFormInstance;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainFormInstance = new MainForm();
Application.Run(MainFormInstance);
}
}
public class Other
{
public void AddTextToListBox()
{
Program.MainFormInstance.TextToBox("Test");
}
}
I would just pass a delegate to the other class.
/* this runs (previous code was not guaranteed to run) */
class OtherClass
{
public delegate void TextToBox(string s);
TextToBox textToBox;
int next = 0;
public OtherClass(TextToBox ttb)
{
textToBox = ttb;
}
public void SendSomeText()
{
textToBox(next.ToString());
next++;
}
}
I'm assuming you'll be instantiating OtherClass from MainForm. Is this how you're calling "OtherClass"?
public partial class MainForm : Form
{
OtherClass otherClass;
public MainForm()
{
/* just two controls -- listBox1 and button1 */
InitializeComponent();
otherClass = new OtherClass(this.TextToBox);
}
public void TextToBox(string aString)
{
listBox1.Items.Add(aString);
}
private void button1_Click(object sender, EventArgs e)
{
otherClass.SendSomeText();
}
}
On a button click the next numeric value is added at the beginning of the ListBox. You'll have to post some of your code if you need further help.
alternatively you could use a singleton pattern, or static methods and make sure you include the class in a 'using' statement at the top of your program