c# winforms array items gets deleted - c#

I know this might look silly but I got a strange problem in my winforms. I have a windows application in which after a particular set of operations are completed I want to populate a Checked ComboBox. I am doing this using two classes. I want to copy a array from helper class to the form class. Array gets copied when AddArrayItems method is called. But when I see the ComboBox in the form, its null. After debugging with watch variables I got to know that the problem is after copying the array to Form1 array, as soon the control goes back to the caller, the array items are deleted. I tried to replicate my stuff, not exactly but still similar to what I am doing.
My code looks like this:
using System;
using System.Windows.Forms;
namespace DemoApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string[] cboxAr;
public void AddCmboBoxItems(string[] cbArry)
{
cboxAr = new string[cbArry.Length];
Array.Copy(cbArry, 0, cboxAr, 0, cbArry.Length);
//cbArry.CopyTo(cboxAr, 0);
//foreach (string s in cboxAr)
//comboBox1.Items.Add(s);
comboBox1.Show();
}
private void button1_Click(object sender, EventArgs e)
{
HelperClass.DoSomething();
}
}
public class HelperClass
{
public HelperClass()
{
}
public void HelperMethod()
{
SomeMethod();
}
private void SomeMethod()
{
string[] partnrName = new string[5] { "str1", "str2", "str3", "str4", "str5"};
Form1 f = new Form1();
f.AddCmboBoxItems(partnrName);
}
public static void DoSomething()
{
new HelperClass().HelperMethod();
}
}
}
I don't understand what exactly the problem is here. Can anyone please push me in the right direction. Thanks in advance.

You're never showing the form after modifying its controls:
Form1 f = new Form1();
f.AddCmboBoxItems(partnrName);
But you're calling this from within an existing form:
private void button1_Click(object sender, EventArgs e)
{
HelperClass.DoSomething();
}
Presumably you want to modify the controls on that form? Then you'll need a reference to that form. Pass one to the method:
private void button1_Click(object sender, EventArgs e)
{
HelperClass.DoSomething(this);
}
And accept it in the method definition:
public static void DoSomething(Form1 form)
{
new HelperClass().HelperMethod(form);
}
And so until the point where you need to use it. (Side note: You have a lot of weird indirection happening here with a seemingly random mix of static and instance methods and classes. You can simplify a lot, which will make this involve fewer code changes.)
Ultimately, SomeMethod needs the instance of the form to modify:
private void SomeMethod(Form1 form)
{
string[] partnrName = new string[5] { "str1", "str2", "str3", "str4", "str5"};
form.AddCmboBoxItems(partnrName);
}
To illustrate the overall point, consider an analogy...
A car rolls off of an assembly line. You open the trunk and put a suitcase inside. Moments later another car rolls off of the same assembly line. It is identical to the first car in every way. When you open the trunk of the second car, do you expect to find your suitcase inside it?
A Form is an object like any other. Changes made to one instance of an object are not reflected in other instances of the same object. Each instance maintains its own state. In order to modify a particular instance, you need a reference to that instance.

Related

C#: Trying to use control elements in different class - "object reference is required"

I am brand new to C# (I apologise if my question is noobish - I'm teaching myself, so it's a bumpy process). I am trying to develop a winform and since some of the methods are pretty long, I am trying to keep it in a couple classes. This is what I'm kind of hoping to achieve:
public partial class formMainForm : Form
{
public formMainForm()
{
InitializeComponent();
}
private void UpDown1_ValueChanged(object sender, EventArgs e)
{
longCalculations.LongMethod1();
}
}
public class longCalculations
{
private void LongMethod1()
{
// Arbitrarily long code goes here
}
}
I'm doing this in an attempt to keep the formMainForm class tidy and be able to split any calculations into manageable chunks. However, I'm encountering problems with using form controls (e.g. check boxes, numeric up-down controls, etc.) in my non-form classes.
If I leave them as is (e.g. CheckBox1) I get a the name does not exist in the current context error. I searched around and I found that it's because that box is defined in a different class. However, if I change it to formMainForm.CheckBox1, the error is now an object reference is required for the non-static field, method or property. Again, I looked around and it appears that that is due to the form initialization method not being static.
If I change public formMainForm() to static formMainForm(), the error now moves to InitializeComponent(); and I do not know where to go from here. I also tried making an instantiation of the formMainForm() method, but that didn't do anything (the code I attempted to use is below. I found it somewhere on this site as an answer to a similar problem).
private void formLoader(object sender, EventArgs e)
{
shadowrunMainForm runForm = new shadowrunMainForm();
runForm.Show();
}
How can I use the formcontrol names in other classes?
P.S. It is my first post here - I am super sorry if I have missed this question already being asked somewhere. I did search, but I didn't find what I was looking for.
EDIT
It seems I hadn't made myself clear - this was just an example of code and my problem is with the second class, not the first one. I have now simplified the code to:
public partial class formMainForm : Form
{
public formMainForm()
{
InitializeComponent();
}
}
public class longCalculations
{
private void LongMethod1()
{
List<CheckBox> listOfBoxes = new List<CheckBox>();
listOfBoxes.Add(CheckBox1);
// The code displays an "object reference is required for the non-static field, method or property" error at this stage. Changing the "CheckBox1" to formMainForm.CheckBox1 doesn't help
// Arbitrarily long code goes here
}
}
LongMethod1 works perfectly fine when placed in the formMainForm partial class. Moving it to the other form makes it unable to take data from those checkboxes.
I believe this line longCalculations.LongMethod1(); is throwing error cause you are trying to access a instance method as if it's a static method and as well it's defined as private method which won't be accessible outside the class. You need to create an instance of longCalculations class before accessing any of it's member or method(s) and mark the method public like
private void UpDown1_ValueChanged(object sender, EventArgs e)
{
longCalculations ln = new longCalculations();
ln.LongMethod1();
}
public class longCalculations
{
public void LongMethod1()
{
// Arbitrarily long code goes here
}
}
(OR) If you really want it to be a static method then define accordingly with static modifier like
public class longCalculations
{
public static void LongMethod1()
{
// Arbitrarily long code goes here
}
}
Now you can call it like the way you are trying
public static class longCalculations
{
public static void LongMethod1()
{
// Arbitrarily long code goes here
}
}
If you're going to make a call longCalculations.LongMethod1();, then you need to make your class static as such.
Or you leave it as not static method by calling
longCalculations lc = new longCalculations()
lc.LongMethod1();
As for accessing controls in separate classes, you can pass in the form and make the controls public which can be dangerous.
So on your Form.designer.cs, change any control you may have to public modifier. Then you would make a call like this...
private void UpDown1_ValueChanged(object sender, EventArgs e)
{
longCalculations.LongMethod1(this);
}
public void LongMethod1(Form1 form)
{
// Arbitrarily long code goes here
form.label1.Text = someString;
//more settings and whatnot
}
Or do something like this:
public class longCalculations
{
public string LongMethod1()
{
// Arbitrarily long code goes here
return myString;
}
}
longCalculations lc = new longCalculations()
string result = lc.LongMethod1();
this.label1.Text = result;
Ideally, your longCalculations class would not attempt to modify the form directly. Instead it would return an object that the form could use to update its controls.
If you need to access the form directly from the longCalculations class, first change the method to accept an instance of your form
public void LongMethod1(formMainForm myForm)
Then you can pass the form itself as a parameter
var calc = new longCalculations();
calc.LongMethod1(this);
In your other class, you need to have an instance of your formMainForm class:
var myForm = new formMainForm();
Then you can access its members like this:
myForm.CheckBox1.Checked = true;

C# Hashtable of FileSystemWatchers

I'm trying to create a hashtable of filesystem watchers. This is to keep a running record of active filesystemwatchers with the directories they are watching as keys. Then via a form the user can add and delete folders to watch, which are visible in a listview or something.
My main problem is how to "keep" the hash table between methods and classes. I'm a bit of a novice to C# and it doesn't seem to work the way I'm used to in VB.NET.
So I have (stripped down to simplify):
public partial class MainForm : Form
{
public static Hashtable globalHashTable;
public MainForm()
{
InitializeComponent();
}
public void Button1Click(object sender, EventArgs e)
{
FileSystemWatcher watcher1 = new FileSystemWatcher(#"C:\");
globalHashTable.Add(#"C:\",watcher1);
}
}
}
So that a filesystemwatcher is added to the hashtable. However since the globalhashtable is static (?) this won't work. Making it non static means I have to create an instance of it when the buttton is pressed, so I have a new one each time as it's not "kept". My problem is how to keep a table in memory between methods and classes.
I'm fairly sure I've majorly misunderstood something as I'm new to all of this. Also I doubt this is even a half decent way to do this, if anyone has a better way, then please go ahead!
Thanks,
Matt
Expanding on #Ron Beyer's suggestion you can do something like this:
private Dictionary<string, FileSystemWatcher> _fileSystemWatcherMap;
public MainForm()
{
InitializeComponent();
_fileSystemWatcherMap = new Dictionary<string, FileSystemWatcher>();
}
public void Button1Click(object sender, EventArgs e)
{
string pathToWatch = #"C:\"; // Must be a different path each time otherwise will throw
var watcher = new FileSystemWatcher(pathToWatch);
_fileSystemWatcherMap.Add(pathToWatch, watcher);
}
This way all methods in the MainForm can access the file watchers.
If you need to share this among forms that are created from MainForm you can simply pass this data before showing the dialog.
If the other form is created in a different way the you can create a static class like this:
public static class FileWatcherMap
{
private static Dictionary<string, FileSystemWatcher> _fileSystemWatcherMap = new Dictionary<string,FileSystemWatcher>();
public static void AddWatcher(string path, FileSystemWatcher fsw)
{
_fileSystemWatcherMap.Add(path, fsw);
}
public static void RemoveWatcher(string path)
{
_fileSystemWatcherMap.Remove(path);
}
}
then in the click handler you add the watcher to this list:
public void Button1Click(object sender, EventArgs e)
{
string pathToWatch = #"C:\"; // Must be a different path each time otherwise will throw
var watcher = new FileSystemWatcher(pathToWatch);
FileWatcherMap.AddWatcher(pathToWatch, watcher);
}
Now FileWatcherMap class would be accessible from any other form

Change picturebox image inside form1 from test.cs

I am trying to update picture box image inside "form1" from another cs file
my code inside test.cs
slot_13.modifier = public;
and inside form1 i wrote this also
CheckForIllegalCrossThreadCalls = false;
test.cs
inventory_Viewer.viewer x = new inventory_Viewer.viewer();
x.slot_13.Image = Image.FromFile(#"C:\Users\Axmed\Google Drive\C# Source Codes\inventory Viewer\inventory Viewer\bin\Release\icon\icon_default.png");
But it doesn't work
If i used this line inside "form1"
x.slot_13.Image = Image.FromFile(#"C:\Users\Axmed\Google Drive\C# Source Codes\inventory Viewer\inventory Viewer\bin\Release\icon\icon_default.png");
image gets changed
Your code misses a lot of context, so I'm going to do a few assumptions. Given a MainForm that shows the InventoryViewerForm and also wants to change the image on the InventoryViewerForm, you could hold a reference to the second form like this:
// Your inventory_Viewer.viewer
public partial class InventoryViewerForm
{
public InventoryViewerForm()
{
}
}
// The form from which to show the viewer.
public partial class MainForm
{
private readonly InventoryViewerForm _inventoryViewerForm;
public MainForm()
{
_inventoryViewerForm = new InventoryViewerForm();
}
private void ShowInventoryViewerButton_Click(object sender, EventArgs e)
{
_inventoryViewerForm.Show();
}
private void ChangeImageButton_Click(object sender, EventArgs e)
{
// Dispose the previously loaded image.
if (_inventoryViewerForm.Image != null)
{
_inventoryViewerForm.Image.Dispose();
}
_inventoryViewerForm.Image = Image.FromFile("NewImage.png");
}
}
But this is bad design altogether. You don't want to tightly couple your forms like this, and you want to leverage the data binding of WinForms and the events of .NET for this. In order to properly implement that, you'll need to show more code.

Opening new Windows Form from another Form

Now before anyone goes and marks this question as a duplicate, I'd like to say that my problem differs from the other ones. I'm trying to open an existing Form from another, but I'm having problems in the sense that I've set some Forms to 'host' others (To transfer variables between them). Here's what I mean:
public partial class Schedule_Tasks : Form
{
readonly Schedules schedules;
public Schedule_Tasks(Schedules host)
{
this.schedules = host;
InitializeComponent();
}
So in this snippet of code, I'm trying to get the value of some variables from the Schedules form, into the Schedule_Tasks Form. So I've used the 'host' system. SO far this method works fine, but my problem occurs when I try to open a specific Form, from another that isn't 'hosting'. For example using:
new Schedules().Show();
So obviously when I'm declaring this, I'd put something like 'this' in the brackets after Schedules, but that doesn't work if the Form is being called outside of the 'host' Form. I'd just like to now is there something I'm missing or can change? Please let me know if any part isn't clear, it's a little difficult to explain. Any help is appreciated, Cheers.
EDIT
Here's the code that I'm working with now:
public partial class Schedual_Tasks : Form
{
readonly Scheduals scheduals;
public string selectedDevice;
public string getPath;
public string totalPath;
public Schedual_Tasks(Scheduals host)
{
this.scheduals = host;
InitializeComponent();
selectedDevice = scheduals.itemSelected;
}
private void Schedual_Tasks_Load(object sender, EventArgs e)
{
}
private void changeDirectory_Click(object sender, EventArgs e)
{
new Folder_Browser(this).Show(); //Error Occurs here
}
}
And here is the constructor for Folder_Browser, which is the Form I'm trying to call:
readonly Back_up_Options backOptions;
public string deviceSel;
public Folder_Browser(Back_up_Options host)
{
InitializeComponent();
this.backOptions = host;
deviceSel = backOptions.deviceSel;
}
Your (Folder_Browser) Form's constructor is declared as
public Folder_Browser(Back_up_Options host)
That means you cannot pass a Schedual_Tasks instance as the host parameter because there is no way to convert from a Schedual_Tasks object into a Back_up_Options object. The compiler detects this and creates an error message.
If you cannot pass the host parameter, you can pass null instead:
new Folder_Browser(null).Show();
But then you need to make sure that you check the backOptions member for null reference each time you use it. For example:
if(backOptions != null)
{
deviceSel = backOptions.deviceSel;
}
else
{
deviceSel = null;
}
That in turn means that you will need to check deviceSel for null each time you use it and so on.
And of course, using your Form without a "host" needs to be possible at all. If you have code that requires a "host", it will fail.

Windows Forms, Designer, and Singleton

I'm trying to work with Windows Forms and User Controls and thus far it's been nothing but a headache. I can't make the form or the controls static because the designer doesn't like that and when I use Singleton on my form and controls, the designer still throws errors at me.
My FormMain:
public partial class FormMain : Form
{
private static FormMain inst;
public static FormMain Instance
{
get
{
if (inst == null || inst.IsDisposed)
inst = new FormMain();
return inst;
}
}
private FormMain()
{
inst = this;
InitializeComponent();
}
MainScreen.cs:
public partial class MainScreen : UserControl
{
private static MainScreen inst;
public static MainScreen Instance
{
get
{
if (inst == null || inst.IsDisposed)
inst = new MainScreen();
return inst;
}
}
private MainScreen()
{
inst = this;
InitializeComponent();
}
If the constructor of MainScreen is public the program runs, but when I change it to private I now get an error in FormMain.Designer.cs saying "'Adventurers_of_Wintercrest.UserControls.MainScreen.MainScreen()' is inaccessible due to its protection level". It points to this line:
this.controlMainScreen = new Adventurers_of_Wintercrest.UserControls.MainScreen();
I think this is the instance of the class that the designer makes by default. Should I ditch the designer? Or is there a way around this? Or is there another way to make class properties accessible without using Singleton (since I can't seem to make the form or controls static)? Any help would be greatly appreciated.
You need to keep a reference to each instance of each form if you want to access the public properties of the instantiated form.
One way is to have a class with a static variable for each type of form:
class FormReferenceHolder
{
public static Form1 form1;
public static Form2 form2;
}
This way you would set the static variable whenever you instantiate a form, and then you can access that variable from anywhere in the program. You can go one step further with this and use properties that set up the form if it doesn't already exist:
class FormReferenceHolder
{
private static Form1 form1;
public static Form1 Form1
{
get
{
if (form1 == null) form1 = new Form1();
return form1 ;
}
}
}
...
static void Main()
{
Application.Run(FormReferenceHolder.Form1 );
}
I think I answered a previous question about this, which looks like it is what got you started down this route. The first point is that I wasn't recommending this pattern specifically, just trying to teach you more about how software developers can manage scope.
That said, the problem you are facing isn't insurmountable. You could hobble a public constructor by throwing an exception at runtime and not at design time, for instance, and modify Program.cs to use the static Instance instead of manually constructing the form.
But.
As I said in the other question, the better option would be to change architecture so that you don't need your library code to directly manipulate the GUI in the first place.
You can do this either by just having the GUI ask the library questions when it thinks it needs new data (simple functions) or by letting the GUI be notified when something needs to change. Either method would be better than having the library fiddle with labels directly.
A good place to start would be something like an MVC (model-view-controller) architecture, which I was alluding to in my previous answer. It might be best, though, to give us an idea of what your high-level program structure looks like now on a bit more detail. What are the main classes you are using in your system (not just the ones you've mentioned so far)? What is the main responsibility of each, and where does each live? Then our recommendations could be a little more specific.
EDIT
So, I have mocked up a quick demo of a possible alternative architecture, based on your comment.
I have the following in my project:
FormMain (Form)
TitleScreen (UserControl)
InGameMenu (UserControl)
MainScreen (UserControl)
GameController (Class)
GameModel (Class)
I didn't use Date and LoadSave, for now.
FormMain simply has an instance of each UserControl dropped on it. No special code.
GameController is a singleton (since you tried to use this pattern already and I think it would be helpful for you to try using a working version of it) that responds to user input by manipulating the model. Note well: you don't manipulate the model directly from your GUI (which is the View part of model-view-controller). It exposes an instance of GameModel and has a bunch of methods that let you perform game actions like loading/saving, ending a turn, etc.
GameModel is where all your game state is stored. In this case, that's just a Date and a turn counter (as if this were going to be a turn-based game). The date is a string (in my game world, dates are presented in the format "Eschaton 23, 3834.4"), and each turn is a day.
TitleScreen and InGameMenu each just have one button, for clarity. In theory (not implementation), TitleScreen lets you start a new game and InGameMenu lets you load an existing one.
So with the introductions out of the way, here's the code.
GameModel:
public class GameModel
{
string displayDate = "Eschaton 23, 3834.4 (default value for illustration, never actually used)";
public GameModel()
{
// Initialize to 0 and then increment immediately. This is a hack to start on turn 1 and to have the game
// date be initialized to day 1.
incrementableDayNumber = 0;
IncrementDate();
}
public void PretendToLoadAGame(string gameDate)
{
DisplayDate = gameDate;
incrementableDayNumber = 1;
}
public string DisplayDate
{
get { return displayDate; }
set
{
// set the internal value
displayDate = value;
// notify the View of the change in Date
if (DateChanged != null)
DateChanged(this, EventArgs.Empty);
}
}
public event EventHandler DateChanged;
// use similar techniques to handle other properties, like
int incrementableDayNumber;
public void IncrementDate()
{
incrementableDayNumber++;
DisplayDate = "Eschaton " + incrementableDayNumber + ", 9994.9 (from turn end)";
}
}
Things to note: your model has an event (in this case, just one of type EventHandler; you could create more expressive types of events later, but let's start simple) called DateChanged. This will be fired whenever DisplayDate changes. You can see how that happens when you look at the property definition: the set accessor (which you will NOT call from your GUI) raises the event if anyone is listening. There are also internal fields to store game state and methods which GameController (not your GUI) will call as required.
GameController looks like this:
public class GameController
{
private static GameController instance;
public static GameController Instance
{
get
{
if (instance == null)
instance = new GameController();
return instance;
}
}
private GameController()
{
Model = new GameModel();
}
public void LoadSavedGame(string file)
{
// set all the state as saved from file. Since this could involve initialization
// code that could be shared with LoadNewGame, for instance, you could move this logic
// to a method on the model. Lots of options, as usual in software development.
Model.PretendToLoadAGame("Eschaton 93, 9776.9 (Debug: LoadSavedGame)");
}
public void LoadNewGame()
{
Model.PretendToLoadAGame("Eschaton 12, 9772.3 (Debug: LoadNewGame)");
}
public void SaveGame()
{
// to do
}
// Increment the date
public void EndTurn()
{
Model.IncrementDate();
}
public GameModel Model
{
get;
private set;
}
}
At the top you see the singleton implementation. Then comes the constructor, which makes sure there's always a model around, and methods to load and save games. (In this case I don't change the instance of GameModel even when a new game is loaded. The reason is that GameModel has events and I don't want listeners to have to unwire and rewire them in this simple sample code. You can decide how you want to approach this on your own.) Notice that these methods basically implement all the high-level actions your GUI might need to perform on the game state: load or save a game, end a turn, etc.
Now the rest is easy.
TitleScreen:
public partial class TitleScreen : UserControl
{
public TitleScreen()
{
InitializeComponent();
}
private void btnLoadNew(object sender, EventArgs e)
{
GameController.Instance.LoadNewGame();
}
}
InGameMenu:
public partial class InGameMenu : UserControl
{
public InGameMenu()
{
InitializeComponent();
}
private void btnLoadSaved_Click(object sender, EventArgs e)
{
GameController.Instance.LoadSavedGame("test");
}
}
Notice how these two do nothing but call methods on the Controller. Easy.
public partial class MainScreen : UserControl
{
public MainScreen()
{
InitializeComponent();
GameController.Instance.Model.DateChanged += Model_DateChanged;
lblDate.Text = GameController.Instance.Model.DisplayDate;
}
void Model_DateChanged(object sender, EventArgs e)
{
lblDate.Text = GameController.Instance.Model.DisplayDate;
}
void Instance_CurrentGameChanged(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void btnEndTurn_Click(object sender, EventArgs e)
{
GameController.Instance.EndTurn();
}
}
This is a little more involved, but not very. The key is, it wires up the DateChanged event on the model. This way it can be notified when the date is incremented. I also implemented another game function (end turn) in a button here.
If you duplicate this and run it, you'll find that the game date is manipulated from lots of places, and the label is always updated properly. Best of all, your controller and model don't actually know anything at all about the View-- not even that it's based on WinForms. You could as easily use those two classes in a Windows Phone or Mono context as anything else.
Does this clarify some of the architecture principles I and others have been trying to explain?
In essence the problem is that when the application runs, it's going to try to instantiate the main form-window. But by using the Singleton pattern, you're essentially forbidding the application from doing that.
Take a look at the sample code here:
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.aspx
You'll notice in particular this section:
[STAThread]
public static void Main()
{
// Start the application.
Application.Run(new Form1());
}
Notice how the program is trying to instantiate Form1. Your code says, nah, I don't really want that since you mark the constructor as private (same holds true for static forms as well). But that's counter to how windows forms is supposed to work. If you want a singleton form-window, just don't make any more. Simple as that.

Categories

Resources