Populate listview multiple times using C# Class - c#

I am trying to populate a listview each time a button is selected. At the moment I have the button populating the listview once each time. Each time a new value is entered it will overwrite the current listview.
I want to be able to add in a new item and continue till there is multiple rows.
protected void btnAddSkuBarcode_Click(object sender, EventArgs e)
{
var SKUS = new List<SkuBar>
{
new SkuBar {SkuBarcode = txtSkuBarcode.Text , Qty = txtQty.Text},
};
lvWebLabels.DataSource = SKUS;
lvWebLabels.DataBind();
}
public class SkuBar
{
public string SkuBarcode { get; set; }
public string Qty { get; set; }
}

Currently you're creating a new variable (SKUS) every time the button is clicked. When you bind to that new list, you lose anything previously bound to the control.
Since the list needs to persist in a greater scope than just the method, put it in something like class scope:
List<SkuBar> SKUS = new List<SkuBar>();
Then just add to the existing list:
protected void btnAddSkuBarcode_Click(object sender, EventArgs e)
{
SKUS.Add(new SkuBar {SkuBarcode = txtSkuBarcode.Text , Qty = txtQty.Text});
lvWebLabels.DataSource = SKUS;
lvWebLabels.DataBind();
}
Note that this will only work in a stateful system. If by chance you're using WebForms then the object itself is also dropped from scope for each request, so you'd need to persist the data somewhere else. Session state, database, etc.

You're creating a new List<T> every time the button is clicked so you lose the previous one. Define the List<T> outside of the button press so it can be re-used:
namespace YourNamespace
{
public class YourClass
{
List<SkuBar> SKUS;
public YourClass() // Or form load or whatever
{
SKUS = new List<SkuBar>();
}
protected void btnAddSkuBarcode_Click(object sender, EventArgs e)
{
SKUS.Add(new SkuBar {SkuBarcode = txtSkuBarcode.Text , Qty = txtQty.Text});
lvWebLabels.DataSource = SKUS;
lvWebLabels.DataBind();
}
}
}

Related

Prevent raising SelectionChanged event on a DataRow's Button click within a C# DataGridView

I have a DataGridView which has a Button column.
Each record in the DataGridView will have a separate Button. A QueueMusic object containing Row-specific data should be queued upon clicking a Row's Button.
I currently have this working by placing a custom class (QueueMusic) onto a Queue collection using that collection's .Enqueue() method.
I have two event handler methods.
DataGridViewAllMusic_SelectionChanged method, which begins playing music associated the current Button's Row.
DataGridViewAllMusic_CellClick method, which handles queueing the playlist (defined within the QueueMusic class) that is associated with the current Button's Row.
The Problem
Once music is playing, each subsequent click of a Row's Button interrupts the currently playing music with music defined by the newest Row's QueueMusic object.
I have a class QueueMusic.
internal class QueueMusic
{
public string Url { get; set; }
public int RowIndex { get; set; }
public static Queue<QueueMusic> queulist = new Queue<QueueMusic>();
}
and a DataGridView CellClickEvent
private void DataGridViewAllMusic_CellClick(object sender, DataGridViewCellEventArgs
e)
{
var senderGrid = (DataGridView)sender;
if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn && e.RowIndex >=
0)
{
QueueMusic qm = new QueueMusic();
qm.RowIndex = e.RowIndex;
qm.Url = DataGridViewAllMusic.Rows[e.RowIndex].Cells[2].Value.ToString();
QueueMusic.queulist.Enqueue(qm);
}
}
private void DataGridViewAllMusic_SelectionChanged(object sender, EventArgs e)
{
play();
}
Desired Behavior
How can I prevent a Row's Button click event from being handled by DataGridViewAllMusic_SelectionChanged when music is already playing?
The desired behavior is to queue the next playlist in the background without inturrupting the currently playing playlist.
You omitted several crucial parts of your code when writing your post here.
You need to include the play() method's logic. Understanding what is happening there will greatly help us help you.
For example, what data comes from the URL that you inject into each instance of QueueMusic?
You'll definitely need to define a Form-level property to hold a single instance of whatever Queue or Music class you have. Once instantiated on Form load, that object should only be added to when you click a Row's Button. You'll need to handle Dequeueing playlists as they finish; maybe that's handled by other code that you didn't show.
The static Queue that you have defined inside of QueueMusic is not a good design.
Music playlists start over each time you click because you are overwriting the QueueMusic object on each click.
I would suggest something like the following:
internal class MusicQueue
{
public MusicQueue()
{
PlaylistQueue = new Queue<Playlist>();
// any other setup...
}
public Queue<Playlist> Playlist { get; set; }
}
internal class Playlist
{
public string Url { get; set; }
public int RowIndex { get; set; }
public void Play()
{
// ?
}
public bool IsPlaying { get; set; }
}
Within your Form's main class code, do the following:
...
private MusicQueue queue { get; set; }
private void Form1_Load(object sender, EventArgs e)
{
queue = new MusicQueue();
// any other setup...
}
...
Then within the two handlers:
private void DataGridViewAllMusic_CellClick(object sender, DataGridViewCellEventArgs e)
{
...
// get data from the DataGridView's Row
// put the new playlist on the queue
// you may want to check if the current Row's playlist is already on the queue
queue.Playlist.Add(new Playlist() { (construct the new playlist here) };
}
private void DataGridViewAllMusic_SelectionChanged(object sender, EventArgs e)
{
// depends on what `play()` is doing, but I'd probably
// suggest checking if any playlist is playing.
// if not, start the first playlist
if (queue.Playlist.Any(p => p.IsPlaying)
{
queue.Playlist.FirstOrDefault().Play();
}
}

How do I get a label in a form to display the values of a property

In my C# windows form I have 2 forms. I would like to display a collection of strings in a label on a form. When I debug I show the 2 elements in my array but they are not showing in the label I am passing it to. When I hover of toString the data is there but how to I pass it to sender so it will display in the label control I have on my form?
In the snippet of code below to data is in toString but how do I get it from there down to sender.ToString????
public AccountReport_cs(Func<string> toString)
{
this.toString = toString;
}
private void AccountReport_cs_Load(object sender, EventArgs e)
{
label1.Text = sender.ToString();
}
This is another piece of the code that will open form2 where the information should be displayed.
private void reportButton2_Start(object sender, EventArgs e)
{
AccountReport_cs accountReport = new AccountReport_cs(allTransactions.ToString);
accountReport.ShowDialog();
}
Here is the last piece of code and this will show how the data gets to EndOfMonth.
public class Transaction
{
public string EndOfMonth { get; set; }
}
public override List<Transaction> closeMonth()
{
var transactions = new List<Transaction>();
var endString = new Transaction();
endString.EndOfMonth = reportString;
transactions.Add(endString);
return transactions;
}
If you need to send information between forms, the best thing you can do is create a property in the target form and assign the value you want to send before displaying the form; thus you will not need to change the default constructor of the form.
// Destiny form
public partial class FormDestiny : Form {
// Property for receive data from other forms, you decide the datatype
public string ExternalData { get; set; }
public FormDestiny() {
InitializeComponent();
// Set external data after InitializeComponent()
this.MyLabel.Text = ExternalData;
}
}
// Source form. Here, prepare all data to send to destiny form
public partial class FormSource : Form {
public FormSource() {
InitializeComponent();
}
private void SenderButton_Click(object sender, EventArgs e) {
// Instance of destiny form
FormDestiny destinyForm = new FormDestiny();
destinyForm.ExternalData = PrepareExternalData("someValueIfNeeded");
destinyForm.ShowDialog();
}
// Your business logic here
private string PrepareExternalData(string myparameters) {
string result = "";
// Some beautiful and complex code...
return result;
}
}

C# WinForm - Calculate Hours By Given 3 Number using Method in Class

I'm creating a simple distance calculator. I have a window form with 2 combo boxes. The first combobox has the default place which is the Place1 and the second combobox has the value of Place1,Place2 and Place3.
Here's my code in my class.
public class classDistance
{
public string Places { get; set; }
public int DistanceKm { get; set; }
public static void Hours() {
List<string> Cities = new List<string>();
Cities.Add("Place1");
Cities.Add("Place2");
Cities.Add("Place3");
List<int> DestinationKm = new List<int>();
DestinationKm.Add(10); // Place1 = 10Km
DestinationKm.Add(20); // Place2 = 20Km
DestinationKm.Add(30); // Place3 = 30Km
return;
}
}
//Place1 has the speed of 80kmph.
And when the user click the button in the Form, it will calculate the Hours from the method class classDistance
// Inside my form
public Form1()
{
InitializeComponent();
}
classDistance classname = new classDistance();
private void button1_Click(object sender, EventArgs e)
{
if(comboBox1.Text == "Place1"){
// call the method on the classname if the user click the button and calculate the hours
}
}
How can I do that?
Thank you in advance!
As you made method Hours() static, you can simply call it by the class without creating any instance, like that:
private void button1_Click(object sender, EventArgs e)
{
ClassDistance.Hours();
}
But now your method just fills the lists, not calculates hours actually.
UPD:
I think it's better off using dictionary for storing and getting data in your case:
var citiesDistance = new Dictionary<string, int>
{
{"Place1", 10},
{"Place2", 20},
{"Place3", 30}
};
Then, you can get appropriate value by the place's name, e.g.:
var name = "Place2";
var length = citiesDistance[name];
And after that, you can calculate hours, needed to get to the place:
var speed = 80;
var hours = length/speed;
Hope, I've understood your question correctly)
At the moment you have a list of places and a list of distances which aren't really related to each other.
What I would do is create a new class which contains both the place name and distance, e.g.:
public class Destination
{
public string Name {get;set;}
public int DistanceKm {get;set;}
// ctor
public Destination(String name, int distanceKm)
{
Name = name;
DistanceKm = distanceKm;
}
public int getHours (int speed)
{
// do your calculation for the number of hours here
return hours;
}
}
You can then populate the destination with it's name and distance via the constructor, and use the getHours method to return the number of hours based on the speed you pass into it.
You could also create a List if you were handling multiple destinations

Need some guide on simple undo/redo

This is my first C# application, entirely self-taught without any prior software programming background. I did some research on Undo/Redo but could not find anything helpful (or easy to understand). Therefore, I'm hoping someone can help me in designing undo/redo function for my program (winforms application). The application consist of a main form where subsequent child forms will be called to record user specified values during certain events (button clicks etc). After every event is handled, a bitmap will be drawn in buffer and then loaded to a picturebox within the main form during the OnPaint event of the main form. Each input in separated into custom class objects and added into separate List and BindingList. Objects contained within List are used for graphics (to indicate coordinates etc) while objects in BindingList are used to display some important values on DataGridView. Just to give a brief description, the codes look something like this:
public partial class MainForm : form
{
public class DataClass_1
{
public double a { get; set; }
public double b { get; set; }
public SubDataClass_1 { get; set; }
}
public class SubDataClass_1
{
public double x { get; set; }
public double y { get; set; }
public string SomeString { get; set; }
public CustomEnum Enum_SubDataClass_1 { get; set; }
}
public class DisplayDataClass
{
public string SomeString { get; set; }
public double e { get; set; }
public double f { get; set; }
}
public enum CustomEnum { Enum1, Enum2, Enum3 };
// Lists that contain objects which hold the necessary values to be drawn and displayed
public List<DataClass_1> List_1 = new List<DataClass_1>();
public List<DataClass_2> List_2 = new List<DataClass_2>(); // Object has similar data types as DataClass_1
public BindingList<DisplayDataClass> DisplayList = new BindingList<DisplayDataClass>();
Bitmap buffer;
public MainForm()
{
InitializeComponent();
dgv.DataSource = DisplayList;
}
private void DrawObject_1()
{
// some drawing codes here
}
private void DrawObject_2()
{
// some drawing codes here
}
protected override void OnPaint(PaintEventArgs e)
{
DrawObject_1();
DrawObject_2();
pictureBox1.Image = buffer;
}
// Event to get input
private void action_button_click(object sender, EventArgs e)
{
ChildForm form = new ChildForm(this);
form.ShowDialog();
Invalidate();
}
}
The child forms' codes look something like this:
public partial class ChildForm : form
{
public ChildForm(MainForm MainForm)
{
InitializeComponent();
// Do something
}
private void ok_button_click(object sender, EventArgs e)
{
DataClass_1 Data_1 = new DataClass_1();
DisplayDataClass DisplayData = new DisplayDataClass();
// Parsing, calculations, set values to Data_1 and DisplayData
MainForm.List_1.Add(Data_1);
MainForm.DisplayList.Add(DisplayData);
this.DialogResult = System.Windows.Forms.DialogResult.OK;
this.Close();
}
}
Since all necessary data are stored in the lists and will only be changed after certain events are triggered (mostly button clicks), therefore I tried to use these lists to determine the state of the application during run time. My approach in implementing the undo/redo function is by adding the following codes:
public partial class MainForm : form
{
public class State()
{
public List<DataClass_1> List_1 { get; set; }
public List<DataClass_2> List_2 { get; set; }
public BindingList<DisplayDataClass> DisplayList { get; set; }
// and so on
public State()
{
List_1 = new List<DataClass_1>();
List_2 = new List<DataClass_2>();
DisplayList = new BindingList<DisplayDataClass>();
}
}
State currentState = new State();
Stack<State> undoStack = new Stack<State>();
Stack<State> redoStack = new Stack<State>();
private void MainForm_Shown(object sender, EventArgs e)
{
// Saves original state as first item in undoStack
undoStack.Push(currentState);
}
protected override void OnPaint(PaintEventArgs e)
{
// Update lists from currentState before drawing
List_1 = new List<DataClass_1>(currentState.List_1);
List_2 = new List<DataClass_2>(currentState.List_2);
DisplayList = new BindingList<DisplayDataClass>(currentState.DisplayList);
}
// When undo button is clicked
private void undo_button_Click(object sender, EventArgs e)
{
if (undoStack.Count > 0)
{
redoStack.Push(currentState);
undoStack.Pop();
currentState = undoStack.Last();
Invalidate();
}
}
// When redo button is clicked
private void redo_button_Click(object sender, EventArgs e)
{
// Have not thought about this yet, trying to get undo done first
}
// Events that trigger changes to values held by data objects
private void action_button_Click(object sender, EventArgs e)
{
// Replace the following code with previously stated version
if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
ChildForm form = new ChildForm(this)
UpdateState();
undoStack.Push(currentState);
Invalidate();
}
}
// To update currentState to current values
private void UpdateState()
{
currentState.List_1 = new List<DataClass_1>(List_1);
currentState.List_2 = new List<DataClass_2>(List_2);
currentState.DisplayList = new BindingList<DisplayDataClass>(DisplayList);
// and so on
}
}
Result:
The application does not perform the undo function correctly. The program shows the correct output under normal conditions but when the undo event is triggered, regardless of how many objects have been drawn, the application reverts back to initial state (the state where there is no recorded data). I've used System.Diagnostics.Debug.WriteLine() during events where the stack is changed to check the number of counts within undoStack and it seems to give the correct counts. I'm guessing that the lists need to be copied/cloned in a different manner? Or am I doing something wrong here? Can anyone please guide me? Performance, readability, resource management, future maintenance and etc need not be considered.
There are a lot of approaches that will work, each with different strengths and weaknesses, but I generally like to define an abstract Action class and then a separate UndoRedoStack class.
The Action class would have two methods (Do and Undo) which each subclassed Action can implement. You isolate any logic that can "change state" to these Action subclasses thereby keeping that logic neatly encapsulated.
The UndoRedoStack is like a regular stack except with three core methods.
ApplyAction (like Push)
UndoAction (like Pop, but be sure to only
move the pointer/index without truncating or throwing away any
existing actions).
RedoAction (like Push, but you use the next value
already in the underlying stack/list instead of pushping/inserting a
new one).
Usually I find the biggest challenge then becomes designing each Action subclass in such a way that it maintains enough information to both undo and redo itself. But being able to encapsulate all state manipulation logic to individual Action subclasses usually makes it easiest for me to maintain in the long run.
You are storing reference objects in your stacks. If you want your method to work, you need to implement a clone() method in your state object, and store a new clone each time, otherwise, changes made are made to each member of the stack, as they all point to the same reference object.

Page item not being persistent?

In my ASP.NET page, I have a generic class that is defined as below:
public partial class log_states : BasePage
{
protected class StatesUsed
{
public int StateCode { get; set; }
public string StateName { get; set; }
}
private List<StatesUsed> _statesUsed;
}
In the Page_Load() event, I initialize _statesUsed like below, and bind it to a grid:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
_statesUsed = new List<StatesUsed>();
BindMyGrid();
}
}
private void BindMyGrid()
{
gvStates.DataSource = _statesUsed;
gvStates.DataBind();
}
I then have a form to add new States. When the user adds a state, I'm trying to add it to the local _statesUsed variable, and rebind the grid. Example:
protected void btnAddState_Click(object sender, EventArgs e)
{
_statesUsed.Add(new StatesUsed { StateCode = 1, StateName = "Test" });
BindMyGrid();
}
This always fails when trying to add the new item saying "Object reference not set to an instance of an object"...
How do I keep _statesUsed persistant? The idea is to add all user input using the generic class and then update the database at one go. If you know of another way to accomplish this, I'd be very grateful.
Thanks in advance!
Instead of
private List<StatesUsed> _statesUsed;
I'm usually using something similar to:
private List<StatesUsed> _statesUsed
{
get
{
var result = ViewState["_stateUsed"] as List<StatesUsed>;
if ( result == null )
{
result = new List<StatesUsed>();
ViewState["_stateUsed"] = result;
}
return result;
}
}
I.e. I am persisting page class variables to the ViewState.
If you want to keep stuff "alive" through multiple postbacks you either have to store stuff to a database, use Session, use the Viewstate, or store it temporarily in shared server memory. Which of these you choose is dependent on your use case,
In your case I would probably add an asp:HiddenField runat="server" ID="HiddenFieldUsedStateIDs" in which I wrote the IDs comma separated whenever there is a change and then read the values into the generic list in Page_Load (on every Page_Load, not just !IsPostBack)
This would utilize the Viewstate mechanism in Asp.Net to write the values to the rendered HTML and read it back into the HiddenField's value on each post
Asuuming that your viewstate in not disabled, you could do,
protected void btnAddMat_Click(object sender, EventArgs e)
{
List<StatesUsed> temp = null;
temp = (List<StatesUsed>)gvStates.DataSource;
if(temp != null)
temp.Add(new StatesUsed { StateCode = 1, StateName = "Test" });
gvStates.DataBind();
}

Categories

Resources