I have a method to populate a combobox with some strings. At the end of the method I assign to the SelectedIndexChanged event. Here's how that method looks
public ComboBox PopulateComboBox()
{
Worksheet sheetWithTemplateNames = _iReader.GetWorksheetByName("Templates");
int lastRowOfTemplates = _iReader.GetLastRow(sheetWithTemplateNames);
var templateNames = _iHandler.GetTemplateNames(sheetWithTemplateNames, lastRowOfTemplates);
foreach (var template in templateNames)
{
Box.Items.Add(template);
}
Box.SelectedIndexChanged += Box_SelectedIndexChanged;
return Box;
}
and it works as I want to. My problem is that I need to use this templateNames list in the actual event and that's causing trouble. Here's how my event looks like now but ain't functioning.
private void Box_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox cmb = (ComboBox)sender;
var chosenObject = cmb.SelectedIndex;
MessageBox.Show(templateNames[chosenObject]);
}
but my list is now empty. It's instantiated in the constructor so I'd assume it'd keep it's state but that isn't the situation. Here's the top of the class
public class TemplateListCombobox
{
public ComboBox Box { get; set; }
private IDataReader _iReader;
private IDataHandler _iHandler;
private List<string> templateNames;
public TemplateListCombobox()
{
Box = new ComboBox();
_iReader = new DataReader();
_iHandler = new DataHandler();
templateNames = new List<string>();
}
}
so how could I possibly keep the state of my list through the event?
UPDATE:
MY class that calls this:
public static class GroupBoxHolder
{
private static GroupBox _thisGroupBox;
public static GroupBox GetGroupBox()
{
PopulateGroupBox();
return _thisGroupBox;
}
public static void PopulateGroupBox()
{
_thisGroupBox = new GroupBox();
TemplateListCombobox combo = new TemplateListCombobox();
ComboBox box = combo.GetComboBox();
_thisGroupBox.Controls.Add(box);
ConfigureGroupBox();
}
public static void ConfigureGroupBox()
{
_thisGroupBox.Location = new Point { X = 75, Y = 15 };
_thisGroupBox.Height = 150;
_thisGroupBox.Width = 400;
}
}
and my updated class
public class TemplateListCombobox
{
private ComboBox _box;
private readonly IDataReader _iReader;
private readonly IDataHandler _iHandler;
private readonly Worksheet _sheetWithTemplateNames;
public TemplateListCombobox()
{
_box = new ComboBox();
_iReader = new DataReader();
_iHandler = new DataHandler();
_sheetWithTemplateNames = _iReader.GetWorksheetByName("Templates");
PopulateComboBox();
}
public void PopulateComboBox()
{
int lastRowOfTemplates = _iReader.GetLastRow(_sheetWithTemplateNames);
var templateNames = _iHandler.GetTemplateNames(_sheetWithTemplateNames, lastRowOfTemplates);
foreach (var template in templateNames)
{
_box.Items.Add(template);
}
_box.SelectedIndexChanged += Box_SelectedIndexChanged;
}
public ComboBox GetComboBox()
{
return _box;
}
private void Box_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox cmb = (ComboBox)sender;
var chosenObject = cmb.SelectedItem.ToString();
var firstRowForTemplate = _iReader.GetFirstRowForTemplate(_sheetWithTemplateNames, chosenObject.ToString());
var attributes = _iReader.GetTemplateAttributes(_sheetWithTemplateNames, chosenObject, firstRowForTemplate);
}
}
A. Two lists?
var templateNames = creates a new local variable.
Do you want to set a member field instead? If so, remove var.
B. Read from comobox
Since the combo box contains the names read directly from it.
The flow of the code is something like the following:
var templateNames = _iHandler.GetTemplateNames(sheetWithTemplateNames, lastRowOfTemplates);
foreach (var template in templateNames)
{
Box.Items.Add(template);
}
... time passes
var chosenObject = cmb.SelectedIndex;
MessageBox.Show(templateNames[chosenObject]);
The combobox contains all the information you need. (You could use SelectedItem).
Note on two sources.
Since you're not clearing the combobox's items in the populate method, if it was called twice the combobox would gain new elements. That's not great but more importantly templateNames[chosenObject] would not work anymore because the combobox and the list would be out of sync.
In addition to the var templateNames mistake that doesn't cause the problem, you don't call PopulateComboBox in the code you provide, so the list is empty...
I don't understand why this method returns the Box since there is no parameter and it is a member field that you access directly in the code.
You only need to assign the event in the constructor, not each time you call the populate method that you need to call somewhere.
You should improve your class design because there is several clumsiness. For example, what is TemplateListCombobox without parent and how do you intend to use it? How are initialized _iReader and _iHandler? Do you need to keep templateNames? And so on...
Related
I have a ComboBox and an ObservableCollection set as DataSource for that ComboBox.
When I programmatically add/remove items from the observable collection, nothing changes in the ComboBox.
What am I doing wrong?
Part 2: tried to put a BindingSource as a proxy for ObservableCollection. When programmatically added/removed items from ObservableCollection, no event like ListChanged or similar fired.
How can I make a ComboBox automatically update its list when underlying collection changes?
public Form1()
{
InitializeComponent();
comboBox1.DataSource = new ObservableCollection<MyItem>(
new []
{
new MyItem() { Name = "AAA"},
new MyItem() { Name = "BBB"},
});
}
private void Button3_Click(object sender, EventArgs e)
{
// Nothing changes in the ComboBox when I add a new item to ObservableCollection
((ObservableCollection<MyItem>)(comboBox1.DataSource))
.Add(new MyItem() { Name = Guid.NewGuid().ToString()});
}
}
public class MyItem
{
public string Name { get; set; }
}
It helps to wrap a list in a BindingList<T>. Here a little test code:
public partial class Form1 : Form
{
private readonly List<string> _coll = new List<string> { "aaaaa", "bbbbb", "ccccc" };
private readonly BindingList<string> _blist;
private readonly Random _rand = new Random();
private const string Templ = "mcvnoqei4yutladfffvtymoiaro875b247ytmlarkfhsdmptiuo58y1toye";
public Form1()
{
InitializeComponent();
_blist = new BindingList<string>(_coll);
comboBox1.DataSource = _blist;
}
private void AddButton_Click(object sender, EventArgs e)
{
int i = _rand.Next(Templ.Length - 5);
string s = Templ.Substring(i, 5);
_blist.Add(s);
}
}
Note that you have to make the changes (Add, Remove etc.) to the BindingList. The BindingSource works the same way.
I'm working on a small form app, and I have "paired" my radio buttons with lists in a common class. The purpose of this was to turn on/off the corresponding list
public class myType
{
public RadioButton button { get; set; }
public ListBox list { get; set; }
}
I proceed to create these pairs through a for loop inside an array
for (int i = 0; i < broj_botuna; i++)
{
theArray[i] = new myType();
}
I use a common event handler for all the radio buttons:
private void test_CheckedChanged(object sender, EventArgs e)
{
var xx = sender as RadioButton;
//do stuff
positionInArray = Array.IndexOf(theArray, xx);
}
except that the last line of code "xx" should be of type "myType" and not "radioButton" that I managed to retrieve.
So could anyone tell me how do I get the reference from "radioButton" to "myType"? Or is there a better alternative?
You can use Array.FindIndex like:
var positionInArray = Array.FindIndex(theArray, b => b.button == xx);
You could create some constructs that allow you to easily associate your properties to the parent object if you wanted to.
This approach would allow you to always reference your parent type provided that you added a bit more code in your get's and set's.
static void Main()
{
myType item = new myType();
var button = new Button();
myType.button = button;
var list = new ListBox();
myType.list = list;
item = list.GetParent();
bool isSameButton = button == item.button;
bool isSameList = list == item.list;
Assert.IsTrue(isSameButton);
Assert.IsTrue(isSameList);
}
public class myType
{
private RadioButton _button;
public RadioButton button
{
get { return _button; }
set {
value.AssociateParent(this);
_button = value;
}
}
private ListBox _list;
public ListBox list
{
get { return _list; }
set {
value.AssociateParent(this);
_list= value;
}
}
}
public static class Extensions
{
private static Dictionary<object, object> Items { get; set; }
static Extensions()
{
Items = new Dictionary<object, object>();
}
public static void AssociateParent(this object child, object parent)
{
Items[child] = parent;
}
public static object GetParent(this object child)
{
if (Items.ContainsKey(child)) return Items[child];
return null;
}
}
I have bound to my combobox this simple class:
public class Company
{
public Guid CorporationId { set; get; }
public Guid TokenId { set; get; }
public string Name { set; get; }
}
And this is my binding:
private void FillCompaniesComboBox()
{
_doneLoadingComboBox = false;
comboBox_Companies.Items.Clear();
if (CurrentSettings.AllCompanies.Count == 0)
{
return;
}
bindingSource1.DataSource = CurrentSettings.AllCompanies;
comboBox_Companies.DataSource = bindingSource1.DataSource;
comboBox_Companies.DisplayMember = "Name";
comboBox_Companies.ValueMember = "CorporationId";
comboBox_Companies.SelectedIndex = 1;
_doneLoadingComboBox = true;
}
When I attempt to get the value of the selected item, I'm getting different results. Here is the code I am using to get my value:
private void comboBox_Companies_SelectedIndexChanged(object sender, EventArgs e)
{
if (!_doneLoadingComboBox && comboBox_Companies.SelectedIndex == -1)
{
return;
}
var value = (Company)comboBox_Companies.SelectedValue;
Console.WriteLine("Value: " + value.CorporationId);
}
Here is what is happening:
This one works at intended:
And this is were it is causing an issue:
Am I not retrieving the data correctly? I need the Company information that it is bound to.
Okay so here's what you need to do...
Assuming that your CurrentSettings.AllCompanies is an IList<Company> that you've already populated with data, here's what your code should look like:
public class ComboBoxItem {
// your class
private Company Comp;
}
private readonly BindingSource _bsSelectedCompany = new BindingSource();
private readonly ComboBoxItem _comboBoxItem = new ComboBoxItem();
// your main form method
public MainForm() {
// initialization code...
InitializeComponent();
// prevents errors in case your data binding objects are empty
ResetComboBox(comboBox1);
comboBox1.DataBindings.Add(new Binding(
"SelectedItem",
_bsSelectedCompany,
"Comp",
false,
DataSourceUpdateMode.OnPropertyChanged
));
comboBox1.DataSource = CurrentSettings.AllCompanies;
comboBox1.DisplayMember = "Name";
}
// simple method for resetting a given combo box to a default state
private static void ResetComboBox(ComboBox comboBox) {
comboBox.Items.Clear();
comboBox.Items.Add("Select a method...");
comboBox.SelectedItem = comboBox.Items[0];
}
By doing this, you're able to just use _comboBoxItem to safely get the information about your selected item without having to potentially Invoke it (in the case of accessing it on a separate thread).
Been looking for a clear example of this.
I made a new object including setting several properties, added the whole object to the listBox then wrote a string to describe them. Now I want one item from the lsitBox object at the selected index. There are many syntaxes that appear to have similar but different usages it is complicating the search...
Pseudocode:
SpecialClass object = new SpecialClass;
object.propertyA;
Object.PropertyB;
listBox.Items.Add(object);
//listBox.SelectedItem[get propertyA]? What would retrieve propertyA or propertyB from the //list after putting the object in the list?
.... I tried to use this variable setting, something like this...
MRecipeForm parent = new MRecipeForm();
ListViewItem item = new ListViewItem();
item.Tag = parent.recipeListB.Items;
var myObject = (double)parent.recipeListB.SelectedItems[0].Tag;
// here you can access your properties myObject.propertA etc...
....
This is my current code that throws an exception:
MRecipeForm parent = new MRecipeForm();
ListViewItem item = new ListViewItem();
item.Tag = parent.recipeListB.Items;
Substrate o = ((ListBox)sender).SelectedItem as Substrate;
double dryWtLbs = o.BatchDryWtLbs; //BatchDryWtLbs is type double
Just store your object into your item's Tag property. When you adding your item:
ListViewItem item = new ListViewItem();
item.Tag = myObject;
...
Then:
var myObject = (SpecialClass)listBox.SelectedItems[0].Tag;
// here you can access your properties myObject.propertA etc...
Example to retrieve a double after changing selected index :
Forms 1 has a label : label1 and a listbox : listBox1
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var object1 = new SpecialClass { Text = "First line", Number = 1d };
var object2 = new SpecialClass { Text = "Second line", Number = 2d };
listBox1.Items.Add(object1);
listBox1.Items.Add(object2);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
SpecialClass o = ((ListBox)sender).SelectedItem as SpecialClass;
label1.Text = o.Number.ToString();
}
}
public class SpecialClass
{
public string Text { get; set; }
public double Number { get; set; }
public override string ToString()
{
return Text;
}
}
I have a listbox full of items for my order.
I want to take all of the items inside my listbox and transfer them into my listview.
Then I want to take my listview and display it in another form (my messagebox).
My new listview:
private void CustomerInfo_Click(object sender, EventArgs e)
{
ListViewItem customers = new ListViewItem(fullName.Text);
customers.SubItems.Add(totalcount.ToString());
customers.SubItems.Add(total.ToString());
customers.SubItems.Add(Address.Text);
customers.SubItems.Add(telephone.Text);
for (int i = 0; i < OrderlistBox.Items.Count; i++)
{
customers.SubItems.Add(OrderlistBox.Items[i].ToString());
}
Customers.Items.Add(customers);
//CLEAR ALL FIELDS
OrderlistBox.Items.Clear();
fullName.Text = "";
Address.Text = "";
telephone.Text = "";
totalDue.Text = "";
totalItems.Text = "";
}
My contextMenuStrip, so when I click on the customer I can get its info (name, address, order, etc.):
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm
{
name = Customers.SelectedItems[0].SubItems[0].Text,
address = Customers.SelectedItems[0].SubItems[3].Text,
telephone = Customers.SelectedItems[0].SubItems[4].Text,
};
myformmessagedialog.ShowDialog();
}
}
My new form, the messagebox where I will display all the info for the client:
public partial class MessageBoxForm : Form
{
public MessageBoxForm()
{
InitializeComponent();
}
public string name;
public string address;
public string telephone;
public ListViewItem order = new ListViewItem();
private void MessageBoxForm_Load(object sender, EventArgs e)
{
lblName.Text = name;
lbladdress.Text = address;
lbltelephone.Text = telephone;
orderListView.Items.Add(order);
}
}
I'm sorry if this seems confusing but I'm just looking for help to go in the right direction. Any help is appreciated.
One way to do this is to put the data that you want to display in some sort of ViewModel, basically a class or set of classes that has the data that you want to display. Then the main form can display it, and you can pass a reference to that ViewModel to the message box and it can display it as well.
In general you want to avoid any kind of code that directly ties controls from different forms together.
The easiest way based on your current setup is to simply pass your list view data across to your MessageBoxForm e.g.
public partial class MessageBoxForm : Form
{
...
public void LoadListView(ListViewItemCollection items)
{
orderListView.Clear();
orderListView.AddRange(items);
}
}
....
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm
{
name = Customers.SelectedItems[0].SubItems[0].Text,
address = Customers.SelectedItems[0].SubItems[3].Text,
telephone = Customers.SelectedItems[0].SubItems[4].Text,
};
myformmessagedialog.LoadListView(Customers.Items);
myformmessagedialog.ShowDialog();
}
}
Basic answer is you don't.
You maintain a collection of items (whatever they are).
You display them in a list box.
You display them in a list view.
If you want say select some from the list box and only move them to the list view.
Then you use the listbox selection to find them in your collections of items, create a list of selected ones then passs that to the form with the listview to display.
Don't use UI controls to store your data and try really hard to never make one form's UI directly dependant on another.
I'm guessing what you'd need (and I could have misunderstood what you are looking for) is a new method in you MessageBoxForm to pass in your Customers object:
private void customerInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (Customers.SelectedItems.Count != 0)
{
var myformmessagedialog = new MessageBoxForm;
myformmessagedialog.Customers = Customers;
if (myformmessagedialog.ShowDialog() == DialogResult.OK)
{
Customers = myformmessagedialog.Customers;
}
}
}
If so, simply modify your class to be something like this:
public partial class MessageBoxForm : Form
{
public MessageBoxForm()
{
InitializeComponent();
}
private void MessageBoxForm_Load(object sender, EventArgs e)
{
if (Customers != null)
{
// add your code here to add your Customers as needed
}
}
public Customers Customers { get; set; }
}
To access anything from the parent form you need to pass it to the child form so
myformmessagedialog.ShowDialog();
becomes
myformmessagedialog dialog = new myformmessagedialg(this);
dialog.ShowDialog();
and your class constructor becomes this:
public MessageBoxForm(myformmessagedialog parent){
name=parent.fullName.Text;
address=parent.address.Text;
...etc...
InitializeComponent();
}
Though it might be better to just pass in the name, address, etc rather than the whole form, this way is nice for while you are changing things because you have one less place to change to add another variable to pass.