pass DataStruct around more efficiently (winforms) - c#

i have a DataStruct[] that gets used multiple times on a form. It is created from reading a CSV file. The structure has 5 "columns", each with about 100,000 rows
in particular, i have a bunch of ChangeEvents (trackbars, textboxes, etc.), where each change event is re-making the data structure, but i feel it's slowing down the changes (they are rendering on a graph, and it's slow to react)
once i have the actual structure, i don't need to change it, i just need to work with the data. I don't know how i can create the DataStruct[] only once, then pass that struct into the various change events without rebuilding it
the following code currently exists in all my change events (edited for brevity):
string[] fileArray = File.ReadAllLines(tempfile);
DataStruct[] data = new DataStruct[fileArray.Length];
for (int i = 0; i < fileArray.Length; i++)
{
List<string> dataList = fileArray[i].Split(',').ToList<string>();
data[i].X = (Convert.ToSingle(dataList[0]));
}
my confusion is: i have a variety of void() methods that use the structure, and it's easy to pass into those. however i don't understand how to pass into a change event, since the handler refreshes every time the change occurs, i don't know where to call the pass. For example:
private void trackBar1_Scroll(object sender, EventArgs e)
{
label282.Text = trackBar1.Value.ToString();
chart17.Series[0].Points.Clear();
VoltageChanger();
}
how would i call this Scroll change without re-doing the struct? VoltageChanger() uses the struct to do some stuff and make a new graph based on the trackbar value.
or...am i silly in thinking that there may be a slowdown here, and it's just the graph rendering that won't get any better?
thanks (edit: i have been reading but event handling outside of the winforms defaults is currently new to me)

I suggest you read up on variable scope here. For your example, you need to read the information once and have it accessible to all the methods within the class. That is what a module-level variable will do. In the code snippet below, notice the declaration for DataStruct is within the class definition, not within any individual method. This makes the variable visible to that class' methods.
public partial class Form1 : Form
{
private DataStruct[] _data; // <-- Module level variable
public Form1()
{
InitializeComponent();
LoadData();
}
private void LoadData()
{
// Open file code omitted
_data = new DataStruct[fileArray.Length];
// Read data into file omitted
}
private void Method1()
{
// _data will be accessible here because it is a module-level variable
}
}

Related

Best way to package a series of commands that will be reused throughout your program?

I have several different instances in my code where I find myself copying/pasting blocks of code that get reused in other areas of my application. I'm relatively new to C# and am unsure of how to package this information in a way that is more convenient and safer for reuse. Any way I can assign this block to some sort of function/class so that all I have to do is call the function/class name to run this series of commands?
//Clear all form entries.
comboBoxAccount.ResetText();
comboBoxAccount.SelectedIndex = -1;
textBoxAccountName.Clear();
comboBoxPrinter.ResetText();
comboBoxPrinter.SelectedIndex = -1;
comboBoxCheckType.ResetText();
comboBoxCheckType.SelectedIndex = -1;
textBoxCheckNumber.Clear();
textBoxRoutingNumber.Clear();
textBoxAccountNumber.Clear();
textBoxMicrFormat.Clear();
textBoxAccountAddress.Clear();
textBoxBankAddress.Clear();
You can create a public Static class or a public class with a Static method
ResetControls and call it whatever number of times you want. This method efficient when you want to do the reset from different classes(modules).
However, If you need to call that piece of code from code behind of a form then above method is overkill. Just create a private function and call it.
private void reset()
{
comboBoxAccount.ResetText();
comboBoxAccount.SelectedIndex = -1;
textBoxAccountName.Clear();
comboBoxPrinter.ResetText();
comboBoxPrinter.SelectedIndex = -1;
comboBoxCheckType.ResetText();
comboBoxCheckType.SelectedIndex = -1;
textBoxCheckNumber.Clear();
textBoxRoutingNumber.Clear();
textBoxAccountNumber.Clear();
textBoxMicrFormat.Clear();
textBoxAccountAddress.Clear();
textBoxBankAddress.Clear();
}

1 Publisher Class Event, Multiple Subscribers, Different Parameters

I am using delegates and events to pass data around in my application, but want the triggering of 1 event to pass different sets of data to different places.
Problem
I have a main class which performs some work. Once the work is complete, I want it to notify a number of other classes (including the UI) so that other actions can be carried out. The information that the other classes needs is different.
Example
Main.cs - Performs an action and wants to update the UI, send a text message and write to a log file.
UI updates a DataGrid so needs the individual fields.
The log file writer needs all the whole line as an array / List.
The text message code needs the Line, LineNumber and FileName, but as a tab delimited string.
The below all works correctly when I am only trying to update the UI, but when I try to send different information to different places then I run into errors.
I have tried creating different extensions of EventArgs, but if I try to declare 2 delegates with different signatures then I get an error message.
Any help would be much appreciated.
FrmMain.cs
Main main = new Main();
main.PatternFound += OnPatternFound;
main.DoSomeWork();
private void OnPatternFound(object source, LineEventArgs e)
{
UpdateDataGrid(e.Line, e.FileName, e.LineNumber);
}
private void UpdateDataGrid(string line, string file, int lineNumber)
{
if (InvokeRequired)
{
Invoke(new Action<string, string, int>(UpdateDataGrid), line, file, lineNumber);
}
else
{
dgResults.Rows.Add(line, file, lineNumber);
}
}
Main.cs
public delegate void PatternFoundEventHandler(object sender, LineEventArgs e);
public event PatternFoundEventHandler PatternFound;
protected virtual void OnPatternFound(string line, string fileName, int lineNumber)
{
PatternFound?.Invoke(this, new LineEventArgs { Line = line, FileName = fileName, LineNumber = lineNumber });
}
public void DoSomeWork()
{
//Finished my work
OnPatternFound(line, file, lineNumber);
}
LineEventArgs.cs
public class LineEventArgs : EventArgs
{
public string Line { get; set; }
public string FileName { get; set; }
public int LineNumber { get; set; }
}
What you are trying to achieve is possibly not a good approach for several reasons. Your decided to stick to events which means you decided to do what is called "inversion of control".
You implemented your main class in a way that says: "I am doing some sort of pattern matching and I will tell whoever is interested what I found and I am doing it in the way my event-args implementation defines." As a result it's now up to the subscribers to take this information as is or leave it.
Strictly spoken, what you said...
Performs an action and wants to update the UI, send a text message and write to a log file.
...is not what you implemented, because the main class does nothing of this. Anyway, in my opinion it is a viable solution to keep it event-driven, but then you would need to change your upstream code.
You could for example attach three different event handlers to this single event. Every handler would then have to transform the data in a format it needs: The logging handler would have to transform it to an array, the UI handler would have to concatenate it and so on.
The other alternative would be that you have one handler and dispatch to three different methods, like the OnPatternFound method would call a UpdateUI method and a Log method and so on.
Lastly you could also get rid of inversion of control and move your logic to the main class, but I would not recommend it! Your approach is good, but you should just not try to invoke the same event in three different ways. That's up to the subscribers.

Modify an object in multiple layers of classes

I have an application with the following:
A data layer class containing the data handling logic and the data itself.
public class DataLayer_JSON
{
CacheData chd = new CacheData();
//The chd object contains the actual data used in the DataLayer_JSON class
public DataLayer_JSON(string relPath) }
A main menu form (essentially it could be just any class)
public partial class MainMenuForm : Form
{
DataLayer_JSON data = new DataLayer_JSON("questions.txt");
...
private void btnEditon_Click(object sender, EventArgs e)
{
...
using (var EF = new EditorForm(data.GetCathegories(), data.GetDifficulties(), data.GetQuestions()))
{
var result = EF.ShowDialog();
if(result == DialogResult.OK)
{
data.ClearQuestions(); //Clear all cached questions
//Since we are still in scope of the EditorForm (EF) within the using clause, we can access it's members
data.AddQuestionRange(EF.AllQuestions); //Replace cache with the edited list
}
}
...
//Here we save the data permanently to a text file when closing the program.
//This could also be done from the EditorForm if we wanted to
private void MainMenuForm_FormClosing(object sender, FormClosingEventArgs e)
{
data.Save("questions.txt");
}
An editor form (started from #2)
public EditorForm(IEnumerable<INameObject> Cathegories, IEnumerable<INameObject> Difficulties, IEnumerable<Question> oldQuestions)
{
...
}
In #2, I create and initialize the data layer instance (#1). I want to modify the data contained in #1 from within #3, but so far, I have only been able to pass by value the contents from #1 to #3, via #2. The results are then returned from #3 to #2 and handled there.
I've read about passing by reference in C#, but my conclusion is that you cant assign a variable to the reference, and then have that variable modify the original data.
I read about the references in C# in the following places this time, as well as reading extensively about this topic many times before:
C# reference assignment operator? How do I assign by "reference" to a class field in c#?
So the question is:
How can I go about changing the content of an instance of #1 directly in #3?
How can I go about changing the content of an instance of #1 directly in #3?
Well, you'd start by passing the instance of #1 to #3. Currently you are not doing that.
So for example:
public EditorForm(DataLayer_Json data)
{
...
}
and
using (var EF = new EditorForm(data))
Then inside the EditorForm, you can do whatever you want to data (except for reassigning the actual reference) and those changes will be reflected outside of EditorForm as well.
data.ClearQuestions(); // Works fine, mutating the object.
data = new DataLayer_JSON(); // Doesn't affect the `data` variable that was passed in.

use PictureBox from another class c#

I hope my question will be clear
In Form1.cs i have PictureBox named: ico_ok
i would like to use this PictureBox in my new class that i bulit.
when i start typing ico... nothing appears.
what is the way to use this object in another class?
here the code:
public void button2_Click(object sender, EventArgs e)
{
lbl_check.Visible = true;
btn_continue.Visible = false;
txtbox_cusnumber.Enabled = false;
string userID = (txtbox_cusnumber.Text.ToString());
CheckOUinADexist checkou = new CheckOUinADexist(userID);
}
after that look at the new class:
namespace ChekingOUinActiveDirectory
{
class CheckOUinADexist
{
public CheckOUinADexist(string userID)
{
//this place i would like to use ico_ok
}
}
}
Thank you for helping.
Maayan
The simplest approach is probably to provide that class with the dependency on the PictureBox:
public CheckOUinADexist(string userID, PictureBox pbox)
{
pbox.[your code]
}
Then supply it when calling the method:
CheckOUinADexist checkou = new CheckOUinADexist(userID, ico_ok);
Whether or not this is the ideal approach depends on what you're going to be doing with that PictureBox inside that object, how portable that object needs to be across technology platforms, etc.
In general you don't want UI elements to permeate into non-UI logic. If CheckOUinADexist is a UI-bound class and exists solely to help the UI, then this isn't a problem. If it's part of business logic then you wouldn't want to couple that logic with the UI technology. Instead, you'd likely pass it the data needed from the PictureBox, but not the PictureBox itself.
This all depends a lot on the overall architecture of what you're trying to achieve here, which we don't know.
Basically you'd give the target class a reference to the "shared data" -- picture box in this case.
class CheckOUinADexist
{
PictureBox _picBox
public CheckOUinADexist(string userID, PictureBox picBox)
{
//this place i would like to use ico_ok
_picBox = picBox;
_picBox.myAction();
}
}
Whether you want to actually stored Picturebox as a field (as opposed to just use a parameter) depends on whether you need access to the field throughout the lifetime of the instance(s) or whether it is just needed for object construction. If you are not sure, you are safer (IMHO) just storing a reference in a field. Make further uses of it a lot easier.

Why Windows Form TextBox won't update from outside class?

Newbie here. I'm running Visual Studio C# Express 2008. I have two Windows Forms, each with a TextBox. The textboxes update within the same class but not as the result of a invoked method from outside the class. I need to be able to update tbRooms.Text when the UpdateListOfRooms() method is invoked. I've outlined the problem in pseudo-code below. I appreciate your help!
fLocations.cs
fLocations_Load()
{
this.tbLocations.Text = Properties.Settings.Default.LocationID + " locationsLoad"; --updates
}
dgvLocations_SelectionChanged()
{
var rooms = new fRooms();
rooms.tbRooms.Text = Properties.Settings.Default.LocationID + " locationssSelectionChanged"; --updates
rooms.UpdateListOfRooms();
}
fRooms.cs
fRooms_Load()
{
this.tbRooms.Text = Properties.Settings.Default.LocationID + " roomsLoad"; --updates
}
UpdateListOfRooms()
{
this.tbRooms.Text = Properties.Settings.Default.LocationID + " roomsUpdateListOfRooms"; --does NOT update; still says "roomsLoad"
}
Updated 8/20/14:
I've been a busy bee :) I read all the parts of the tutorial by #jmcilhinney and decided to approach this by including references to the two forms, Locations and Rooms, in the MainMenu class that launches them:
(MainMenu.cs) Instances of Locations and Rooms are created. In the constructor, 'rooms' is passed to the 'locations' instance and both forms are shown.
(Locations.cs) Another Rooms instance is created at class scope so it can be seen by all methods of the class. In the constructor, this instance is set to the one being passed by MainMenu which means that this class is working with the same instance created in MainMenu. When the user changes the selection on dgvLocations, the 'dgvLocations_SelectionChanged' event is fired which invokes the Rooms.UpdateRooms method.
(Rooms.cs) The 'UpdateRooms' method displays a new set of rooms based on the passed value of 'locationID'.
This link was helpful. Visual C# - Access instance of object created in one class in another.
public partial class MainMenu : Form
{
Locations locations;
Rooms rooms;
public MainMenu()
{
rooms = new Rooms();
locations = new Locations(rooms);
locations.Show();
rooms.Show();
InitializeComponent();
}
}
public partial class Locations : Form
{
Rooms rooms;
public Locations(Rooms r)
{
rooms = r;
InitializeComponent();
}
private void Locations_Load(object sender, EventArgs e)
{
// Populate this.dgvLocations using SQL query.
}
private void dgvLocations_SelectionChanged(object sender, EventArgs e)
{
// Update the rooms instance with current locationID.
rooms.UpdateRooms(dgvLocations.CurrentCell.Value.ToString());
}
}
public partial class Rooms : Form
{
public Rooms()
{
InitializeComponent();
}
private void Rooms_Load(object sender, EventArgs e)
{
// Populate this.dgvRooms using SQL query.
}
public void UpdateRooms(string locationID)
{
// Update dgvRooms based on user changing the locationID in dgvLocations
}
}
In the first code snippet, you create a new fRooms object but you never call its Show or ShowDialog method, which means that you never display it to the user. That means that any changes you make to that form will not be seen. Presumably the user can see an fRooms object though, but you are not making any changes to that one.
Consider this. Let's say that I give you a note pad and you open it and look at the first page. Let's say that I now buy a new note pad and write on the first page of it. Would you expect to see the words I wrote magically appear on the page in front of you? Of course not. We both are holding a note pad but they are two different note pads. You're looking at one and I'm writing on the other, so you won;t see what I write.
The same goes for your forms. They are both fRooms objects but they are two different fRooms objects. Changes you make to one will not affect the other. If you want the user to see the changes you make then you must make those changes to the fRooms object that the user is looking at.

Categories