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;
}
}
Related
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...
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 need to create a user control MyTypeListControl to display collection of objects of type MyType using a user controls MyTypeDisplayControl instance for each of those objects.
So that I could
add instance of MyTypeListControl to my WinForm, then
load collection of MyType and
assign it to MyTypeListControl's DataSource.
In the result it should generate and show appropriate count of MyTypeDisplayControl instances in MyTypeListControl's instance.
In case if I needed to show list of properties - equivalent would be DataGrid with specific fields from MyType assigned to specific DataGrid's columns, but I want to view each MyType item as a user control - with more power for visual representation and functionality than DataGrid provides for it's rows.
Is that even possible?
I found this SO resource how to create My collection type, but this is only small part of the problem solution...
It is quite easy (if you know how) and doesn't take so much effort as you might think in the first place (at least for a simple implementation that handles collection of less then 100 items).
So at first lets create a MyType:
public class MyType
{
public static MyType Empty = new MyType(String.Empty, DateTime.MinValue);
public MyType(string myName, DateTime myBirthday)
{
MyName = myName;
MyBirthday = myBirthday;
}
public DateTime MyBirthday { get; private set; }
public string MyName { get; private set; }
}
At next we need a MyTypeControl:
public partial class MyTypeControl : UserControl
{
private MyType _MyType;
private Label labelBirthday;
private Label labelName;
private Label labelSeparator;
public MyTypeControl()
{
InitializeComponent();
}
public event EventHandler MyTypeChanged;
public MyType MyType
{
get { return _MyType; }
set
{
if (_MyType == value)
return;
_MyType = value ?? MyType.Empty;
OnMyTypeChanged(EventArgs.Empty);
}
}
protected virtual void OnMyTypeChanged(EventArgs eventArgs)
{
UpdateVisualization();
RaiseEvent(MyTypeChanged, eventArgs);
}
protected void UpdateVisualization()
{
SuspendLayout();
labelName.Text = _MyType.MyName;
labelBirthday.Text = _MyType.MyBirthday.ToString("F");
labelBirthday.Visible = _MyType.MyBirthday != DateTime.MinValue;
ResumeLayout();
}
private void InitializeComponent()
{
labelName = new Label();
labelBirthday = new Label();
labelSeparator = new Label();
SuspendLayout();
labelName.Dock = DockStyle.Top;
labelName.Location = new Point(0, 0);
labelName.TextAlign = ContentAlignment.MiddleCenter;
labelBirthday.Dock = DockStyle.Top;
labelBirthday.TextAlign = ContentAlignment.MiddleCenter;
labelSeparator.BorderStyle = BorderStyle.Fixed3D;
labelSeparator.Dock = DockStyle.Top;
labelSeparator.Size = new Size(150, 2);
Controls.Add(labelSeparator);
Controls.Add(labelBirthday);
Controls.Add(labelName);
MinimumSize = new Size(0, 48);
Name = "MyTypeControl";
Size = new Size(150, 48);
ResumeLayout(false);
}
private void RaiseEvent(EventHandler eventHandler, EventArgs eventArgs)
{
var temp = eventHandler;
if (temp != null)
temp(this, eventArgs);
}
}
Then comes our magically list control:
public class MyTypeListControl : UserControl
{
private ObservableCollection<MyType> _Items;
public MyTypeListControl()
{
AutoScroll = true;
_Items = new ObservableCollection<MyType>();
_Items.CollectionChanged += OnItemsCollectionChanged;
}
public Collection<MyType> Items
{
get { return _Items; }
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateVisualization();
}
private void UpdateVisualization()
{
SuspendLayout();
Controls.Clear();
foreach (var item in _Items)
{
var control = new MyTypeControl { MyType = item, Dock = DockStyle.Top };
Controls.Add(control);
Controls.SetChildIndex(control, 0);
}
ResumeLayout();
}
}
And now simply create the list control in your form or parent control and fill it with some meaningful values:
myTypeListControl.Items.Add(new MyType("Adam", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 40))));
myTypeListControl.Items.Add(new MyType("Eva", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 38))));
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).
I am trying to add an object ITEM with TEXT and VALUE to a ComboBox so I can read it later
public partial class Form1 : Form
{
ComboboxItem item;
public Form1()
{
InitializeComponent();
comboBox1.Items.Add(new ComboboxItem("Dormir", 12).Text);
}
private void button1_Click(object sender, EventArgs e)
{
ComboboxItem c = (ComboboxItem)comboBox1.SelectedItem;
label1.Text = c.Text;
label2.Text = c.Value.ToString();
}
}
The problem is, I cant add the full Item because isn't a string...and give an exception at beginning of click event
Extra information:
This ComboboxItem, its a class that I created with 2 parameters, string, and int
public class ComboboxItem
{
public string Text { get; set; }
public object Value { get; set; }
public ComboboxItem(string texto, double valor)
{
this.Text = texto;
this.Value = valor;
}
}
You could (should) set the displaymember and valuemember in another place, but...
public Form1()
{
InitializeComponent();
comboBox1.DisplayMember="Text";
comboBox1.ValueMember ="Value";
comboBox1.Items.Add(new ComboboxItem("Dormir", 12));
}
Create the ComboboxItem class and override the ToString method.
The ToString method will be called to visualize the item. By default, ToString() returns the typename.
public class ComboboxItem
{
public object Value{get;set;}
public string Text {get;set;}
public override string ToString(){ return Text; }
}
Then, you can do this:
var item = new CombobxItem { Value = 123, Text = "Some text" };
combobox1.Items.Add(item);
you dont need to add .Text at the end of ("text","value")
so add it as :
comboBox1.Items.Add(new ComboBoxItem("dormir","12"));
You are on the right lines by creating your own ComboBoxItem class.
public class ComboboxItem
{
public string Text { get; set; }
public object Value { get; set; }
}
There are two ways to use this, (Constructor aside):
Method 1:
private void button1_Click(object sender, EventArgs e)
{
ComboboxItem item = new ComboboxItem();
item.Text = "Item text1";
item.Value = 12;
comboBox1.Items.Add(item);
}
Method 2:
private void button1_Click(object sender, EventArgs e)
{
ComboboxItem item = new ComboboxItem
{
Text = "Item text1",
Value = 12
};
comboBox1.Items.Add(item);
}
Have you tried adding it like this instead? Then when ever you get the Item out just cast it as a ComboboxItem :)
...
var selectedItem = comboBox1.SelectedItem as ComboboxItem;
var myValue = selectedItem.Value;
...
Alternative KeyValuePair:
comboBox1.Items.Add(new KeyValuePair("Item1", "Item1 Value"));
Question based on returnign string value and other combobox answers..
ComboBox: Adding Text and Value to an Item (no Binding Source)