I want to create a simple listbox, which is binded to a linkedlist.
The list should only ever hold 5 items at any given time.
When a new item is added, it should check if item count >= 5 and then remove the last item and add the new item to the top.
To do this, I made this test app:
public partial class Form1 : Form
{
LinkedList<string> list01 = new LinkedList<string>();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
list01.AddFirst("AAA");
list01.AddFirst("BBB");
list01.AddFirst("CCC");
listBox1.DataSource = new BindingSource(list01, "");
}
private void button1_Click(object sender, EventArgs e)
{
if (list01.Count >= 5)
list01.RemoveLast();
list01.AddFirst(DateTime.Now.ToString());
listBox1.DataSource = new BindingSource(list01, "");
}
}
It appears, when ever I add a new item, I have to keep setting the datasource to a new bindingsource for the added item to show on the U.I.
Is there way to initisalise one binding source, and when the items in it changes, auto update the listbox without having to set the datasource every time you add a new item?
You need a collection which implements collection changed notification. There are two options you have BindingList<T> and ObservableCollection<T>.
Pick any one, From your comment it seems you're just looking for AddFirst and RemoveLast. You can create a extension method yourself which does that.
public static class BindingListExtension
{
public static void AddFirst<T>(this BindingList<T> list, T item)
{
list.Insert(0, item);
}
public static void RemoveLast<T>(this BindingList<T> list)
{
list.RemoveAt(list.Count - 1);
}
}
Based on Sriram Sakthivel's suggestion, I have achieved my requirement like this:
public partial class Form1 : Form
{
BindingList<string> list01 = new BindingList<string>();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
listBox1.DataSource = list01;
list01.Add("AAA");
list01.Add("BBB");
list01.Add("CCC");
}
private void button1_Click(object sender, EventArgs e)
{
if (list01.Count >= 5)
list01.RemoveAt(4);
list01.Insert(0, DateTime.Now.ToString());
}
}
Related
I know that you can forcibly refresh the ListView using the method ListView.Refresh(). However, how do I forcibly stop the ListView from updating its list every time I insert an object (for design purposes)?
Code:
string[] newData = { //Some strings };
ListViewItem newRow = new ListViewItem(newData);
listView1.Items.Add(newRow);
The above will insert a new row into my listView1 and listView will automatically update its list and add that new item at the bottom-most row. I want to prevent the automatic adding of data into my visual list, I want to only update the data on the click of a button I've provided.
So what you can do is create a class that will manage your state for you:
public class ListViewStateHelper
{
private readonly ListView _listView;
private readonly List<string> _items;
public ListViewStateHelper(ListView listView)
{
_listView = listView;
_items = new List<string>();
}
public void AddItem(string value)
{
_items.Add(value);
}
public void DeleteItem(string value)
{
_items.Remove(value);
}
public void Refresh()
{
_listView.Items.AddRange(_items.Select(i => new ListViewItem(i)).ToArray());
}
}
then create a global variable and initialize it
private readonly ListViewStateHelper _stateHelper;
public Form1()
{
InitializeComponent();
_stateHelper = new ListViewStateHelper(listView1);
}
then have your event handlers call it:
private void add_Click(object sender, EventArgs e)
{
_stateHelper.AddItem("a");
}
private void delete_Click(object sender, EventArgs e)
{
_stateHelper.DeleteItem("a");
}
private void refresh_Click(object sender, EventArgs e)
{
_stateHelper.Refresh();
}
Or you can simply just create a global variable that will hold your datasource and work against it, then you don't need the state helper class.
I'm trying to fill a List<String> from another form while both forms are open.
In the form WorkOrder i have a list: List<String> materialsList, then in my other form AddMaterials i have other List<String> list, this is the list that i want to pass to the WorkOrder form, but both forms are open and i don't know it is possible to do that.
Here are my forms:
If i click the Add Materials button in the WorkOrder form then the form AddMaterials opens. Now with the form AddMaterials open, i fill one by one the elements of the list with the button Add new Material, then when i click finish i want to pass the List<String> list to the WorkOrder list: List<String> materialsList.
This is the code that i'm trying to solve this:
In the WorkOrder form:
List<String> materialsList = new List<string>();
public void setList(List<String> l)
{
this.materialsList = l;
}
In the AddMaterials form:
public partial class AddMaterials : Form
{
List<String> list = new List<string>();
public AddMaterials()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Adding Materials
list.Add(material.Text);
}
private void button2_Click(object sender, EventArgs e)
{
//Passing the list with Method setList()
WorkOrder o = new WorkOrder();
o.setList(list);
}
}
Any question post on comments.
You could pass a reference to either the WorkOrder Form or the WorkOrder.materialsList in the constructor of your AddMaterials Form.
So your AddMaterials code could be
public partial class AddMaterials : Form
{
WorkOrder wo;
public AddMaterials(WorkOrder wo)
{
InitializeComponent();
this.wo = wo;
}
private void button1_Click(object sender, EventArgs e)
{
//Adding Materials
this.wo.materialsList.Add(material.Text);
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
}
In WorkOrder you'd have this function which opens a new AddMaterials Form. You could place in logic here before you create a new AddMaterials Form to clear the list or do something else if you have multiple materials list for a WorkOrder.
private void button1_Click(object sender, EventArgs e)
{
// Add logic here to deal with multiple AddMaterial Forms
// on one work order
new AddMaterials(this).Show();
}
What you're doing is not working since you are using two completely different lists.
You can create a custom constructor for AddMaterials:
public AddMaterials(List<string> materials)
{
InitializeComponent();
this.materials = materials;
}
and declaring materials as a member of AssMaterials class:
private List<string> materials;
If you want to keep things even more separated you can pass a delegate:
public delegate void Del(string str);
class AddMaterials
{
private Del addMaterialDelegate;
public AddMaterials(Del AddMaterialDelegate) : Form
{
InitializeComponent();
this.addMaterialDelegate = AddMaterialDelegate;
}
private void button1_Click(object sender, EventArgs e)
{
//Adding Materials
addMaterialDelegate(material.Text);
}
}
and then from WorkOrder:
private void button1_Click(object sender, EventArgs e)
{
new AddMaterials(OnAddMaterialDelegate).Show();
}
private void OnAddMaterialDelegate(string material)
{
this.materialsList.Add(material);
}
This will allow you to change your mind in the future without too much rework (for example if you want to switch from a List to a different type).
I made a class so when the user selects item from listbox it uninstalls that item, except the problem is I can't access the list box. I tried public aswell, but in the code of form1.cs the only thing clostest to that list box is
keep in mind name of listbox is ProgramslistBox
Ok guys I re edited this post;
private void button1_Click(object sender, EventArgs e)
{
if(ProgramsListbox.SelectedIndex == -1)
{
MessageBox.Show("Please select an item to uninstall!");
}
else
{
ProgramsListbox_SelectedIndexChanged("",EventArgs.Empty);
}
}
this code is the FORM1.CS class, and I have another class called UninstallItem.cs is where I want my code to be, this below is my other class
namespace PC_TECH_Registery_Cleaner
{
class UninstallItem
{
public void uninstallSelectedItem()
{
Form1 c = new Form1();
}
}
}
And this below is still in my FORM1.CS class, I was experimenting with it :
public void ProgramsListbox_SelectedIndexChanged(object sender, EventArgs e)
{
//this will access the Uninstall item class so we can uninstall selected item.
UninstallItem c = new UninstallItem();
c.uninstallSelectedItem();
}
Within your Form1.cs create instance of UnIstallItem class and use it. Then on Button Click call "RemoveSelected" method of UnInstaItem class by passing programsListBox to it and it should remove the selected item.
public class Form1:Form
{
ListBox ProgramsListbox;
UninstallItem unistall;
public Form1(){
InitializeComponent();
uninstall = new UninstallItem();
button1.Click+= button1_Click;
}
void button1_Click(object sender, EventArgs e){
unistall.RemoveSelected(ProgramsListbox);
}
}
Then in your external class;
public class UninstallItem{
public UninstallItem{}
public void RemoveSelected(ListBox list)
{
if(list.SelectedIndex==-1)
{
MessageBox.Show("Please Select Item from List");
return;
}
list.Items.RemoveAt(list.SelectedIndex);
}
}
The 2 easy ways to think about this are either
Call the method in your class from the event handler in your form
Have a method on your class which matches the signature of an event handler, and subscribe to the event.
The first requires no major change
private MyClass myClass = new MyClass();
public void ProgramsListbox_SelectedIndexChanged(object sender, EventArgs e)
{
myClass.DoSomething();
}
The second requires your class to have a specific method that matches the signature of that event handler currently in your form
public class MyClass
{
public void DoSomething(object sender, EventArgs e)
{
var listBox = (ListBox)sender;
// read selected index perhaps, or selected item maybe
}
}
And then in your form
private MyClass myClass = new MyClass();
protected override void OnLoad(EventArgs e)
{
this.ProgramsListBox.SelectedIndexChanged += myClass.DoSomething;
}
Is there an event in Win Forms that can fire when the count of items in ListView change? I tried Size and Text - oddly enough they "sorta" worked but not always...
Im trying to trigger a label to update with the count of the listview items as it changes without manually doing that in a hundred methods.
If you are not using a bound datasource you can create a wrapper around the ListView Control and add a Method and an Event to fire off an event on adding an item to your ListView Collection.
Custom ListView
public class customListView : ListView
{
public event EventHandler<CustomEventArgs> UpdateListViewCounts;
public void UpdateList(string data)
{
// You may have to modify this depending on the
// Complexity of your Items
this.Items.Add(new ListViewItem(data));
CustomEventArgs e = new CustomEventArgs(Items.Count);
UpdateListViewCounts(this, e);
}
}
public class CustomEventArgs : EventArgs
{
private int _count;
public CustomEventArgs(int count)
{
_count = count;
}
public int Count
{
get { return _count; }
}
}
Example Usuage
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
customListView1.UpdateListViewCounts+=customListView1_UpdateListViewCounts;
}
private void customListView1_UpdateListViewCounts(object sender, CustomEventArgs e)
{
//You can check for the originating Listview if
//you have multiple ones and want to implement
//Multiple Labels
label1.Text = e.Count.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
customListView1.UpdateList("Hello");
}
}
Can someone tell me what i'm doing wrong here?
I've got a list in a database class that I want to view in a listbox on my form yet it's not showing anything.
I call the form from a button click of my first form which is where I enter the data and it works if I put a listbox on that form, but I'm wanting to open another form which will only show the data if that makes sense?
here's my code for the form that I want to view what's in the list:
public partial class Summary : Form
{
public Summary()
{
InitializeComponent();
}
private Database viewlist = new Database();
private void Summary_Load(object sender, EventArgs e)
{
}
private void sum()
{
List<String> listofPicks = viewlist.listPickups();
listBox1.Items.AddRange(listofPicks.ToArray());
}
private void button1_Click(object sender, EventArgs e)
{
sum();
}
}
Also may i make it clear that this code works if it's all done on the same form
Try this (kind of), load the list when the form loads, not on a button click:
public partial class Summary : Form
{
private Database _viewlist = new Database();
public Summary()
{
InitializeComponent();
}
private void Summary_Load(object sender, EventArgs e)
{
LoadList();
}
private void LoadList()
{
listBox1.Items.Clear();
listBox1.Items.AddRange(_viewlist.Pickups.ToArray());
}
}
public class Database
{
public List<string> Pickups
{
get { return new List<string> {"alfa", "beta"}; }
}
}