I am writing a C#.NET application. I have a form. When the form is created I create an instance of a class. When I close the form, I want to dispose of the class so that the next time I open the form I can just create a fresh new instance of the class. So, in the form_Closing event I added code like this: classInstance = null; The problem is, for some reason, the next time I open the form the class is not equal to null but rather it is in the same state as it was right before I closed the form. Why is this happening?
EDIT: Adding Info:
myHandler is a field in the Form class.
it looks like this:
private HSFW_Handler myHandler;
The Class that I am referring to is a singleton so I create it like this:
public static HSFW_Handler GetInstance()
{
if (myHSFW == null)
{
myHSFW = new HSFW_Handler();
return myHSFW;
}
else return myHSFW;
}
I create an initial instance of it in the Form_Shown event
private void SetupDialogForm_Shown(object sender, EventArgs e)
{
try
{
myHandler = HSFW_Handler.GetInstance();
UpdateDisplay();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The Form Closing looks like this...
private void SetupDialogForm_FormClosing(object sender, FormClosingEventArgs e)
{
myHandler = null;
}
Let's look at this code:
public static HSFW_Handler GetInstance()
{
if (myHSFW == null)
{
myHSFW = new HSFW_Handler();
return myHSFW;
}
else return myHSFW;
}
And this:
private void SetupDialogForm_FormClosing(object sender, FormClosingEventArgs e)
{
myHandler = null;
}
Notice anything?
You're setting myHandler to null; but this appears to be an instance-level member of SetupDialogForm. Or anyway, it's not the same as myHSFW, which is your static variable in the HSFW_Handler class.
When you do this...
myHandler = HSFW_Handler.GetInstance();
...you're making myHandler a reference to the same object pointed to by HSFW_Handler.myHSFW; but they're still two separate references. Setting one to null has no impact on the other.
The point of all this is that you need to actually change the value of myHSFW to null to get the behavior you seem to want.
I'd do it like this:
public class HSFW_Handler
{
public static void DeleteInstance()
{
myHSFW = null;
}
}
Then:
private void SetupDialogForm_FormClosing(object sender, FormClosingEventArgs e)
{
HSFW_Handler.DeleteInstance();
}
Are you certain that the form_Closing event is being fired, so that you're closing the form and not hiding it?
If so, then search through the code looking for every time classInstance is set to something. You'll probably find the culprit in or around the constructor or another event such as Load or Shown.
Related
I'm trying to call method Run in script1. Then from script1 call method alert or wait and try to update statusLabel in Form1. But this code has an error.
static Label status = this.Controls.Find("statusLabel", true).FirstOrDefault() as Label;
This code will only work in Form1. Because this return error in another classes. Maybe it is not correct and you know better solution.
P.S. I know how to solve this problem (see below "Not the best solution"), but the code will be ~ 10-30 new lines.
Project
Form1.cs
public partial class Form1 : Form
{
private void statusLabel_Click(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
script1.Run();
...
script30.Run();
}
}
function.cs (Frequently used functions)
public class function
{
static Label statusLabel = this.Controls.Find("statusLabel", true).FirstOrDefault() as Label;
static public void alert(string str)
{
statusLabel.Text = str;
}
static public void wait(int sec)
{
int i = 0;
while (i++ < sec)
{
statusLabel.Text = "Wait: " + (sec+1-i).ToString();
Thread.Sleep(1000);
}
}
}
script1.cs (script1,script2 ... it is about 30 mini programs)
public class script1
{
static public void Run()
{
function.alert("Script1 is running");
function.wait(5);
function.alert("Script1 is done");
}
}
Not the best solution
remove in function.cs
static Label status = this.Controls.Find("statusLabel", true).FirstOrDefault() as Label;
Form1.cs
public partial class Form1 : Form
{
private void Form1_Load(object sender, EventArgs e)
{
script1.Run(this.statusLabel);
}
}
function.cs
public class function
{
private Label _statusLabel;
public scriptClass(Label statusLabel)
{
_statusLabel = statusLabel;
}
}
script1.cs (script1,script2 ... it is about 30 mini programs)
public class script1
{
static public void Run(Label statusLabel)
{
function _function = new function(statusLabel);
}
}
The statusLabel object is owned by, and should be encapsulated and hidden by, the Form1 class. To ensure good decoupling of your classes, as well as proper data hiding, only the Form1 class should be directly accessing it. And it should (by default) be able to access it via a field named statusLabel (i.e. no need to call this.Controls.Find() (nor should that even work from the function class, since that class also is not the owner of the object, nor of a Controls property).
The correct way to do this is for the script1 class to expose a StatusText property, and an event that is raised when the property value changes. There are two canonical ways to implement the event:
Implement an event named StatusTextChanged
Implement the INotifyPropertyChanged interface
Note that in your example, #2 is not an option because you are using static classes to implement your scripts. IMHO this is inadvisable for a variety of reasons, but since #1 is a perfectly fine solution I won't belabor that point. :)
The first looks like this:
class script1
{
public static string StatusText { get; private set; }
public static event EventHandler StatusTextChanged;
static public void Run()
{
ChangeStatusText("Script1 is running");
function.wait(5);
ChangeStatusText("Script1 is done");
}
static void ChangeStatusText(string text)
{
StatusText = text;
EventHandler handler = StatusTextChanged;
if (handler != null)
{
handler(null, EventArgs.Empty);
}
}
}
Then in Form1:
public partial class Form1
{
private void Form1_Load(object sender, EventArgs e)
{
script1.StatusTextChanged += (sender1, e1) => statusLabel.Text = script1.Text;
script1.Run();
...
script30.StatusTextChanged += (sender1, e1) => statusLabel.Text = script30.Text;
script30.Run();
}
}
Note in the above, each scriptX class has to reimplement the event. You could instead make a base class that each of the scriptX classes inherits, and which contains the event in question. Then the Form1 class need only subscribe to the one base class event. It would also address, or at least minimize the hassle of, the issue of leaving event handlers subscribed to 30 different events.
Of course, in this case then the Form1 class won't know which script is updating the text, but maybe that doesn't matter in your case.
Also note that if you did make the scriptX classes non-static, you might then again run into the issue of having to subscribe multiple times. But that is much more easily handled, since it seems certain in that case you'd use a base class, and so it would be easy to generalize the "subscribe, run script, unsubscribe" logic into a helper method.
I declare a subscription to event in:
public class MainClass
{
public void btndel_bar_Click(object sender, RoutedEventArgs e)
{
SomeClass sc = new SomeClass();
sc.FieldUpdate += new SomeClass.FieldUpdateHandler(sc_FieldUpdate);
}
void sc_FieldUpdate(object sender, ValueEventArgs e)
{
MessageBox.Show(e.Smth_property);
}
}
And here is I want to listen event:
public class Someclass
{
public delegate void FieldUpdateHandler(object sender, ValueEventArgs e);
public event FieldUpdateHandler FieldUpdate;
void Somemethod()
{
string str = "Steel";
ValueEventArgs args = new ValueEventArgs(str);
FieldUpdate(this, args);
}
}
A class which carries data:
public class ValueEventArgs : EventArgs
{
private string smth;
public ValueEventArgs(string smth)
{
this.smth = smth;
}
public string Smth_property
{
get { return smth; }
}
}
I always have FieldUpdate=null. How to solve it?
You're calling Somemethod() in the constructor, before the calling code gets a chance to add the event handler.
Therefore, the event is still null.
The moment you create the object of SomeClass your event would get reinitialized.
Make your event a static so that multiple objects of SomeClass would share it
public static event FieldUpdateHandler FieldUpdate;
I've read articles about delegates and events and after reading I always I thought to make all operations again. I did all over again and it works! Consequently I done something wrong when I did at the beginning of.
Im making a program what connects to multiple 3th party systems. The connect with different formats so i created multiple classes to deal with them. I have now three 4 classes.
The MainForm is the first class. This is the basic windows form class with the user interface.
SDKCommunication is the second class.
VMS (this class handles the events given of by the 2th party system and activates methods on SDK COmmunication)
Events
Events Class
public class Events
{
public event EventHandler LoginStateChanged;
private bool loginstate;
public bool LogInState
{
get { return this.loginstate; }
set
{
this.loginstate = value;
if (this.LoginStateChanged != null)
this.LoginStateChanged(this, new EventArgs());
}
}
}
part of SDKCommunicatie class
Events events = new Events();
public void onLogon(string username, string directory, string system)
{
events.LogInState = false;
}
MainForm Class
SDKCommunicatie sdkcommunicatie = new SDKCommunicatie();
Events events = new Events();
public MainForm()
{
InitializeComponent();
events.LoginStateChanged += new EventHandler(events_LoginStateChanged);
}
void events_LoginStateChanged(object sender, EventArgs e)
{
log.Info("EventFired loginstateChanged");
}
When the LogInState Changes in the SDKCommunicatie class. There needs to be an event fired in the MainForm class. But sadly that doesn't work.
But when I change the loginstate in the mainform(with a buttonclick)(see code below) the event is fired. But that is not the intention i would like to have.
private void button1_Click(object sender, EventArgs e)
{
events.LogInState = true;
}
If my question isn't clear enough, please let me know.
VMS class Added as reply to #Astef
class VMS {
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(MainForm));
GxUIProxyVB m_UIProxy = new GxUIProxyVB();
public string username2;
public string directory2;
public string Status;
public void initOmni()
{
m_UIProxy.CreateInstance();
m_UIProxy.OnLogon += new _IGxUIProxyVBEvents_OnLogonEventHandler(m_UIProxy_OnLogon);
m_UIProxy.OnLogoff += new _IGxUIProxyVBEvents_OnLogoffEventHandler(m_UIProxy_OnLogoff);
m_UIProxy.OnError += new _IGxUIProxyVBEvents_OnErrorEventHandler(m_UIProxy_OnError);
m_UIProxy.OnAlarmStatusEx2 += new _IGxUIProxyVBEvents_OnAlarmStatusEx2EventHandler(m_UIProxy_OnAlarmStatusEx2);
}
public void login(string username, string password, string directory)
{
username2 = username;
directory2 = directory;
initOmni();
m_UIProxy.LogOn(directory, username, password,false);
}
public void logOff()
{
m_UIProxy.LogOff();
}
void m_UIProxy_OnLogon()
{
SDKCommunicatie sdkcommunicatie = new SDKCommunicatie();
sdkcommunicatie.onLogon(username2, directory2, "Genetec Omnicast");
}
I have fixed this with deleting the following:
SDKCommunicatie sdkcommunicatie = new SDKCommunicatie();
And adding the following in the base of VMS:
SDKCommunicatie sdkcommunicatie;
But now i got a new error in the mainform when i tried to call a class in SDKCommunicatie
connectedStatus = sdkcommunicatie.connectedStatus();
I got the following error:
NullReferenceException was unhandled
You are not using the same instance of the Events class, and that's why on button click you catch LoginStateChanged. You should inject the same instance of Events class to SDKCommunicatie class, then you'll be able to listen to event changes.
Edit:
Jeremy Todd and I were both writing at the same time.
Events in your SDKCommunicatie are not fired because you've created an individual instance of class Events for it. That is not the instance you have placed on the MainForm.
Inject the right instance (pass a reference) to SDKCommunicatie from MainForm through constructor, property or somehow else. For example:
MainForm:
SDKCommunicatie sdkcommunicatie;
Events events = new Events();
public MainForm()
{
InitializeComponent();
events.LoginStateChanged += new EventHandler(events_LoginStateChanged);
sdkcommunicatie = new SDKCommunicatie(events);
}
void events_LoginStateChanged(object sender, EventArgs e)
{
log.Info("EventFired loginstateChanged");
}
SDKCommunicatie:
Events events;
public SDKCommunicatie(Envents eventsInstance)
{
events = eventsInstance;
}
public void onLogon(string username, string directory, string system)
{
events.LogInState = false;
}
Your SDKCommunication class and your MainForm class each have their own separate instance of Events, so any events you trigger from one won't be visible from the other -- they're being raised on an entirely different object.
What you need is a single instance of the Events class that both SDKCommunication and MainForm can share -- that way they'll both be seeing the same thing. There are several different approaches you could take for this. Depending on what it needs to do, one very simple possibility might be to make Events a static class, and then the events would be visible everywhere without needing to create any instances.
I have solved the riddle.
When i need a method is a class i can call the method directly like this:
public class MainForm : Form
{
SDKCommunication sdkcommunication = new SDKCommunication();
public MainForm()
{
}
private void Button1_Click(oject sender, EventArgs e)
{
sdkcommunication.method("Test")
}
}
This is pretty straightforward. Look here the receiverclass:
public class SDKCommunication
{
method(string word)
{
//do something with word
}
}
The biggest problem is calling the class with the form(the original class). I have solved this with a eventhandler.
class CustomEventHandler1 : EventArgs
{
public CustomEventHandler1(string u, string d)
{
msgu = u;
msgd = d;
}
private string msgu;
private string msgd;
public string Username
{
get { return msgu; }
}
public string Directory
{
get { return msgd; }
}
}
Then the SDKCOmmunication class should look like this:
class SDKCommunication
{
public event EventHandler<CustomEventHandler1> RaiseCustomEventHandler1;
protected virtual void OnRaiseCustomEventHandler1(CustomEventHandler1 e)
{
EventHandler<CustomEventHandler1> handler = RaiseCustomEventHandler1;
if (handler != null)
{
handler(this,e);
}
}
//Custom Method that is called somewhere
internal void custommethod()
{
OnRaiseCustomEventHandler1(new CustomEventHandler1("johnsmith", "localhost");
}
}
Then in the mainform class:
public class MainForm : Form
{
public MainForm()
{
sdkcommunication.RaiseCustomEventHandler1 += new EventHandler<CustomEventHandler1>(sdkcommunication_RaiseCustomEventHandler1);
}
void sdkcommunication_RaiseCustomEventHandler1(object sender, CustomEventHandler1 e)
{
//Do something.
}
}
The information sended with the event you can get with e.Username and e.Directory. In this example they are strings where e.Username = johnsmith and e.Directory = localhost.
I hope somebody can use this information for their own code.
I have a class and a form. the class is intended to do some processes when event is raised and return back the values to the form to display only. I kind of have problem passing values back to form. For instance, I have this code in class print:
public class PrintClass : Form1
{
public void printEventHandler(object sender, EventArgs e)
{
string text = "Process Completed";
append_Tbox(text);
}
}
and the method in form1 to display the text:
public void append_Tbox(string s)
{
TboxPrint.AppendText(s);
}
However, nothing is displayed. I believe there is something wrong, but I can't figure it out.
What is the fastest way to pass values from the class to form?
First off, your processing class shouldn't extend Form1. This is giving you the illusion that you can access the methods of your existing form, but it's not doing what you think it is. You're creating an entirely new form when you do this, and just not showing it. That form has it's own set of all instance fields, so you're not accessing the controls of your main form. Even if this would work (and it won't) it's not a well designed solution.
The proper way to do this is actually much easier. You just need to have your other class return a value from it's method:
public class PrintClass
{
public string DoWork()
{
Thread.Sleep(2000);//placeholder for real work.
return "Process Completed";
}
}
Now your main form can just call that method and append the return value to a textbox.
Once you do this you'll have an entirely separate issue. If you do the work in the UI thread you'll be blocking that UI thread while the work takes place, preventing the form from being repainted, or any other events from being handled. You need to do the work in a background thread and then marshal back to the UI thread to update the UI with the results. There are a number of ways of doing this, but if you have C# 5.0 using await is by far the easiest:
public class Form1 : Form
{
private void SomeEventHandler(object sender, EventArgs args)
{
string result = await Task.Run(()=>new PrintClass().DoWork());
TboxPrint.AppendText(result);
}
}
If you need a C# 4.0 solution you can use ContinueWith, which is more or less what the above will be translated to, but it's not quite as clean of syntax.
public class Form1 : Form
{
private void SomeEventHandler(object sender, EventArgs args)
{
Task.Factory.StartNew(()=>new PrintClass().DoWork())
.ContinueWith(t => TboxPrint.AppendText(t.Result)
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
}
I have create delegate in Main Form
public delegate void LoginDelegate(string s);
public partial class AirLineReservationMDI : Form
{
LoginDelegate loginAirLineDelegate;
}
loginAirLineDelegate = new LoginDelegate(DisableToolStripMenuItems);
public void DisableToolStripMenuItems(string s)
{
this.viewToolStripMenuItem.Visible = true;
this.bookingToolStripMenuItem.Visible = true;
this.existingUserToolStripMenuItem.Visible = false;
this.newUserToolStripMenuItem.Visible = false;
this.toolStripStatusUserID.Text = "USerID :- "+s;
this.LoginUserId = s;
}
in Another Class, (I have passed delagete object to this class )
I fired the Delegate
logDelegate(textBoxUserName.Text);
I used Action<T> delegate to solve the problem. here is the code and it works fine.
class PrintClass
{
public Action<string> DisplayDelegate;
public void printEventHandler(object sender, EventArgs e)
{
string text = "Event Handled, and text value is passed";
var copy = DisplayDelegate;
if (copy != null)
{
copy(text);
}
}
}
and in `Form1.cs' :
private void Form1_Load(object sender, EventArgs e)
{
PrintClass p = new PrintClass();
BtnPrint.Click += p.printEventHandler;
//subscrite the displayevent method to action delegate
p.DisplayDelegate += DisplayEvent;
}
public void DisplayEvent(string s)
{
Invoke(new Action(() => TboxPrint.AppendText(s)));
}
so the text 'Event Handled, and text value is passed' is displayed on the textbox.
I m not sure if it is the efficient way.
Thanks guys.
See Also: How to enable a timer from a different thread/class
The timer is assigned to a form and I'd like to Enable it at a specific location,but from another class.I don't want to make it public
This is the code I use to access memo
public string TextValue
{
set
{
if (this.Memo.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
this.Memo.Text += value + "\n";
});
}
else
{
this.Memo.Text += value + "\n";
}
}
}
public static void addtxt(string txt)
{
var form = Form.ActiveForm as Form1;
if(form != null)
form.TextValue = txt;
}
If you don't want to expose the timer itself, expose a public method or property that you can call to enable the timer. Obviously the Form that enables the Timer will need a reference to the Form that owns the Timer.
How would you like to enable the timer ? What action is undertaken in order to enable it ?
Is it possible to add an event to the class from which you want to enable the timer, and, on the form which contains the timer, subscribe to that event ?
In the event-handler for that event, you can then enable the timer.
When the other class raises the event, the eventhandler will enable the timer.
public class SomeOtherClassThatDoesStuff
{
public event EventHandler SomethingHappened;
public void DoStuff()
{
...
if( SomethingHappened != null )
SomethingHappened;
...
}
}
public class Form1
{
private void Button1_Click(object sender, EventArgs e )
{
SomeOtherClassThatDoesStuff o = new SomeOtherClassThatDoesStuff();
o.SomethingHappened += new EventHandler(EnableTimer);
o.DoStuff();
}
private void EnableTimer(object sender, EventArgs e )
{
myTimer.Enabled = true;
}
}
Something like this. (I haven't tested, nor did I even compile it, but I think you'll catch the drift :) ).
Make the timer "internal" then it is accessible to other classes in the assembly.
You could add an accessor method to Enable the timer, this would allow yu to keep the timer private but make the method public, would that work?
Also, you could look at making the accessor protected or internal, depending on who is calling it it may not need to be public.