c# sharing a class between multiple forms - c#

I have been looking around and found some answers with the same question as me, however i did try the code and it didn't work in my case.
I tried to do the same thing as this. My aim is to enable many different forms to use a class to do something.
Here are the part of simplified version of my class:
public class test
{
somedll g = new somedll();
somedll h = new somedll();
public void Stop(int module)
{
string command = "STOP";
if (module == 1)
{
this.WriteCommand(1, command);
}
else if (module == 2)
{
this.WriteCommand(2, command);
}
}
private void WriteCommand(int module, string command)
{
try
{
if (module == 1)
{
g.write(command + '\r');
}
else if (module == 2)
{
h.write(command + '\r');
}
}
catch (Exception)
{
if (module == 1)
{
gconnected = false;
}
else if (module == 2)
{
hconnected = false;
}
MessageBox.Show("<Write failed> Please connect.");
}
}
As my problems is i am unable to use static to make my class to be able to shared by all forms, as static is more efficient in just sharing variable/data.
edit*: I dont actually need static, i just need to make this class accessible from all other forms, without declaring new instances, i mentioned static because i did tried to used it and it cant compile.
The problem with not creating multiple instances is due to i need the data to be centralized. As declaring multiple instances cause all forms to get independent data.
edit*: My aim is to use this class that i show above with other forms without creating new instances from each forms. For example, in each form1, form2, form3 and form4, i will need to access 'stop', and then stop will in turn call 'writecommand' to finally send out the command. So, due to there is some calling in the method, static is unable to use(in my field of knowledge in c# only
Is there anyway to solve this? thanks

You can pass a single instance of your class to multiple forms (either through the constructor or via a property). However, you will probably need to make your class thread-safe by using locking.
A static instance of your non-static class would be another way for your forms to access a single instance, but this is not usually the best approach as it more tightly couples the class and the forms.
EDIT
To expand, a static instance of your non-static class Could look like this:
public class test
{
private static test singleInstance = new test();
public static test SingleInstance { get { return singleInstance; } }
somedll g = new somedll();
somedll h = new somedll();
public void Stop(int module)
{
// ...
}
private void WriteCommand(int module, string command)
{
// ...
}
}
Or it could be in another class altogether.
The other (possibly better) approach would be to create a new instance in the code that creates your forms, and to pass it to the forms' constructors, which could be edited to be something like this:
class MyForm : Form
{
private readonly test testInstance;
public MyForm(test testInstance)
{
this.testInstance = testInstance;
}
}

As I understand, you want to create an instance of your class, change it's value from one form and want the changed value of this instance to be reachable from other forms. You said you cannot use static for that, but I think you can. Have you tried using a static class like:
static class YourClass
{
private static string _yourVar= "";
public static string YourVar
{
get { return _yourVar; }
set { _yourVar = value; }
}
}
and change it like:
YourClass.YourVar = "your value"
I highly recommend reading this thread.

Related

Control on another form is inaccessible due to it's protection level

TF2SelectDir.txtTF2DirSelect.Text = "";
This is giving me issues, as txtTF2DirSelect is on one form and I'm trying to change it from another. I tried looking it up, and the entire form itself is already public, not private.
Or, to go along with this, how can I create a variable that can be accessed on any form?
Where it goes wrong
if (canFindTF2 == true)
{
TF2SelectDir.txtTF2DirSelect.Text = "";
The form where TF2SelectDir is is already public
public partial class TF2SelectDir : Form
{
public TF2SelectDir()
{
InitializeComponent();
}
Any ideas? Thanks!!
UPDATE
At the bottom of my TF2SelectDir.Designer.cs, I've found
private System.Windows.Forms.TextBox txtTF2DirSelect;
private System.Windows.Forms.Button btnSaveTF2Dir;
private System.Windows.Forms.Label lblExample;
However, when I changed private to public on txtTF2DirSelect, I got a new error.
"An object reference is required for the non-static field, method, or property 'TF2SelectDir.txtTF2DirSelect' - Error Code CS0120
Since I cannot comment, I am posting this as an answer.
Accessing controls from a separate form, may not be the best idea. I would recommend you use properties. Here is Microsoft's definition and usage example of properties.
Another, even better way, in my opinion, to share data between two forms, is to use events. Once again, here is Microsoft's description of events.
If you need a working example of how to use either of these approaches, I would like to see your effort first and then we can build on that.
Expose control in below way .. why?? #monstertjie_za provided few good links on that already .
namespace TF2Overwatch
{
public partial class TF2SelectDir : Form
{
//Approch 1 - usable when the projects most works are done
//without following a good architecture
//You can use a static variable to preserve the state and intilize each time
//a new instance is created
//Approch 2 - Responibilty of preserving text to initlize in textbox should be taken
//by the form who calling this form
//value will pass by consturtor or by exposing property
//all approch 2 code are kept commented for better understanding
private static string strTxtTF2DirSelectTextToInitize;
public TF2SelectDir()
{
InitializeComponent();
txtTF2DirSelect.Text = strTxtTF2DirSelectTextToInitize;
}
public static string TxtTF2DirSelectTextToInitlize
{
get
{
return strTxtTF2DirSelectTextToInitize;
}
set
{
strTxtTF2DirSelectTextToInitize = value;
}
}
//public TF2SelectDir(string txtTF2DirSelectText)
//{
// InitializeComponent();
// txtTF2DirSelect.Text = txtTF2DirSelectText;
//}
//public string TxtTF2DirSelectTextToInitlize
//{
// get
// {
// return txtTF2DirSelect.Text;
// }
// set
// {
// txtTF2DirSelect.Text = value;
// }
//}
}
public partial class SomeAnotherForm:Form
{
public SomeAnotherForm ()
{
InitializeComponent();
}
protected void InSomeAction(object Sender, EventArgs e)
{
if (canFindTF2 == true)
{
TF2SelectDir.TxtTF2DirSelectText = "Test";
TF2SelectDir t1 = new TF2SelectDir();
t1.Show();
//Approch 2
//TF2SelectDir t1 = new TF2SelectDir("Test");
//t1.Show();
//TF2SelectDir t1 = new TF2SelectDir();
//t1.TxtTF2DirSelectText="Test"; //look here TxtTF2DirSelectText is setting on instance not by class
//t1.Show();
}
}
}
}

Issue while invoking a form from another using System.Window.Forms.Invoke(delegate)

I have two forms and 1 singleton class. I am initalizing the singleton class in btn_A_Click of formA.
public partial class frmA : Form
{
public frmA()
{
InitializeComponent();
frmB frmB;
}
private void btn_A_Click(object sender, EventArgs e)
{
SessionMgmt.GetInstance().StartFormB();
}
}
This is my singleton class and here I am trying to use Forms.Invoke() method.
public class SessionMgmt
{
static SessionMgmt _sessinMgr;
frmB frB;
private SessionMgmt()
{
frB = new frmB();
}
public static SessionMgmt GetInstance()
{
if (_sessinMgr != null)
return _sessinMgr;
else
{
_sessinMgr = new SessionMgmt();
return _sessinMgr;
}
}
public bool StartFormB()
{
frB.Invoke(new EventHandler(DisplayFrmB));
return true;
}
private void DisplayFrmB(Object o, EventArgs e)
{
frB.Visible = true;
frB.Refresh();
}
}
This is my formB.
public partial class frmB : Form
{
}
But from the frB.Invoke(new EventHandler(DisplayFrmB)); method it throws the following exception:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
I can't figure out the issue, please help or advice me if I miss anything.
EDIT
The following structure is the way my current project is displaying the next form. It was done by VB.NET and I need to use similar kind of thing in the new project which uses C#. I saw the Invoke function which points to an event and then to a function. In that function it just makes the Form.Visible=true and Form.Refresh. But for understanding I just tried a POc and followed the same steps ,but it is not yet solved.
What is reason to call invoke? Isn't this doing the job for you?
public bool StartFormB()
{
frB.Visible = true;
return true;
}
Windows Forms is a wrapper around the Windows API, and that exception means the underlying window hasn't been created yet. I think it gets created when you set Visible to true for the first time, and there are a few other situations that do it.
See this link for a possible solution: http://blogs.msdn.com/b/mapo/archive/2011/04/27/forcing-handle-creation-in-a-net-windows-forms-control.aspx
there are two possible reasons for that exception:
The form isn't created when the invoke is called
It's possible that you're creating your controls on the wrong thread
you should always check the InvokeRequired Property before Invoking, and of course check for null before that
public bool StartFormB()
{
if (frB == null)
{
throw new ArgumentNullException("frB");
}
if (frB.InvokeRequired)
{
frB.Invoke(new EventHandler(DisplayFrmB));
}
else
{
if (frB.IsDisposed)
{
throw new ObjectDisposedException("Control is already disposed.");
}
}
return true;
}
Control handle IS NOT created if the control's Visible property is false. When you call Invoke you set your controls visible state to true in delegate, but handle is not created yet, so you can not call Invoke. So - you must call frB.CreateHandle(); after: frB = new frmB(); to force creation of control handle
private SessionMgmt()
{
frB = new frmB();
var h = frB.Handle;
}

class variable gets reset when calling methods in multiple forms

Updated to reflect to my own source
I'm in process of building my first winform application in c# and I'm trying to figure out the best practice for structuring my classes to work smoothly when I use them in my forms.
I have a couple of examples which I will try to explain the best way i can.
When working with get/set variables in a class, the best practice should be something like this:
JobMove.cs
public class JobMove
{
private List<string> jobNames { get; set; }
public string Scanner;
public JobMove()
{
this.Scanner = Properties.Settings.Default.Scanner;
}
public void ListSelected(ListBox lbx)
{
foreach (string jName in this.jobNames)
{
lbx.Items.Add(jName);
}
}
public static List<string> GetCheckedJobs(ListView lw)
{
int countChecked = lw.CheckedItems.Count;
int itemCount = 0;
List<string> jList = new List<string>();
foreach (ListViewItem item in lw.CheckedItems)
{
JobInfo jobInfo = Job.Find(Convert.ToInt32(lw.Items[item.Index].SubItems[1].Text));
jList.Add(jobInfo.Name);
itemCount++;
}
return jList;
}
}
My problem is when I combine this with my forms and I call this, then I would try to do something like this:
MyForm1.cs
public partial class MyForm1 : Form
{
private void btnMoveJobs_Click(object sender, EventArgs e)
{
Properties.Settings.Default.Scanner = cbxScanners.SelectedItem.ToString();
JobMove moveJobs = new JobMove();
frmMoveJobs FrmMoveJobs = new frmMoveJobs();
FrmMoveJobs.ShowDialog();
}
}
MyForm2.cs
public partial class frmMoveJobs : Form
{
public frmMoveJobs()
{
InitializeComponent();
JobMove moveJobs = new JobMove();
lblFrom.Text = moveJobs.Scanner;
moveJobs.ListSelected(lbxJobsToMove);
cbxMjScanners.DataSource = System.Enum.GetValues(typeof(Scanners));
}
}
But when I call MyClass in MyForm2 and I want to call the DoSomethingElse method, then myString will be reset to a null value. And that makes sense to me, but how do I work around this?
I tried to figure out what to use here to get easier around these flaws in my code, but my knowledge is far too weak to just implement an easy solution.
I know I could just store this variable in Settings.settings as an example, but to me that just seems like a real overload for such a simple task.
I might just need a point in the right direction to right on what to do in this situation.
If you do a MyClass myClass = new MyClass(); then indeed - the values are independent and unrelated. If you want to share the MyClass instance then pass the MyClass instance between the forms. Perhaps:
using(var form2 = new Form2()) {
form2.SensibleName = existingMyClassInstance;
form2.ShowDialog();
}
(note the using above btw; when using ShowDialog() it is your job to make sure the form is disposed; it only gets disposed automatically if using Show())
Firstly, they're properties, not variables (the variables are the underlying data source).
Secondly, the whole point of get/set accessors is so you can get and set the value without needing helper methods.
Thirdly, and as to your problem, you're creating a new instance of the class in each form (hinted at by the new keyword) and the value of the property will be whatever it is initialised as on construction of the instance (or not.) i.e. the values of properties are not shared between different instances of the same type.
Think of the mold for a key: I can get multiple instances of the key cut from a "blueprint", but any damage that one suffers won't be reflected by the rest - they're unique in that sense.
If you want the forms to both access the same instance of that type, then you will need to stash the instance somewhere in your code which is accessible to both.
A few options:
Pass in an instance of MyClass in the form2's constructor.
Make MyClass a static property of either Form1 or Form2 and access it via that on the other form.
Make MyClass static (not recommended).
If you want to use the instance of MyClass created in MyForm1 inside of MyForm2, you need to provide it to MyForm2.
Something like this would work:
public partial class MyForm2 : Form
{
public MyForm2(MyClass given)
{
InitializeComponent();
given.DoSomethingElse();
}
}
Easy Solution:
private static string myString { get; set; }
Why: because you initialize the class again when initializing Form2 and it will create a new class. With the "static" keyword you create a property which is the same in all instances of this class.
BUT: please read some books before continuing, this would be the solution to this problem, but the source of many others. Try to understand C# and Forms first, than (or alongside with reading/learning) start coding!
this is because each of your form has a new object of "MyClass".
To achieve what you want to do use a static property... this won't be initialized and gives back the same value for each object of MyClass
it looks like this
public class MyClass {
public static string myString { get; set; }
public void ChangeMyString(string newString)
{
myString = newString;
}
public void DoSomethingElse()
{
MessageBox.Show(myString);
}
}

C# Instance Class with Static Methods - using methods differently between threads

Hello I have this code here:
Memory.OpenProcess(Processes[0].Id);
Hook.Apply(........);
Memory and Hook are both non-static classes, and openprocess and Apply are both static methods within those classes.
However, the problem is, for each instance of my Memory or Hook, I want to have a different process opened, and a different Hook applied.
What I want to do is:
Memory newMemory = new Memory();
newMemory.OpenProcess(processes[1].Id);
Hook newHook = new Hook();
newHook.Apply(....);
But of course I cannot do this because the methods are static and not particular to each instance.
I cannot change the static methods because these methods are coming from a dll in which I do not have access to the source code.
Any ideas?
**Edit: I want to do this so I can avoid having to rehook the process every time a new thread comes along that is working with a different process.
It seems that you cannot do that by design. The implementor of the classes from the dll you are consuming might have explicitly want to avoid the functionality you are trying to achieve.
You can load each thread in different AppDomain, that would give you different static methods.
Also, ThreadStaticAttribute might be helpful for you. Don't sure if it fits you, but give it a look.
Upd: More info about using AppDomains. Lets assume, that you have 3-rd party class Memory defined as follows. (And you cannot change it, and it uses inner static variables)
// Cannot be changed
public class Memory
{
static int StaticId;
public static void OpenProcess(int id)
{
StaticId = id;
}
public static int GetOpenedId()
{
return StaticId;
}
}
You can write a wrapper, deriving from MarshalByRefObject (that's important):
class MemoryWrap : MarshalByRefObject
{
public void OpenProcess(int id)
{
Memory.OpenProcess(id);
}
public int GetOpenedId()
{
return Memory.GetOpenedId();
}
}
So if you create instances of MemoryWrap not by new keyword, but using AppDomain.CreateInstanceAndUnwrap in another domain, each instance would have it's own static contexts. Example:
class Program
{
static void Main(string[] args)
{
var type = typeof(MemoryWrap);
var domain1 = AppDomain.CreateDomain("Domain 1");
var memory1 = (MemoryWrap)domain1.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
var domain2 = AppDomain.CreateDomain("Domain 2");
var memory2 = (MemoryWrap)domain2.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
memory1.OpenProcess(1);
memory2.OpenProcess(2);
Console.WriteLine(memory1.GetOpenedId());
Console.WriteLine(memory2.GetOpenedId());
Console.ReadLine();
}
}
It would print:
1
2
PS: in that example I didn't do the clean up just for readability (unloading domains with AppDomain.Unload() and other things). Don't forget to do it in you code. + There is some mess with lifetime of objects in another domain, but it is next level of problems)))
I'm not sure I fully understand the question, but I will try to answer anyways.
You could define two new classes:
public class MemoryInstance : Memory
{
private var m_instanceProcessId;
public MemoryInstance(var processId) : base()
{
m_instanceProcessId = processId;
}
public void OpenProcess()
{
Memory.OpenProcess(m_instanceProcessId);
}
}
public class HookInstance: Hook
{
private var m_hookId;
public HookInstance(var hookId) : base()
{
m_hookId = hookId;
}
public void Apply()
{
Hook.Apply(m_hookId);
}
}
Then in your code you could call:
public static void Main(String[] args)
{
MemoryInstance newMemory = new MemoryInstance(processes[1].Id);
HookInstance newHook = new HookInstance(hookId);
newMemory.OpenProcess();
newHook.Apply();
}
See , if the API writers are doing that it must be for some reason , you should consult your API writers for he reason or if they can provide you something at instamnce level.
BUT for circumvent your situation , you can use the method provided The_Smallest above.
or you can make use of Reflection as shown below
Memory m = Activator.CreateInstance("Your Dll Name", true) , here true stands for the calling of private constructor.
But i am not convinced , you should do it , you first call to the API writer to get the reason of doing this.

access textbox from anywhere in application

How can I make a textbox in my winforms application that accepts new lines of text from anywhere in the application?
I have a main form that contains a textbox. I'd like to directly add text to the box from a method in another class.
Update
I tried this in my main form:
public void Output(String value)
{
if (txtOutput.Text.Length > 0)
{
txtOutput.AppendText(Environment.NewLine);
}
txtOutput.AppendText(value);
}
But I can't call Output from the other class. I'm new to C#, so perhaps I'm missing something obvious.
Regards, Miel.
PS Yes, I know this is bad design, but for now this seems to be the best way to do what I want. The textbox would function like a console.
You'll need to expose the Text property of the TextBox as a string property on your form. For example...
public string TextBoxText
{
get { return textBoxName.Text; }
set { textBoxName.Text = value; }
}
Edit
After reading the question edit, your problem is that you need a reference to a specific instance of the form whereever you're trying to execute that code. You can either pass around a reference (which is the better option), or you could use some smelly code and have a static property that refers to one instance of your form. Something like...
public partial class MyForm : Form
{
private static MyForm instance;
public static MyForm Instance
{
get { return instance; }
}
public MyForm() : base()
{
InitializeComponent();
// ....
instance = this;
}
}
Using this approach, you could call MyForm.Instance.Output("test");
In order to decouple a bit more you could inverse the control a bit:
// interface for exposing append method
public interface IAppend
{
void AppendText(string text);
}
// some class that can use the IAppend interface
public class SomeOtherClass
{
private IAppend _appendTarget = null;
public SomeOtherClass(IAppend appendTarget)
{
_appendTarget = appendTarget;
}
private void AppendText(string text)
{
if (_appendTarget != null)
{
_appendTarget.AppendText(text);
}
}
public void MethodThatWillWantToAppendText()
{
// do some stuff
this.AppendText("I will add this.");
}
}
// implementation of IAppend in the form
void IAppend.AppendText(string text)
{
textBox1.AppendText(text);
}
It looks like your design is a little bit corrupted. You shouldn't let buisness logic mess with GUI controls. Why don't you try a return value and assigning it on the interface side?
This is a REALLY bad way of doing it, but just to make sure all the answers are out there...
In the VS designer, each form control has an item in the Properties window named Modifiers that defaults to Private. Changing this to one of the others settings, such as Internal or Public, will let you access it from outside the form.
I must stress that this is the worst way to do it.

Categories