I display string array as listbox in property grid with type converter with code as follow.
How to select index of listbox for initialize first time program loaded? I want SelectedIndex = 0, show first arrData = "one". Any Idea?
public partial class Form1 : Form
{
string[] arrData = new string[] { "one", "two", "three" };
PropertyGrid pGrid = new PropertyGrid();
public Form1()
{
InitializeComponent();
FirstClass fClass = new FirstClass(arrData);
pGrid.SelectedObject = fClass;
pGrid.Dock = DockStyle.Fill;
this.Controls.Add(pGrid);
}
public class FirstClass
{
public FirstClass(string[] arrData)
{
this.DataSource = arrData;
}
[Browsable(true)]
[DefaultValue("one")]
[TypeConverter(typeof(SecondClass))]
public string Counter { get; set; }
[Browsable(false)]
public string[] DataSource { get; set; }
}
public class SecondClass : StringConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
FirstClass fClass = (FirstClass)context.Instance;
if (fClass == null || fClass.DataSource == null)
{
return new StandardValuesCollection(new string[0]);
}
else
{
return new StandardValuesCollection(fClass.DataSource);
}
}
}
}
If I understood you right it should be as easy as this code below if you just want to set the index of the selected item in an listbox to 0.
ListBox.SelectedIndex = 0;
For further details, please check out the MSDN page.
http://msdn.microsoft.com/en-us/library/system.windows.forms.listbox.selectedindex.aspx
Related
I have a binding source creditUserBindingSource in my below class which recieves a list of CreditUser class as datasource. In my DataGridView I have a DataGridViewComboBoxColumn called ResponsibleList which receives a list of string as DataSource ["Production", "Distribution", "Customer Service", "Sales"]. I want the ResponsibleList to display the responsible from the responsible variable from the list of CrteditUsers for each user.
public partial class CreditUserLimitsForm : Form
{
private List<CreditUser> creditUser;
private bool SetupCheckStatus = false;
//private Dictionary<string, string> fbu;
public CreditUserLimitsForm()
{
InitializeComponent();
}
private void CreditUserLimitsForm_Load(object sender, EventArgs e)
{
//fbu = MainForm.srv.GetFBU();
//foreach (KeyValuePair<string, string> k in fbu)
//{
// lvwFBU.Items.Add(new ListViewItem(new string[] { k.Key, k.Value }));
//}
try
{
creditUser = MainForm.srv.GetCreditUser("","").ToList();
creditUserBindingSource.DataSource = creditUser;
SetAlternateChoicesUsingDataSource(ResponsibleList);
}
catch (Exception ex)
{
Cursor = Cursors.Default;
NutraMsg.DisplayError(this, ex, MainForm.GetMessageDisplayType());
}
}
private void SetAlternateChoicesUsingDataSource(DataGridViewComboBoxColumn comboboxColumn)
{
{
comboboxColumn.DataSource = MainForm.srv.GetResponsibleList();
comboboxColumn.ValueMember = "Responsible";
comboboxColumn.DisplayMember = comboboxColumn.ValueMember;
}
}
}
Here's the code for CreditUser class
public class CreditUser : INotifyPropertyChanged
{
public string Responsible { get; set; }
public int UserId { get; set; }
public int RoutingId { get; set; }
public string UserName { get; set; }
public List<string> AllowedCustomerTypes { get; set; }
public decimal CreditLimit { get; set; }
public bool Restricted
{
get
{
foreach (UserCatalog uc in Catalogs)
{
if (uc.Restricted)
{
return true;
}
}
return false;
}
}
}
If you're binding a list of string values then don't set the DisplayMember or ValueMember. The point of those is to specify members of the items you want to use but you don't want to use members of the items. You want to use the items themselves. Here is a simple example that demonstrates this:
private class Record
{
public int Id { get; set; }
public string Name { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
var idColumn = new DataGridViewTextBoxColumn { HeaderText = "Id", DataPropertyName = "Id" };
var nameColumn = new DataGridViewComboBoxColumn
{
HeaderText = "Name",
DataPropertyName = "Name",
DataSource = new[] {"First", "Second", "Third"}
};
dataGridView1.Columns.AddRange(idColumn, nameColumn);
dataGridView1.DataSource = new BindingList<Record>
{
new() {Id = 1, Name = "First"},
new() {Id = 2, Name = "Second"},
new() {Id = 3, Name = "Third"}
};
}
I see that you have made a custom DataGridComboBoxColumn and have implemented a version of SetAlternateChoicesUsingDataSource that seems to be modeled after the method of the same name in the Microsoft code example for DataGridViewComboBoxColumn.
The purpose of SetAlternateChoicesUsingDataSource in that example is to provide ComboBox drop down options that are tailored and specified for each user using the AllowedCustomerTypes property that you show in your CreditUser class. Something like this:
But based on your comment, the choices are the same for each row. More like this:
This means that your code can be simplified.
DataSources for DataGridViewComboBoxColumn and DataGridView
I believe what might be causing the confusion is that the data sources for DataGridView and for DataGridViewComboBoxColumn are completely unrelated in this case. Since there is no need to provide each user with individualized options, the source of drop down items only needs to be set one time for the entire column.
The DataSource for DataGridViewComboBoxColumn is an array of strings named ResponsibleList that will not change.
private readonly string[] ResponsibleList = new []
{
"Production",
"Distribution",
"Customer Service",
"Sales",
String.Empty
};
The DataSource for dataGridViewCreditUser is a binding list named CreditUsers.
readonly BindingList<CreditUser> CreditUsers = new BindingList<CreditUser>();
Initialize
Assigning these data sources is done in the override of OnLoad (there's no need to have the form subscribe to its own Load event). Allow me to explain what I've done and you can modify this flow to your specific requirements.
protected override void OnLoad(EventArgs e)
{
dataGridViewCreditUser.DataSource = CreditUsers;
Adding one or more items will autogenerate the columns.
// Calls a mock method that returns a simulated response of three CreditUsers.
foreach (var creditUser in mockMainForm_srv_GetCreditUser("", ""))
{
CreditUsers.Add(creditUser);
}
Create a ComboBox column that will be swapped out for the autogenerated one. This is where ResponsibleList becomes the DataSource for the ComboBox.
var colCB = new DataGridViewComboBoxColumn
{
Name = nameof(CreditUser.Responsible),
// Connects the column value to the Responsible property of CreditUser
DataPropertyName = nameof(CreditUser.Responsible),
AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells,
// Connects the four drop-down options in ResponsibleList to the ComboBox
DataSource = ResponsibleList,
};
Perform the swap. Remove the autogenerated column and replace it with the custom version.
var index = dataGridViewCreditUser.Columns[nameof(CreditUser.Responsible)].Index;
dataGridViewCreditUser.Columns.RemoveAt(index);
dataGridViewCreditUser.Columns.Insert(index, colCB);
Make sure the cell is NOT left in an editing state after change of ComboBox or CheckBox.
dataGridViewCreditUser.CurrentCellDirtyStateChanged += (sender, e) =>
{
switch (dataGridViewCreditUser.Columns[dataGridViewCreditUser.CurrentCell.ColumnIndex].Name)
{
case nameof(CreditUser.Responsible):
case nameof(CreditUser.Restricted):
dataGridViewCreditUser.CommitEdit(DataGridViewDataErrorContexts.Commit);
break;
}
};
To monitor ongoing changes, update the Title bar whenever the source list is modified.
CreditUsers.ListChanged += (sender, e) =>
{
if ((dataGridViewCreditUser.CurrentCell != null) && (dataGridViewCreditUser.CurrentCell.RowIndex < CreditUsers.Count))
{
var creditUser = CreditUsers[dataGridViewCreditUser.CurrentCell.RowIndex];
Text = creditUser.ToString();
}
};
Now that the DataGridView is all set up the columns can be formatted.
foreach (DataGridViewColumn col in dataGridViewCreditUser.Columns)
{
if (col.Name == nameof(CreditUser.UserName))
{
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
else
{
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}
}
}
MOCK QUERY FOR TESTING
// MOCK for minimal example
private List<CreditUser> mockMainForm_srv_GetCreditUser(string v1, string v2)
{
return new List<CreditUser>
{
new CreditUser
{
UserName = "Tom",
CreditLimit=10000m,
},
new CreditUser
{
UserName = "Richard",
CreditLimit=1250m,
Restricted = true
},
new CreditUser
{
UserName = "Harry",
CreditLimit=10000m,
},
};
}
CreditUser class
// REDUCED for minimal example
public class CreditUser : INotifyPropertyChanged
{
string _UserName = string.Empty;
public string UserName
{
get => _UserName;
set
{
if (!Equals(_UserName, value))
{
_UserName = value;
OnPropertyChanged();
}
}
}
string _Responsible = String.Empty;
public string Responsible
{
get => _Responsible;
set
{
if (!Equals(_Responsible, value))
{
_Responsible = value;
OnPropertyChanged();
}
}
}
decimal _CreditLimit = 0;
public decimal CreditLimit
{
get => _CreditLimit;
set
{
if (!Equals(_CreditLimit, value))
{
_CreditLimit = value;
OnPropertyChanged();
}
}
}
bool _Restricted = false;
public bool Restricted
{
get => _Restricted;
set
{
if (!Equals(_Restricted, value))
{
_Restricted = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public override string ToString()
{
var ctype = Responsible == string.Empty ? "Not Specified" : $"{Responsible}";
return Restricted ?
$"{UserName} ({ctype}) Restricted" :
$"{UserName} ({ctype}) {CreditLimit}";
}
}
i'm new on Xamarin, i created a view where i'm showing a list of items, i created activity, axml files and style. It work well.
Now i would like start a "intent" on item selected but i have problem to get position or title of item.
My "Console.WriteLine(selectedFromList)" show just 0 value, i would like get more information from my item selected, like the right position or title, so i can validate and "intent" my specific activities for each item selected.
namespace app_sofis.Droid
{
[Activity(Label = "ServiziActivity" , Theme = "#style/ThemeActionBar")]
public class ServiziActivity : Activity
{
public static int TYPE_ITEM = 0;
public static int TYPE_SEPORATOR = 1;
List<IMenuItemsType> item = new List<IMenuItemsType>();
private ListView lst;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Servizi);
// my items
item.Add(new MenuHeaderItem("Servizi per il paziente"));
item.Add(new MenuContentItem("COLLO", "Inforntuni, patologie, interventi e riabilitazione", Resource.Mipmap.ic_schiena));
item.Add(new MenuContentItem("SPALLA", "Inforntuni, patologie, interventi e riabilitazione", Resource.Mipmap.ic_schiena));
item.Add(new MenuContentItem("SCHIENA", "Inforntuni, patologie, interventi e riabilitazione", Resource.Mipmap.ic_schiena));
lst = FindViewById<ListView>(Resource.Id.lstview);
lst.Adapter = new ListViewAdapter(this, item);
lst.ItemClick += (object sender, Android.Widget.AdapterView.ItemClickEventArgs e) =>
{
// HERE I WOULD LIKE VALIDATE ITEM AND START "intent" with my specific activities :)
string selectedFromList = lst.GetItemAtPosition(e.Position).ToString();
// trying to show item position but it return always 0
Console.WriteLine(selectedFromList);
};
}
// code for my listview
public interface IMenuItemsType
{
int GetMenuItemsType();
}
public class MenuHeaderItem : IMenuItemsType
{
public string HeaderText { get; set; }
public int GetMenuItemsType()
{
return TYPE_ITEM;
}
public MenuHeaderItem(string _headerText)
{
HeaderText = _headerText;
}
}
public class MenuContentItem : IMenuItemsType
{
public string Title { get; set; }
public string SubTitle { get; set; }
public int IconImage { get; set; }
public int GetMenuItemsType()
{
return TYPE_SEPORATOR;
}
public MenuContentItem(string _title, string _subtitle, int _iconImage)
{
Title = _title;
SubTitle = _subtitle;
IconImage = _iconImage;
}
}
public class ListViewAdapter : ArrayAdapter<IMenuItemsType>
{
private Context context;
private List<IMenuItemsType> items;
private LayoutInflater inflater;
public ListViewAdapter(Context context, List<IMenuItemsType> items) : base(context, 0, items)
{
this.context = context;
this.items = items;
this.inflater = (LayoutInflater)this.context.GetSystemService(Context.LayoutInflaterService);
}
public override int Count
{
get
{
//throw new System.NotImplementedException();
return items.Count;
}
}
public override long GetItemId(int position)
{
//throw new System.NotImplementedException();
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
//throw new System.NotImplementedException();
View view = convertView;
try
{
IMenuItemsType item = items[position];
if (item.GetMenuItemsType() == TYPE_ITEM)
{
MenuHeaderItem _headerItem = (MenuHeaderItem)item;
view = inflater.Inflate(Resource.Layout.ListViewHeaderItem, null);
// user dont click header item
view.Clickable = false;
var headerName = view.FindViewById<TextView>(Resource.Id.txtHeader);
headerName.Text = _headerItem.HeaderText;
}
else if (item.GetMenuItemsType() == TYPE_SEPORATOR)
{
MenuContentItem _contentItem = (MenuContentItem)item;
view = inflater.Inflate(Resource.Layout.ListViewContentItem, null);
var _title = view.FindViewById<TextView>(Resource.Id.txtTitle);
var _imgIcon = view.FindViewById<ImageView>(Resource.Id.imgIcon);
var _subTitle = view.FindViewById<TextView>(Resource.Id.txtSubTitle);
_title.Text = _contentItem.Title;
_imgIcon.SetBackgroundResource(_contentItem.IconImage);
_subTitle.Text = _contentItem.SubTitle;
}
}
catch (Exception ex)
{
Toast.MakeText(context, ex.Message, ToastLength.Long);
}
return view;
}
}
}
}
MY CONSOLE RETURN
[ViewRootImpl#1fcd26a[ServiziActivity]] ViewPostImeInputStage processPointer 0
[ViewRootImpl#1fcd26a[ServiziActivity]] ViewPostImeInputStage processPointer 1
app_sofis.Droid.ServiziActivity+MenuContentItem
You can use position to get item from the list:
lst.ItemClick += (object sender, Android.Widget.AdapterView.ItemClickEventArgs e) =>
{
MenuItemsType selectedItem = (MenuItemsType)item[e.Position];
Console.WriteLine(selectedItem.Title);
Console.WriteLine(selectedItem.SubTitle);
};
Hope this helps
I would suggest the following addition to your adapter class:
public override IMenuItemsType this [int index]
{
get { return items[index]; }
}
That way it is easier to access an item at the given position.
lst.ItemClick += (object sender, Android.Widget.AdapterView.ItemClickEventArgs e) =>
{
var selectedFromList = (lst.Adapter as ListViewAdapter)[e.Position];
if(selectedFromList is MenuHeaderItem)
{
var intent = new Intent(this, typeof(YOUR_ACTIVITY1));
StartActivity(intent);
}
if(selectedFromList is MenuContentItem)
{
var intent = new Intent(this, typeof(YOUR_ACTIVITY2));
StartActivity(intent);
}
};
I have an issue of not being able to add created columns to the collection of that type.
I have the following property:
public ObservableCollection<BrowseLayoutColumns> _BrowseLayoutColumns = new ObservableCollection<BrowseLayoutColumns>();
[Category("Design")]
public ObservableCollection<BrowseLayoutColumns> BrowseLayoutColumns
{
get { return _BrowseLayoutColumns; }
set { _BrowseLayoutColumns = value; }
}
BrowseLayoutColumns
[TypeConverter(typeof(BrowseLayoutColumns))]
public class BrowseLayoutColumns : DataGridViewColumn
{
#region Properties
public string ColumnName { get; set; }
public string BindingField { get; set; }
#endregion
public BrowseLayoutColumns()
: base(new DataGridViewTextBoxCell())
{
}
public override object Clone()
{
var copy = base.Clone() as BrowseLayoutColumns;
copy.ColumnName = ColumnName;
copy.BindingField = BindingField;
return copy;
}
}
so on the design time:
after I Press the Ok button.
this is what happens and this is what I wanted to happen
the problem is what the compiler does is :
private void InitializeComponent()
{
this.browseLayoutColumns1 = new MyBaseFramework.MyTypeEditors.BrowseLayoutColumns();
this.SuspendLayout();
//
// browseLayoutColumns1
//
this.browseLayoutColumns1.BindingField = "TEST";
this.browseLayoutColumns1.ColumnName = "TEST";
this.browseLayoutColumns1.Name = "browseLayoutColumns1";
//
// BrowseCONTACTS_BASE
//
this.ClientSize = new System.Drawing.Size(606, 447);
this.Name = "BrowseCONTACTS_BASE";
this.ResumeLayout(false);
}
why does not it do the following too?
BrowseLayoutColumns.Add(browseLayoutColumns1);
what am I missing? any help would be very beneficial! thanks in advance
I'm having a class which has multiple collections and these collections are depending on each other. (Think of a DataTable's Columns and Rows).
What I would like to achieve is that I can set the DataContext of a DataGrid to my class and set the binding path of a column to an item of the first collection (DataTable.Column).
But the DataGrid should show a list of items of the second collection. (DataTable.Rows)
public class MyClass
{
public List<object> Collection1 { get; set; }
public List<List<object>> Collection2 { get; set; }
public void AddBinding()
{
var myClass = new MyClass()
{
Collection1 = new List<object>
{
"Item1",
"Item2",
"Item3",
"Item4",
},
Collection2 = new List<List<object>>
{
new List<object>
{
"Value1 linked to Item1",
"Value2 linked to Item2",
"Value3 linked to Item3",
"Value4 linked to Item4",
},
new List<object>
{
"Value5 linked to Item1",
"Value6 linked to Item2",
"Value7 linked to Item3",
"Value8 linked to Item4",
},
}
};
var grid = new DataGrid();
grid.DataContext = myClass;
grid.Columns.Add(new DataGridTextColumn()
{
Binding = new Binding("Item1")
});
grid.Columns.Add(new DataGridTextColumn()
{
Binding = new Binding("Item2")
});
grid.Columns.Add(new DataGridTextColumn()
{
Binding = new Binding("Item3")
});
grid.Columns.Add(new DataGridTextColumn()
{
Binding = new Binding("Item4")
});
}
}
I wonder if it is possible? Anyone knows how to achieve this?
By inheriting DynamicObject in the Row object you can override the methods GetDynamicMemberNames, TryGetIndex, TryGetMember, TrySetIndex, TrySetMember.
These methods makes it possible to use the []-indexers to get or set a specific value (Column).
Set the Rows collection as ItemsSource and set the values in the Columns as Binding from the GridColumn
public class MyClass
{
public List<string> Columns { get; set; }
public List<MyRow> Rows { get; set; }
}
public class MyRow : DynamicObject
{
public MyClass OwnerClass { get; set; }
public List<object> Values { get; set; }
public override IEnumerable<string> GetDynamicMemberNames()
{
return OwnerClass.Columns;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && OwnerClass != null)
{
if (indexes[0] is string stringIndex && OwnerClass.Columns.Contains(stringIndex))
{
result = Values[OwnerClass.Columns.IndexOf(stringIndex)];
return true;
}
else if (indexes[0] is int intIndex)
{
result = Values[intIndex];
return true;
}
}
result = null;
return false;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if ((!string.IsNullOrEmpty(binder.Name) && OwnerClass.Columns.Contains(binder.Name)))
{
result = Values[OwnerClass.Columns.IndexOf(binder.Name)];
return true;
}
result = null;
return false;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
if (indexes.Length == 1 && OwnerClass != null)
{
if (indexes[0] is string stringIndex && OwnerClass.Columns.Contains(stringIndex))
{
Values[OwnerClass.Columns.IndexOf(stringIndex)] = value;
return true;
}
else if (indexes[0] is int intIndex)
{
Values[intIndex] = value;
return true;
}
}
return false;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if ((!string.IsNullOrEmpty(binder.Name) && OwnerClass.Columns.Contains(binder.Name)))
{
Values[OwnerClass.Columns.IndexOf(binder.Name)] = value;
return true;
}
return false;
}
}
Should it be possible to bind a WPF combo box to a class. I have a class that implements IEmunerable and IEnumerator, and contains a list of objects, as follows:
class MyClass
{
public string Title { get; set; }
public string Directory { get; set; }
public MyClass(string title, string directory)
{
Title = title;
Directory = directory;
}
}
class MyClasses : IEnumerable, IEnumerator
{
private List<MyClass> allClasses;
private int position = 0;
public List<MyClass> GetClasses()
{
allClasses = new List<MyClass>()
{
new MyClass("example1", "dir1"),
new MyClass("example2", "dir2")
};
return allClasses;
}
public IEnumerator GetEnumerator()
{
return (IEnumerator) this;
}
public object Current
{
get
{
return allClasses[position];
}
}
public bool MoveNext()
{
position++;
return (position < allClasses.Count());
}
public void Reset()
{
position = -1;
}
}
So now I want to bind this to a WPF combobox. Here’s what I have, which doesn’t work (I instead get a list of type names of the objects):
allClasses.GetClasses();
cboTest.ItemsSource = allClasses;
cboTitle.SelectedValue = "Title";
Can anyone tell me how to implement this binding?
cboTitle.SelectedValue = "Title";
should be
cboTitle.DisplayMemberPath = "Title";
Change
cboTitle.SelectedValue = "Title";
to
cboTitle.DisplayMemberPath = "Title";