Refreshing the listbox item after an element is changed - c#

I have a ParameterItem class for adding some items to a listbox:
class ParameterItem
{
public string Name { get; set; }
public string Value { get; set; }
public ParameterItem(string name, string value)
{
Name = name;
Value = value;
}
public override string ToString()
{
return Name + " = " + Value;
}
public override bool Equals(object obj)
{
if (obj is ParameterItem)
return (Name == ((ParameterItem)obj).Name);
return false;
}
public override int GetHashCode()
{
return Name.ToLowerInvariant().GetHashCode();
}
}
And you can add items to the listbox using two textboxes (name and value). When you click on an item in the listbox, the textboxes get filled with the name and the value of the ParameterItem. I have the following code to change the contents of the selected ParameterItem in the listbox:
private void btnSaveParameter_Click(object sender, EventArgs e)
{
ParameterItem currentParameter = new ParameterItem(textParameterName.Text,
textParameterValue.Text);
// If we already have the parameter set then edit it.
if (lstbxSetParameters.Items.Contains(currentParameter))
{
((ParameterItem)lstbxSetParameters.SelectedItem).Value = currentParameter.Value;
lstbxSetParameters.;
}
// If it's not set yet then add it to the listbox.
else
{
lstbxSetParameters.Items.Add(currentParameter);
textParameterName.Text = String.Empty;
textParameterValue.Text = String.Empty;
}
}
The problem is, even though I can change the contents of the selected ParameterItem, in the listbox, it still looks like it is not changed.
For example I have a parameter in the list box:
TestParameter = 10
And I change the ParameterItem to
TestParameter = 5
but in the listbox it still looks like
TestParameter = 10
even though it's been changed.
How can I solve this problem? I think the listbox item should call the ToString() method of the ParameterItem again and refresh itself but how?
Or is there a better way to add key value pairs in the listbox?

You can change the selected item by remove it and insert it again.
// If we already have the parameter set then edit it.
if (lstbxSetParameters.Items.Contains(currentParameter))
{
var newItem = new ParameterItem((lstbxSetParameters.SelectedItem as ParameterItem).Name, currentParameter.Value);
var index = lstbxSetParameters.SelectedIndex;
lstbxSetParameters.Items.RemoveAt(index);
lstbxSetParameters.Items.Insert(index, newItem);
lstbxSetParameters.SelectedIndex = index;
}

My solution:
string[] nList = new string[lb.Items.Count];
nList = lb.Items.OfType<string>().ToArray();
nList[lb.SelectedIndex] = newValue;
lb.Items.Clear();
lb.Items.AddRange(nList);
This way instead of changing the selected item (with a lot of problem) I reloaded the Listbox with the item changed in the array.

Related

How to Show text in combobox when no item selected like "..Select room..."

How can I show Text in ComboBox when no item selected in Windows Application using LINQ C#
Here is my code how I get all rooms.... in Combobox.
private void LoadRoom()
{
try
{
db = new HotelEntities();
// cmbProvince.Text = "";
var Room = (from u in db.Room
select new { u.RoomId, u.RoomNumber }).ToList();
cmbRoom.Text = ".. Select.."; // This one do not working.
cmbRoom.DisplayMember = "RoomNumber";
cmbRoom.ValueMember = "RoomId";
cmbRoom.DataSource = Room;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Thank you!
If you set the DataSource of a combobox, a currencymanager is used on the background and its position is selected (the first item).
Instead of setting DataSource, try adding the items:
cmbRoom.Items.AddRange(Room);
NB, setting Text as a placeholder will not work if an item is chosen and cleared later, unless you add an extra check (in TextChanged or SelectedIndexChanged)
Fast solution
Create a source class for your ComboItems, with (at least) the properties to Display and the inner value of the property. If you create a generic class you can use it for all your combo boxes.
class ComboDisplay<TSource>
{
public string Display {get; set;}
public TSource Value {get; set;}
}
cmbRoom.DisplayMember = nameof(ComboDisplay.Display);
cmbRoom.ValueMember = nameof(ComboDisplay.Value);
When you create the data source for your combobox, make sure you add a default value. In the example below I assume that you want to select items of type Room in your combo:
IEnumerable<Room> availableRooms = myDbContext.Rooms
.Where(room => room.IsAvailable)
.Select(room => new ComboDisplay<Room>
{
Display = room.Name,
Value = new Room
{
Id = room.Id,
...
},
})
// add a dummy value if nothing is selected
.Concat(new Room[]
{
Display = "Please select a room",
Value = null, // meaning: nothing selected
});
After selection, use comboBox1.SelectedValue to get the selected Room, or null if nothing is selected.
Create a Special Combobox class
If you have to use this regularly, consider creating a generic sub class of ComboBox that can display items of a certain TSource, and will return null if nothing is selected:
class MyComboBox<TSource> : ComboBox
{
public MyComboBox() : base()
{
base.DataSource = this.EmptyList;
base.DisplayMember = nameof(ComboDisplay.Display);
base.ValueMember = nameof(ComboDisplay.Value);
}
private static readonly EmptyItem = new ComboDisplay
{
Display = "Please select a value",
Value = null,
}
Make a property that returns the available combo items. Make sure that the EmptyItem is always in the collection:
public IReadonlyCollection<TSource> ComboItems
{
get {return (IReadOnlyCollection<TSource>)base.DataSource;}
set
{
// TODO: check if the empty element is in your list; if not add it
base.DataSource = value;
}
}
Finally: the function to get the Selected value, or null if nothing is selected:
public TSource SelectedValue
{
get => return (TSource)base.SelectedValue;
set
{
// TODO: check if value is in ComboItems
base.SelectedValue = value;
}
}

Can't programmatically set a ComboBox selection

I am unable to programmatically set the selection in a ComboBox.
I try setting various properties (SelectedItem, SelectedText, SelectedIndex) but the ComboBox does not display the Name. The first row of the ComboBox, which is blank, is selected. The return value of the setting of the Property is correct.
What am I doing wrong?
...
this.bsConstructionContractors.DataSource = typeof(Contractor);
...
public partial class EditContractorDialog : Form
{
public EditContractorDialog(Contractor contractor)
{
InitializeComponent();
this.cmbEditContractorName.DataBindings.Add(new System.Windows.Forms.Binding("SelectedValue", projectView.bsProject, "ContractorId", true));
// new BindingSource is important here, so as to use a separate CurrencyManager
// because bsConstructionContractors is shared with another ComboBox on another form
this.cmbEditContractorName.DataSource = new BindingSource(projectView.bsConstructionContractors, null);
this.cmbEditContractorName.ValueMember = "ContractorId";
this.cmbEditContractorName.DisplayMember = "Name";
cmbEditContractorName.Enabled = true;
cmbEditContractorName.Focus();
// None of the following cause the ComboBox to display Contractor.Name
// The first entry in the ComboBox, which is a blank, is displayed
object myObject = cmbEditContractorName.SelectedItem = contractor;
System.Diagnostics.Trace.WriteLine("EditContractorDialog(): myObject: " + myObject.GetType()); // type is Contractor
System.Diagnostics.Trace.WriteLine("EditContractorDialog(): myObject: " + myObject); // myObject is the contractor
object myObject2 = cmbEditContractorName.SelectedText = contractor.Name;
System.Diagnostics.Trace.WriteLine("EditContractorDialog(): myObject2: " + myObject2.GetType()); // type is String
System.Diagnostics.Trace.WriteLine("EditContractorDialog(): myObject2: " + myObject2); // myObject2 is the contractor.Name
object myObject3 = cmbEditContractorName.SelectedIndex = 3; // arbitrary index, just to see if it would be selected
System.Diagnostics.Trace.WriteLine("EditContractorDialog(): myObject3: " + myObject3.GetType()); // type is Int32
System.Diagnostics.Trace.WriteLine("EditContractorDialog(): myObject3: " + myObject3); // myObject3 = 3
}
}
public partial class Contractor : System.IComparable
{
protected int id;
protected string name;
protected string licenseNumber;
protected Project project;
public virtual int ContractorId
{
get { return this.id; }
set { this.id = value; }
}
public virtual string Name
{
get { return this.name; }
set { this.name = value; }
}
public virtual string LicenseNumber
{
get { return this.licenseNumber; }
set
{ this.licenseNumber = value; }
}
public virtual int CompareTo(object obj)
{
if (!(obj is Contractor))
{
throw new System.InvalidCastException("This object is not of type Contractor");
}
return this.Name.CompareTo(((Contractor)obj).Name);
}
}
Use SelectedIndex finding the index by contractor name:
cmbEditContractorName.SelectedIndex = cmbEditContractorName.FindString(contractor.Name);
That is because you need to get an object reference to assign from the DataSource of the combobox itself.
Also for comboboxes I suggest you to use an ObservableCollection<T>.
cmbEditContractorName.DataSource = new ObservableCollection<Type>(your List<Type>);
cmbEditContractorName.SelectedItem = ((ObservableCollection<Type>)cmbEditContractorName.DataSource).FirstOrDefault(c=> c.yourProperty = "something"); // this will select the first item meeting your condition
Your code isn't working because you are assigning SelectedItem property with a reference of an object not existing in the combobox DataSource.

C# WinForms DataGridView ComboBox databinding

I believe I have read every document on the internet regarding this issue and I'm still having trouble.
I have a DataGridView populated by two List and combined into a var
topList = ShippingOrder.GetTopGroups();
topItemsList = ShippingOrder.GetCurrentTopSheet();
topList.AddRange(topItemsList);
var newList = topList.OrderBy(x => x.GroupOrder).ToList();
One of the columns in the DdataGridView is a ComboBox. It's populated from a separate List
alertList = GetComboBoxValue();
It's values are associated with the datagridview combox.
emailRecipientDataGridViewComboBoxColumn.DataSource = alertList;
emailRecipientDataGridViewComboBoxColumn.DataPropertyName = "EmailAlert";
emailRecipientDataGridViewComboBoxColumn.DisplayMember = "PopUpText";
emailRecipientDataGridViewComboBoxColumn.ValueMember = "PopUpValue";
The DataPropertyName corresponds to topList.EmailAlert.
The DisplayMember corresponds to alertList.PopUpText.
The ValueMember corresponds to alertList.PopUpValue.
Then the datagrid is filled
dgvTopSheet.DataSource = newList;
CurrencyManager cm = (CurrencyManager)(dgvTopSheet.BindingContext[newList]);
cm.Refresh();
Once the datagrid is filled, everything looks good except the combobox value is not showing. I can use the dropdown arrow and the values acquired from the alertList show up but the comboxbox value isn't binding the value from topList on load.
As I understand from the 50 plus documents I've read on the datagridviewcombobox the DataPropertName is the field needed to bind the control to the data in the dgv. This doesn't appear to be working and 4 hours later I'm a bit frustrated. Something simple seems to be alluding me greatly.
Any help is appreciated.
eta -
The BusinessObjects Class
public class BusinessObjects : INotifyPropertyChanged
{
public BusinessObjects()
{
}
private string popUpText;
public string PopUpText
{
get { return popUpText; }
set
{
popUpText = value;
OnPropertyChanged(new PropertyChangedEventArgs("PopUpText"));
}
}
private string popUpValue;
public string PopUpValue
{
get { return popUpValue; }
set
{
popUpValue = value;
OnPropertyChanged(new PropertyChangedEventArgs("PopUpValue"));
}
}
}
The ShippingOrder Class
public class ShippingOrder : INotifyPropertyChanged
{
private string emailAlert;
public string EmailAlert
{
get { return emailAlert; }
set
{
emailAlert = value;
OnPropertyChanged(new PropertyChangedEventArgs("EmailAlert"));
}
}
private string partNumber;
public string PartNumber
{
get { return partNumber; }
set
{
partNumber = value;
OnPropertyChanged(new PropertyChangedEventArgs("PartNumber"));
}
}
}
sample data looks like:
ShippingOrder.PartNumber = "1234Dp01".
ShippingOrder.EmailAlert = "BSmith#example.com".
BusinessObject.PopUpText = "Bob Smith".
BusinessObject.PopUpValue = "BSmith#example.com".

How can I load data in combo item property when another changed, in propertygrid c#?

I have two property in my class: MyCountry & MyCity. I set this class to sourceobject of a property grid. I want load cities i combo when select a country. for example I have 2 Country data:
Country1
Country2
And For Country1, I have (city data)
City11
City12
City13
And For Country2, I have (city data)
city21
City22
City23
When I change select country item in propertygrid, I want load cities of it in city item. this mean, when select Country1, display City11,City12,City13 in City item and when select Country2 Display City21,Cityy22,City23 in City Item.
How can I It ?
my class is :
public class KeywordProperties
{
[TypeConverter(typeof(CountryLocationConvertor))]
public string MyCountry { get; set; }
[TypeConverter(typeof(CityLocationConvertor))]
public string MyCity { get; set; }
}
and I use below class for load countries data for display in combo :
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
HumanRoles Db = new HumanRoles();
List<LocationsFieldSet> Items = new List<LocationsFieldSet>();
Items = Db.LoadLocations(0);
string[] LocationItems = new string[Items.Count];
int count = 0;
foreach (LocationsFieldSet Item in Items)
{
LocationItems[count] = Item.Title;
count++;
}
return new StandardValuesCollection(LocationItems);
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
}
The ITypeDescriptorContext interface provides a property called Instance
which lets you access the object to which the type descriptor request is connected.
You can use this property to determine the current value of the MyCountry property
the user selected. Depending on the value you can load the cities for this country.
Furthermore, in the setter of the MyCountry property I check whether or not the
new value is different from the old one and if this is the case I reset the MyCity property
(to not get an invalid combination of country and city).
Here is a small code sample. For the sake of simplicity I only use one type converter
for both properties.
public class KeywordProperties
{
public KeywordProperties()
{
MyCountry = "Country1";
}
private string myCountry;
[TypeConverter(typeof(ObjectNameConverter))]
public string MyCountry
{
get { return myCountry; }
set
{
if (value != myCountry)
MyCity = "";
myCountry = value;
}
}
private string myCity;
[TypeConverter(typeof(ObjectNameConverter))]
public string MyCity
{
get { return myCity; }
set { myCity = value; }
}
}
public class ObjectNameConverter : StringConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
KeywordProperties myKeywordProps = context.Instance as KeywordProperties;
if (context.PropertyDescriptor.Name == "MyCountry")
{
List<string> listOfCountries = new List<string>();
listOfCountries.Add("Country1");
listOfCountries.Add("Country2");
return new StandardValuesCollection(listOfCountries);
}
List<string> listOfCities = new List<string>();
if (myKeywordProps.MyCountry == "Country1")
{
listOfCities.Add("City11");
listOfCities.Add("City12");
listOfCities.Add("City13");
}
else
{
listOfCities.Add("City21");
listOfCities.Add("City22");
listOfCities.Add("City23");
}
return new StandardValuesCollection(listOfCities);
}
}
In the example above there is one side effect I do not like.
Setting the MyCountry property leads to settting also the MyCity property.
To workaround this side effect you could also use the PropertyValueChanged event
of the PropertyGrid to handle invalid country/city selections.
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
if (e.ChangedItem.Label == "MyCountry")
{
if(e.ChangedItem.Value != e.OldValue)
m.MyCity = "";
}
}
If you use this event, just repalce the code in the setter of the MyCountry property with:
myCountry = value;

difficulty inserting a name to an inserted object of a checkedlistbox

I am abit new in C# and i am trying to insert an object to a CheckedListBox,
so this inserted item will have a title inside the checked list (my object contains a string field inside it which I want to be displayed in the CheckedListBox).
for example this is my class:
public class InfoLayer
{
private string LayerName;
private List<GeoInfo> GeoInfos;
public InfoLayer()
{
LayerName = "New Empty Layer";
GeoInfos = new List<GeoInfo>();
}
public InfoLayer(string LayerName)
{
this.LayerName = LayerName;
GeoInfos = new List<GeoInfo>();
}
public InfoLayer(string LayerName,List<GeoInfo> GeoInfosToClone):this(LayerName)
{
foreach (GeoInfo item in GeoInfosToClone)
{
GeoInfos.Add((GeoInfo)((ICloneable)item).Clone());
}
}
public GeoInfo SearchElement(long id)
{
foreach (GeoInfo info in GeoInfos) // foreach loop running on the list
{
if (info.INFOID == id)
return info; // return the item if we found it
}
return null;
}
public GeoInfo SearchElement(string name)
{
foreach (GeoInfo info in GeoInfos)
{
if (info.INFONAME.CompareTo(name)==0)
return info;
}
return null;
}
public override string ToString()
{
string toReturn = "";
for (int i = 0; i < GeoInfos.Count; i++) // for loop running on the list
{
toReturn += String.Format("{0}\n",GeoInfos[i].ToString()); // piping another geoinfo
}
return toReturn;
}
public string LAYERNAME{get{return LayerName;}}
my class also contains a tostring overrider inside her (not what i want to display)
thanks in advance for your help.
Override ToString() in your class, the class that the object is an instance of.
Edit:
You don't want to display the contents of ToString(). You want to display the LayerName, don't you? Perhaps you should display the values with Databinding instead. Then you can set DisplayMember to your new LAYERNAME property.
I believe this is what you are trying to achieve:
checkedListBox1.Items.Add(yourObject.stringField);
((MyObjectType)checkedListBox1.Items(index)).Name = "whatever"
You will have to know the index of the object you want to change.
You'll just have to override the ToString method in your class so that it returns this Name property value.
public overrides string ToString() {
return Name;
}
It will then display its name when added to your CheckedListbox.

Categories