I have a ComboBox, which currently uses a simple class, containing Name and ID. Name is used as DisplayMember whereas ID is used as ValueMember. However, I would actually like to pass both the Name and the ID, when selecting an item, since this would spare me the operation of looking up the name later. Of course I could store those seperately, but that seems rendundat, since they come from the same place.
Hence arises my question: Is it possible to use the class (from which I get the Name and ID) as ValueMember for the ComboBox?
I was thinking something like this:
cboCategory.DataSource = viewModel.categoryOptions; // Type: BindingList<Equipment>
cboCategory.DisplayMember = "Name";
cboCategory.ValueMember = ??? // <--- This is where I run out of ideas
My Equipment class looks like this:
public class Equipment
{
private int id;
private string name;
public Equipment (int id, string name)
{
this.id = id;
this.name = name;
}
public int Id
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
}
You can access selected instance with SelectedItem property of combobox.
Only you need is cast to Eqipment type before using because SelectedItem is of type object.
var selectedEquipment = (Equipment)combobox.SelectedItem;
You can use data-binding as well to keep your viewmodel "loosely coupled"
cboCategory.DataSource = viewModel.categoryOptions;
cboCategory.DisplayMember = "Name";
cboCategory.ValueMember = "Id";
cboCategory.DataBinding.Add("SelectedItem", viewModel, "SelectedEquipment", true);
With data-binding viewmodel.SelectedEquipment property will be updated when you change selected item in combobox.
There's no way how you can achieve this with pure C# without adding third property where you combine Name and ID.
You can consider that 3rd property security like:
Is it enough to have only get?
Is it enough to have it protected?
etc.
When you're using XAML or WinForms, there's MultiBinding mechanism to achieve similar behavior. IMHO, multi-binding is in most cases overhead and it is more beneficial to create 3rd property.
So your class would look like:
public class Equipment
{
private int id;
private string name;
public Equipment (int id, string name)
{
this.id = id;
this.name = name;
}
public int Id
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
public string Identifier
{
get { return Id.ToString() + " " + Name; }
}
}
You can extent you ViewModel with INotifyPropertyChanged and notify about Identifier change when Name or ID changes.
More sophisticated (if needed) will be returning array of objects instead of string so you wont lose data at conversion (ID.ToString()) <- require more memory.
Related
I have the following C# class with a property Id which I would like to set with a GUID and return if the consumer calls the value of an instance of myClass.Id for which this value has not yet been set, but otherwise to keep and return the existing value.
public class IdentifiableClass{
public string Id {
get {
if (this.Id == null) {
this.Id = Guid.NewGuid().ToString();
Console.WriteLine("########## Id : " + this.Id );
}
return this.Id;
}
set => this.Id = value;
}
}
In C#, this does not work, but rather I get a stackoverflow (not this site, obviously).
Best guess, invoking this.Id within the same property's getter seems to resulting in circular logic.
In Salesforce Apex, with this similar code, it does work as I would expect it to, evaluating the value of this.Id as null, assigning the value to the new Guid, displaying the value, and then returning the value:
public class IdentifiableClass {
public string Id {
get {
if (this.Id == null) {
this.Id = String.valueOf(Integer.valueof((Math.random() * 10)));
System.debug('########## Id : ' + this.Id );
}
return this.Id;
}
set;
}
}
Is it possible to make this work in C#?
If so, how?
Probably you should create full property with private field.
public class IdentifiableClass{
private string id;
public string Id {
get {
if (this.id == null) {
this.id = Guid.NewGuid().ToString();
Console.WriteLine("########## Id : " + this.id );
}
return this.id;
}
set => this.id = value;
}
}
What you need to do is not to use auto-property feature.
You should put explicitly private string _id; field and your getters and setters should internaly use that
I have Class use as Table in SQL server
Then properties below
[Column(Storage = "_new_name", DbType = "nvarchar (2000)")]
public string new_name { get { return _new_name; } set { _new_name = value; } }
So. Can I get Length From my Class using C#
In this case It's 2000
Thank
You can't easily, without resorting to Reflection. Attributes are meta-data, so they only decorate code with additional information required for various processes. In your case, for your ORM to identify which property maps to which column.
Assuming you have a class like this:
public class TestTable
{
private string _new_name;
private string _address;
[Column(Storage = "_new_name", DbType = "nvarchar (2000)")]
public string new_name {
get
{
return _new_name;
}
set
{
_new_name = value;
}
}
[Column(Storage = "_address", DbType = "nvarchar (5000)")]
public string address {
get
{
return _address;
}
set
{
_address = value;
}
}
}
You can read the attribute values from the properties like this:
var properties = typeof(TestTable).GetProperties();
var attributesPerProperty = new Dictionary<string, string>();
foreach (var propertyInfo in properties)
{
var attribute = System.Attribute.GetCustomAttributes(propertyInfo).FirstOrDefault();
if(attribute is ColumnAttribute)
{
var columnAttribute = (ColumnAttribute)attribute;
attributesPerProperty.Add(propertyInfo.Name, columnAttribute.DbType);
}
}
It's not an ideal way of doing things, and I've just given a rough example but if you really, really need to read this kind of information from your classes, the above will get you there.
I have a WizardInfo class which as several TLists as properties, this then populates as the user goes through the wizard on the last screen I query the Tlists and make them into Lists and private fields
I then create Lists of DefaultItems from these lists. This is my own class and as name and Id as its property.
He is some code
public class DefaultItem
{
public int ID {get;set;}
public string Name {get;set;}
}
private List<DefaultItem> _defaultList = null;
_defaultList = new List<DefaultItem>();
defaultValue = PopulateDefaultList(_asmgps, defaultList);
private int PopulateDefaultList(
List<ASGMP> asmgps,
ref List<DefaultItem> defaultList)
{
int isdefault = -1;
foreach (ASGMP asgmp in asgmps)
{
if (asgmp.IsChecked)
{
if (asgmp.IsDefault)
{
isdefault = asgmp.ID;
}
DefaultItem defaultItem = new DefaultItem();
defaultItem.ID = asgmp.ID;
defaultItem.Name = GetMPTName(asgmp.ID);
defaultList.Add(defaultItem);
}
}
return isdefault;
}
private string GetMPTName(int ID)
{
try
{
SGMP sgmp = DataRepository.SGMPProvider.GetByASGMPID(ID)
if (serviceGroupMailPresentation != null)
{
MPT mpt DataRepository.MPTProvider.GetByMPTID(SGMP.MPTID);
if (mailPresentationType != null)
{
return mpt.Name;
}
}
return string.Empty;
}
catch (Exception ex)
{
WindowsEventLog.Write(ex);
throw;
}
}
The problem i am having is when i remove a item from the defaultList it affects asgmp.
I have found the answer. When I get the mpt name I get asgmp from the database this is where Codesmith does a strange thing and connects the usage of the List and the DefaultList. By querying the original List instead of going to the database it now works fine.
It is being removed because List<T> is derived from object, and is a Reference type. Reference types are passed by reference, i.e. when you pass your list, you are passing a pointer to its location in memory. So any changed you make on the copied reference, will also be reflected on the original object.
In order to make a copy you can change this like:
defaultValue = PopulateDefaultList(_asmgps, defaultList);
to this:
defaultValue = PopulateDefaultList(_asmgps.ToList(), defaultList);
This will enumerate the collection as IEnumerable<T> and return is as a list. This will effectivlly create a copy.
erm, instead of PopulateDefaultList why not just do,
var defaultList = asgmps
.Where(asgmp => asgmp.IsChecked)
.Select(asgmp => new
{
IsDefault = asgmp.IsDefault,
Item = new DefaultItem
{
ID = asgmp.ID,
Name = GetMPTName(asgmp.ID)
}
}).ToList();
of course, naming a collection defaultList that contains non-defaults seems counter intuitive.
I found out that this is because of ntiers instead of using the database the to get the ID I should of used the in List of T in
I have a List that contains a series of transaction objects. What I'm trying to do is to display these transaction objects in a Datagridview control on loading a form, basically the Datagridview should represent something of a transaction register to display the data for each of the transaction objects in the list.
I must admit to a lack of experience when it comes to using Datagridviews and I'm having some difficulty with understanding what I need to do here.
My question is, how do I go about getting the details of each of the objects in the list to display in the Datagridview?
Here is my code.
First the transaction class:
public class Transaction
{
// Class properties
private decimal amount;
private string type;
private decimal balance;
private string date;
private string transNum;
private string description;
// Constructor to create transaction object with values set.
public Transaction(decimal amount, string type, decimal currBal, string date, string num, string descrip)
{
this.amount = amount;
this.type = type;
this.balance = currBal;
this.date = date;
this.transNum = num;
this.description = descrip;
}
// Get and Set accessors to allow manipulation of values.
public decimal Amount
{
get
{
return amount;
}
set
{
amount = value;
}
}
public string Type
{
get
{
return type;
}
set
{
type = value;
}
}
public decimal Balance
{
get
{
return balance;
}
set
{
balance = value;
}
}
public string Date
{
get
{
return date;
}
set
{
date = value;
}
}
public string TransNum
{
get
{
return transNum;
}
set
{
transNum = value;
}
}
public string Description
{
get
{
return description;
}
set
{
description = value;
}
}
public decimal addCredit(decimal balance, decimal credit)
{
decimal newBalance;
newBalance = balance + credit;
return newBalance;
}
public decimal subtractDebit(decimal balance, decimal debit)
{
decimal newBalance;
newBalance = balance - debit;
return newBalance;
}
}
}
Now the code for the "Register" form:
public partial class Register : Form
{
List<Transaction> tranList = new List<Transaction>();
public Register(List<Transaction> List)
{
InitializeComponent();
this.tranList = List;
}
private void Register_Load(object sender, System.EventArgs e)
{
//regView represents the Datagridview that I'm trying to work with
regView.AutoSize = true;
regView.DataSource = tranList;
regView.Rows.Add(tranList[0]);
}
}
And here's the output I get.
There's really two high level approaches to this.
1) Add the manually created rows directly to the DataGridView. In this case, you have to manually update/remove them as things change. This approach is "ok" if you don't intend to alter/change the content of the display after you initialize it. It becomes untenable if you do.
To add it directly, you need to create a DataGridViewRow, and populate it with the individual values, and then add the DataGridViewRow to the DataGridView.Rows.
2) Data bind the DGV. There's many articles about databinding to a DataGridView. In some cases, it's easier to just add your data to a DataTable, and then extract a DataView from that, and bind the DataGridView to the DataView. Other people find it easier to directly bind to a collection.
CodeProject has a decent article to get you started down that path, but a quick Google search will yield many other articles.
http://www.codeproject.com/Articles/24656/A-Detailed-Data-Binding-Tutorial
use as DGV:
DataGridView groupListDataGridView;
column:
DataGridViewTextBoxColumn groupListNameColumn;
column setup should be like this:
groupListNameColumn.DataPropertyName = "name";
use this property, else all columns will be added.
groupListDataGridView.AutoGenerateColumns = false;
populate like this:
private void populateGroupList() {
groupListDataGridView.DataSource = null;
formattedGroupList = new SortableBindingList<DataGridGroupObject>();
foreach (GroupObject go in StartUp.GroupList) {
DataGridGroupObject dggo = new DataGridGroupObject();
dggo.id = go.Id;
dggo.name = go.Name;
formattedGroupList.Add(dggo);
}
groupListDataGridView.DataSource = formattedGroupList;
groupListDataGridView.Invalidate();
}
and model:
public class DataGridGroupObject
{
public int id { get; set; } //this will be match id column
public string name { get; set; } // this will be match name column
}
Simply add using System.Linq; at the top. Then you can do this:
//This will create a custom datasource for the DataGridView.
var transactionsDataSource = tranList.Select(x => new
{
Amount = x.amount,
Type = x.type,
Balance = x.balance,
Date = x.date,
TransNum = x.transNum
Description = x.description
}).ToList();
//This will assign the datasource. All the columns you listed will show up, and every row
//of data in the list will populate into the DataGridView.
regView.DataSource = transactionsDataSource;
In c# we can add a text and a value to each items of combobox, I want to know is it possible in Java also? if not please advice.
I Solved my problem by create a class like
public class ItemInfo {
public String Name;
public String Value;
public ItemInfo(String Name , String Value) {
this.Name = Name;
this.Value = Value;
}
public String toString() {
return Name ;
}
public String getValue() {
return Value ;
}
}
than I just create a new object from this class & pass it to my combobox
combbox1.addItem(new ItemInfo(item[0],item[1]));
job done :)!
You should learn how to use ComboBox in java.