When using a basic form application in C# I am having trouble accessing the variabels within it.
So with in the form class I have
public partial class pingerform : Form
{
..
..
private System.Windows.Forms.TextBox textBox2;
public string textBox2Text
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
And then in the main application I have
Application.Run(new pingerform());
...
...
pingerform.textBox2Text.text() = str;
but I am told that there is no object reference.
Error 1
An object reference is required for the non-static field,
method, or property
'pingerform.textBox2Text.get' C:\Users\aaron.street\Documents\Visual
Studio 11\Projects\PingDrop\PingDrop\Program.cs 54 21 PingDrop
So I thought I would make the pinger form class static but it wont let me do this?
Error 1
Cannot create an instance of the static class
'PingDrop.pingerform' C:\Users\aaron.street\Documents\Visual Studio
11\Projects\PingDrop\PingDrop\Program.cs 21 29 PingDrop
How can I access the forms properties with out creating a specific instance of the form,
I have a background thread running that I want to update a text filed with in the form at regular intervals?
Cheers
Aaron
You have no choice but to create new instance and either pass it as parameter to the thread, or store it as member of your main Program class.
Example for the second option:
private static pingerform myPingerform = null;
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
myPingerform = new pingerform();
Thread thread = new Thread(new ThreadStart(UpdateTextBox));
thread.Start();
Application.Run(myPingerform);
}
private static void UpdateTextBox()
{
while (true)
{
myPingerform.textBox2.Text = DateTime.Now.Ticks.ToString();
Thread.Sleep(1000);
}
}
And don't forget to change the textbox to be public.
Note: this is simple working solution to the simple case of one background thread accessing the textbox. If you have more threads accessing it, this will break. For best practice methods that require some more work, please read this.
You cannot access properties of an instance without creating that instance, it is nonsense (or VB which is the same). And you have already created the instance which you then passed to Application.Run(). And anyway you cannot do anything with your form after Application.Run() because it returns only when app exits. If you want to do anything with the form you need to do that in some other places. And of course you cannot make the form class static because you need to create instances.
If you need to do something with a form in another thread, you need to pass the form instance to the thread when you create it. Note though that direct messing with GUI elements from non-GUI threads is a bad idea, you should use Control.BeginInvoke().
Please try this:
pingerform myForm = new pingerform();
Application.Run(myForm);
myForm.textBox2Text = "this is text";
Related
I'm a beginner with C# and I think there is something fundamental about instantiation and passing references that I just do not get.
I am trying to get the default Program class to instantiate 2 other classes, a form called frmGameUI and a class called LocHandler. If this was working correctly the LocHandler would then check the current location and assign the text properties of frmGameUI. For some reason the method to set the properties in LocHandler can not see or get my reference to frmGameUI that I instantiated in Program. What am I doing wrong?
static class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainUI());
GameUI frmGameUI = new GameUI();
frmGameUI.Show();
LocationHandler LocHandler = new LocationHandler();
LocHandler.InitializeRoom();
}
And here is the LocHandler class:
class LocationHandler
{
private string currentRoom = "LivingRoom";
public void InitializeRoom()
{
if (currentRoom == "LivingRoom")
{
frmGameUI.btnLocation1.Text = "Bedroom";
frmGameUI.btnLocation2.Text = "Kitchen";
frmGameUI.btnLocation3.Text = "Patio";
}
}
}
in LocHandler, VS is telling me that frmGameUI does not exist in this context. I'm sure there is something fundamental and simple I just don't grasp here. Any help is appreciated!
Yes, you're definitely missing some fundamental concepts. In C# variables aren't global. They have scope, see: http://msdn.microsoft.com/en-us/library/aa691132(v=vs.71).aspx. In your case, the scope of GameUI frmGameUI = new GameUI(); is local to the the method that you declared it in. So, nobody outside of that method can see that variable. That is to say, they can't see the variable by that name. That's NOT to say that you can't pass that variable to another method. So, if you need the LocationHandler class to deal with that variable, then perhaps you should pass it to the InitializeRoom method. Like this:
LocHandler.InitializeRoom(frmGameUI);
Note that your method signature would change to:
public void InitializeRoom(GameUI gameui)
and then your code in that method would refer to the gameui variable.
///<snip>
gameui.btnLocation1.Text = "Bedroom";
gameui.btnLocation2.Text = "Kitchen";
gameui.btnLocation3.Text = "Patio";
Make sense?
Execution of the Main method will stop on this line, while the main form is showing.
Application.Run(new MainUI());
So what you probably want to do is move the code to create LocationHandler above this line, and pass it in to GameUI. (and also replace MainUI with GameUI)
Create different class with initialized instances and keep them there as public's
I have a form.
In that form I create an instance of a class on a new thread because it runs some long running logic. The form also gives the user the ability to cancel this logic/thread.
That class opens a new form if input is required.
The new form sometimes appears behind the other form.
I set a property on the class:
public Form ParentForm{get;set;}
I can now do:
MyForm form = new MyForm();
form.ShowDialog(ParentForm);
However I get a cross thread exception when calling ShowDialog(ParentForm).
I know I can use InvokeRequired somehow but not sure how on a property.
Thanks
UPDATE: Have tried doing this but still get exception:
MyForm form = new MyForm();
form.ShowDialog(GetParentForm());
private Form GetParentForm()
{
//You have to Invoke() so you can wait for the function to return and obtain its return value.
if (ParentForm.InvokeRequired)
{
return (Form)ParentForm.Invoke(new Func<Form>(() => GetParentForm()));
}
else
{
return ParentForm;
}
}
Your updated method (GetParentForm) won't work because you're wrapping the task of getting the reference to ParentForm in an InvokeRequired block. You could try wrapping the ShowDialog call in such a block instead, but I think you would still get the cross-threading error.
Your simplest fix would be to move the code that creates and shows the second form out of your class and into ParentForm. So instead of this:
MyForm form = new MyForm();
form.ShowDialog(ParentForm);
you would do this:
ParentForm.showMyNewForm();
and in ParentForm you would have this:
public void showMyNewForm()
{
MyForm form = new MyForm();
form.ShowDialog(this);
}
If MyForm needs to have a reference to the class on the other thread, you would just add a parameter to showMyNewForm() so that the reference to it can be passed in.
What you're trying to do here (creating and showing related, connected forms that are created on different threads) is really going against the grain of how forms are meant to be used in .NET.
you can add async method to a form.
Let's say like this:
public class MyForm : Form
{
public void ShowModalAsync()
{
this.Invoke(new Action(()=> {
ShowDilaog(..);
}));
}
}
and use this, like:
MyForm form = new MyForm();
form.ShowModalAsync(...);
Should work for you.
By the way, if your problem is only the fact that the window appears on bihind of others, try to make use of Form.TopMost property setting it to true. Having in mind that it, yes, will bring it infront of other forms, but not necessary infront of other topmost forms.
I'm shifting a project over from winforms to WPF. When my code was based on WinForms I used (this.InvokeRequired) to check if the thread has access. Now I use the following code based on my Mainform :
// this is the delegate declaration to Allow Background worker thread to write to Log Output
delegate void LogUpdateCallBack(String LogMessage);
// method to update the Log Window from the Background Thread
public void LogUpdate(String LogMessage)
{
Console.WriteLine("Entering");
if (!Application.Current.Dispatcher.CheckAccess())
{
Console.WriteLine("Thread doesn't have UI access");
LogUpdateCallBack callback = new LogUpdateCallBack(LogUpdate);
Application.Current.Dispatcher.Invoke(callback, LogMessage);
}
else
{
Console.WriteLine("Thread has UI access");
listBox_Output.Items.Add(LogMessage);
Console.WriteLine(LogMessage);
// listBox_Output.TopIndex = listBox_Output.Items.Count - 1;
}
Console.WriteLine("Exiting");
}
The issue I have is that the Listbox isn't updating. There are no errors or exceptions, I've tried updating other UI controls. The LogMessage is writing into the Output window so I'm stumped.
Here's sample Console output :
Entering
Thread doesn't have UI access
Entering
Thread has UI access
My LogMessage is output here
Exiting
Exiting
Entering
Thread doesn't have UI access
Entering
Thread has UI access
My LogMessage is output here
Exiting
Exiting
I've tried updating other UI controls just to check if it's an issue with my Listbox but with no luck.
Other than switching over to CheckAccess() the only other major change I've made in the new WPF code is to base all the code running in the Background worker in another Class .. I'm not sure if this could be part of the issue ?.
--
#JonRaynor
I tried your idea :
Application.Current.Dispatcher.BeginInvoke(new LogUpdateCallBack(LogUpdate), LogMessage)
However my Listbox still isn't updating, if I output
Console.WriteLine(listBox_Output);
I see the list box array growing :
System.Windows.Controls.ListBox Items.Count:2020
System.Windows.Controls.ListBox Items.Count:2021
System.Windows.Controls.ListBox Items.Count:2022
System.Windows.Controls.ListBox Items.Count:2023
System.Windows.Controls.ListBox Items.Count:2024
System.Windows.Controls.ListBox Items.Count:2025
But no change in the form. This is very confusing !.
I just started on WPF as well and had to relearn from the old WinForms way. I've been using BeginInvoke() and this type of syntax on my screens (forms)...
public delegate void WorkCompleted();
/// <summary>
/// Marks the end of the progress
/// </summary>
private void ProgressComplete()
{
if (!this.Dispatcher.CheckAccess())
{
this.Dispatcher.BeginInvoke(new WorkCompleted(ProgressComplete), System.Windows.Threading.DispatcherPriority.Normal, null);
}
else
{
this.buttonClose.Visibility = Visibility.Visible;
this.progressBarStatus.IsIndeterminate = false;
}
}
I finally solved this.
My issue was that as I was calling the LogUpdate method from both another thread AND another class, therefore I needed to pass a reference to my main form which contained the listbox into this class rather than create a new instance of the main form in the secondary class.
so rather than have this declaration in my secondary class :
public MainWindow MainForm = new MainWindow();
I needed to pass a reference of the form into the method in the secondary class :
public void Plot(BackgroundWorker worker, MainWindow MainForm)
In WPF, all the UI controls are derived from DispatcherObject. That means your ListBox control is, itself, a dispatch-capable object. Try invoking the Dispatcher on that object instead of relying on the form itself. Take out the parameter on your delegate (because you have access to the LogMessage variable anyway) and it'd look something like:
public void LogUpdate(string LogMessage)
{
Console.WriteLine("Entering");
if (listBox_Output.Dispatcher.CheckAccess())
{
listBox_Output.Dispatcher.Invoke(new LogUpdateCallBack(delegate
{
listBox_Output.Items.Add(LogMessage);
}));
Console.WriteLine(LogMessage);
}
}
I have a windows forms program with a form MainForm. On a button press I start a code that runs (pulses) on every 0.5secs on another thread. I want to modify many things, like labels, progressbars on my MainForm, from the Pulse method. How is this possible?
So I would like to know, how to interract with variables, values, in that thread, and the MainForm. Modify each other, etc..
On foo button click, I tell my pulsator to start.
Pulsator.Initialize();
Here is the Pulsator class:
public static class Pulsator
{
private static Thread _worker;
public static void Initialize()
{
_worker = new Thread(Pulse);
_worker.IsBackground = true;
_worker.Start();
}
public static void Close()
{
if (_worker != null)
{
_worker.Abort();
while (_worker.IsAlive || _worker.ThreadState != ThreadState.Stopped)
{
//closing
}
}
}
public static void Pulse()
{
if (_worker != null)
{
while (true)
{
SomeOtherClass.Pulse();
Thread.Sleep(500);
}
}
else
{
SomeOtherClass.Pulse(); // yeah I know this doesnt needed
}
}
}
SomeOtherClass Pulse method looks like :
public static void Pulse()
{
//here I will have several values, variables, and I want to show results,
// values on my MainForm, like:
Random random = new Random();
MainForm.label1.Text = random.Next(123,321).ToString(); // I hope you know what I mean
}
Of course it's much complicated, it's just a silly example.
Generally, in WinForms it's not safe to modify the state of visual controls outside the thread that owns the control's underlying unmanaged resources (window handle). You have to use the Control.Invoke method to schedule executing the modification on the control's owning thread.
As others already mentioned, you have to use Control.Invoke to change the UI controls from the background thread.
Another option is to use System.ComponentModel.BackgroundWorker (it's available in the form designer toolbox). You could then take a regular forms timer, to call the RunWorkerAsync-Method and do your background work in the DoWork event handler, which is automatically called from another thread.
From there, you can hand data back to the main thread, by calling ReportProgress. This will raise the ProgressChanged event in the main thread, where you are free to update all your UI controls.
Why not use a System.Timers.Timer?
E.g.:
trainPassageTimer = new Timer(500);
trainPassageTimer.AutoReset = true;
trainPassageTimer.Elapsed += TimeElapsed;
...
private void TimeElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
// Do stuff
// Remember to use BeginInvoke or Invoke to access Windows.Forms controls
}
C# 2 or higher (VS2005) has anonymous delegates (and C# 3 has lambdas which are a slightly neater version of the same idea).
These allow a thread to be started with a function that can "see" variables in the surrounding scope. So there is no need to explicitly pass it anything. On the downside, there is the danger that the thread will accidentally depend on something that it should not (e.g. a variable that is changing in other threads).
_worker = new Thread(delegate
{
// can refer to variables in enclosing scope(s).
});
I working on project and have problem with threading and update of UI. I read many post around web and this site, but didnt find answer to my problem.
I making program that a waiting for some UDP data on port XXXX. Looping in separated thread. When data come he extract information and put it in buffer, and go back wait another data. Another thread routine wait for data in buffer and send it to email.
Because of functionality and code design I have Form1 class (UI) - main thread, Class2 (rutine for reading UDP and extracting data ) - secodn thread, Class3( reading buffer and send mail ) - third thread, Class4 ( Queue buffer ( read, write, delete items ))...
Problem is that when I want to update UI from one of Class I got BufferOwerflow Exception...It's logical because I first must make instance if Class to start threading and in Class I must make instance of Form to pass data, and then we have infinite loop.
All post I found here are about update UI from thread but in same Class. I understand problem about stack and looping because of instances. I just don't want to have one big main class with huge amount of code inside...
Is it posible to update main Form form another class and threading routine inside?
I write some example from head ( it's not working ) just to have clue about what I am talking.
namespace ThreadUIClass
{
public delegate void updateTextBoxDelegate(string text);
public partial class Form1 : Form
{
void updateTextBox(string text)
{
textBox.Text = text;
}
public Form1()
{
InitializeComponent();
Go();
}
public void Go()
{
textBox.Text = "START";
DoWork dW = new DoWork(); //<= instance of work class
Thread myThread = new Thread(dW.WorkInBeckground);
myThread.IsBackground = true;
myThread.Start();
}
}
public class DoWork
{
public void WorkInBeckground()
{
while (true) //loop and wait for data
{
// Listen some ports and get data
// If (data==ok) update main UI textbox with status
Form1 myForm = new Form1(); //<= instance of main class
myForm.Invoke(new updateTextBoxDelegate(???updateTextBox), new object[] { "BUFFER LOADING..." });
Thread.Sleep(1000);
// Continue looping.....
}
}
}
}
You need to use a threadstart object and pass in a reference to your form object. What you are actually doing is creating a new reference to your mainform, which in turn kicks off a thread, which in turn does the same thing.
So just use a paramaterizedthreadstart object to kick off your thread, passing in "this" and you should be fine.
MSDN ParameterizedThreadStart
Would not be better off using the BackgroundWorker to achieve this? There's a pretty good example here. This class is UI thread aware and you can call directly to the UI components then.