I have a class containing details of my customer.
class CustomerData : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get
{ return _Name }
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
// Lots of other properties configured.
}
I also have a list of CustomerData values List<CustomerData> MyData;
I currently databinding an individual CustomerData object to textboxes in the below way which works fine.
this.NameTxtBox.DataBindings.Add("Text", MyCustomer, "Name", false, DataSourceUpdateMode.OnPropertyChanged);
I'm struggling to find a way to bind each object in the list MyData to a ListBox.
I want to have each object in the MyData list in displayed in the ListBox showing the name.
I have tried setting the DataSource equal to the MyData list and setting the DisplayMember to "Name" however when i add items to the MyData list the listbox does not update.
Any ideas on how this is done?
I found that List<T> will not allow updating of a ListBox when the bound list is modified.
In order to get this to work you need to use BindingList<T>
BindingList<CustomerData> MyData = new BindingList<CustomerData>();
MyListBox.DataSource = MyData;
MyListBox.DisplayMember = "Name";
MyData.Add(new CustomerData(){ Name = "Jimmy" } ); //<-- This causes the ListBox to update with the new entry Jimmy.
Related
I got to have one question about the auto generating column function of wpf data grid.
As you may already know, when we use a data table as an itemsource like
this.datagrid.ItemsSource = table.AsDataView();
we can see the data grid make columns according to table's columns.
But When I use my own list of data model, I get stuck.
My model class is like below
public class MyDataGridModel
{
private object[] _itemArray;
public object[] ItemArray
{
get { return _itemArray; }
set { _itemArray = value; }
}
public MyDataGridModel(List<string> data)
{
ItemArray = data.ToArray();
}
}
Populating a data grid with a list of MyDataGridModel doesn't work.
It just make one column named "ItemArray", not many columns as much as number of data in ItemArray
So, My question is that there is any interface or some magic method to make my model work like data table.
Update
It seemed that there was a unclear thing on my question.
I tried like below and xaml for a datagrid only set AutoGenerateColumn property to true.
For DataTable
List<string> data = new List<string>() { "a", "b", "c" };
DataTable table = new DataTable();
table.Columns.Add("col1");
table.Columns.Add("col2");
table.Columns.Add("col3");
table.Rows.Add(data.ToArray());
table.Rows.Add(data.ToArray());
table.Rows.Add(data.ToArray());
this.datagrid.ItemsSource = table.AsDataView();
then the the datagrid showed that three columns each named coll,2,3 and three rows that each has a,b,c
For List of MyDataGridModel
List<string> data = new List<string>() { "a", "b", "c" };
List<MyDataGridModel> data2 = new List<MyDataGridModel>();
data2.Add(new MyDataGridModel(data));
data2.Add(new MyDataGridModel(data));
data2.Add(new MyDataGridModel(data));
this.datagrid.ItemsSource = data2;
then the datagrid showed only one column named ItemArray and three rows which have string "String[] Array".
I know that I can see all data in MyDataGridModel if I add some column configuration to a datagrid. But I want to see all data without any specific column configuration, which makes a datagrid be able to use for any length of ItemArray.
I even hope I get to know if it is impossible thing or not
Thanks in advance
Your ViewModel should be inheriting from INotifyPropertyChanged
Create a new class file called ViewModelBase
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
Be sure to include the using statements
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
Then in your ViewModel
public class MyDataGridModel : ViewModelBase
{
private List<string> _items;
public List<string> Items
{
get => _items;
set => Set(ref _items, value);
}
public MyDataGridModel(List<string> data)
{
Items = data;
}
}
Then in your View set the ItemSource to bind to your Items List, be sure to set the DataContext in your view to the ViewModel
<Window.DataContext>
<local:MyDataGridModel/>
</Window.DataContext>
<DataGrid ItemsSource="{Binding Items, UpdateSourceTrigger=PropertyChanged}" />
You are only going to get 1 Column as your data type is just of string if you want to display more columns you will need to make a Model with all your properties you wish to display in the datagrid and set the type of string to your Model name then create columns in the datagrid for each property and bind their values to each column.
Hope this is of some help to you.
I have a combobox that's set to DropDownStyle=DropDownList (meaning users can't type anything, just select from dropdown). The combo contains a list of states.
I am trying to bind the selected text value to _model.StateBar, but my code doesn't seem to update the property of the object.
I've tried both of the following:
cboStates.DataBindings.Add("Text", _model, "StateBar")
cboStates.DataBindings.Add("SelectedItem", _model, "StateBar")
cboStates.DataBindings.Add("SelectedValue", _model, "StateBar")
I just need to bind it one way: updates from the control need to end up on the object.
Binding to ComboBox.SelectedValue should work, but only in case when you add items through ComboBox.DataSource.
public class Model
{
public string StateBar { get; set; }
}
// In the form
var states = new List<string> { "Alabama", "California" };
combobox.DataSource = states;
combobox.DataBindings.Add("SelectedValue", _model, "StateBar", true, DataSourceUpdateMode.OnPropertyChanged);
Binding to SelectedItem should work in all cases.
I have a ComboBox with few static values.
<ComboBox Name="cmbBoxField" Grid.Column="4" Grid.Row="2" Style="{StaticResource comboBoxStyleFixedWidth}" ItemsSource="{Binding}" ></ComboBox>
MVVMModle1.cmbBoxField.Items.Add(new CustomComboBoxItem("Text Box", "0"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Pick List", "1"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Check Box", "2"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Radio Button", "3"));
When I am saving the data in Database table it is getting saved.
((CustomComboBoxItem)(MVVMModle1.cmbBoxField.SelectedValue)).Value.ToString();
Now when I am trying to Edit my form and binding the value again to combobox it is not showing the value.
MVVMModle1.cmbBoxField.SelectedValue = dtDataList.Rows[0]["ControlList"].ToString().Trim();
Someone please help me in this. How to bind selected value to the combobox?
There are quite a few problems with your code here:
You are setting the ItemsControl.ItemsSource property to the default binding (bind to the current data context), which is incorrect unless the DataContext is any type that implements IEnumerable, which it probably isn't.
If this is correct because the DataContext is, for example, an ObservableCollection<T>, then you still have an issue because you are adding items statically to the ComboBox instead of whatever the ItemsSource is.
Also, the type of items you are adding are CustomComboBoxItem, which I'm going to assume inherits from ComboBoxItem. Either way, you can't say the SelectedValue is some string since the values in the ComboBox are not strings.
You should really not have a collection of CustomComboBoxItem's, but instead a custom class that is in itself it's own ViewModel.
Now that that's been said, here is a suggested solution to your problem:
<ComboBox ItemsSource="{Binding Path=MyCollection}"
SelectedValue="{Binding Path=MySelectedString}"
SelectedValuePath="StringProp" />
public class CustomComboBoxItem : ComboBoxItem
{
// Not sure what the property name is...
public string StringProp { get; set; }
...
}
// I'm assuming you don't have a separate ViewModel class and you're using
// the actual window/page as your ViewModel (which you shouldn't do...)
public class MyWPFWindow : Window, INotifyPropertyChanged
{
public MyWPFWindow()
{
MyCollection = new ObservableCollection<CustomComboBoxItem>();
// Add values somewhere in code, doesn't have to be here...
MyCollection.Add(new CustomComboBoxItem("Text Box", "0"));
etc ...
InitializeComponent();
}
public ObservableCollection<CustomComboBoxItem> MyCollection
{
get;
private set;
}
private string _mySelectedString;
public string MySelectedString
{
get { return _mySelectedString; }
set
{
if (String.Equals(value, _mySelectedString)) return;
_mySelectedString = value;
RaisePropertyChanged("MySelectedString");
}
}
public void GetStringFromDb()
{
// ...
MySelectedString = dtDataList.Rows[0]["ControlList"].ToString().Trim();
}
}
You could alternatively not implement INotifyPropertyChanged and use a DependencyProperty for your MySelectedString property, but using INPC is the preferred way. Anyways, that should give you enough information to know which direction to head in...
TL;DR;
Take advantage of binding to an ObservableCollection<T> (create a property for this).
Add your items (CustomComboBoxItems) to the ObservableCollection<T>.
Bind the ItemsSource to the new collection property you created.
Bind the SelectedValue to some string property you create (take advantage of INPC).
Set the SelectedValuePath to the path of the string property name of your CustomComboBoxItem.
Can you use cmbBoxField.DataBoundItem()? If not target the source from the selected value, i.e. Get the ID then query the source again to get the data.
(CustomComboBoxItem)MVVMModle1.cmbBoxField.DataBoundItem();
When you bind a datasource it is simpler to do it like this:
private List GetItems(){
List<CustomComboBoxItem> items = new List<CustomComboBoxItem>();
items.Add(new CustomComboBoxItem() {Prop1 = "Text Box", Prop2 = "0"});
//...and so on
return items;
}
Then in your main code:
List<CustomComboBoxItem> items = this.GetItems();
MVVMModle1.cmbBoxField.DisplayMember = Prop1;
MVVMModle1.cmbBoxField.ValueMember = Prop2;
MVVMModle1.cmbBoxField.DataSource = items;
This will then allow your selected value to work, either select by index, value or text
var selected = dtDataList.Rows[0]["ControlList"].ToString().Trim();
MVVMModle1.cmbBoxField.SelectedValue = selected;
I have a class that stores a list of dictionary entries. I want bind that to a datasource for gridview from codebehind.
Code for dictionary type of , representing ErrorMessage and failed field.
public partial class FailedFields
{
private Dictionary<string, string> Code_Error = new Dictionary<string, string>();
public void AddFailedField(string field, string message)
{
Code_Error.Add(field, message);
}
public Dictionary<string, string> GetFailedFields()
{
return Code_Error;
}
}
Code for List of Dictionary entries.
public partial class ErrorFieldsList
{
private static List<Order.FailedFields> ErrorList = new List<Slab.FailedFields>();
public void AddErrorField(Order.FailedFields errs)
{
ErrorList.Add(errs);
}
public List<Order.FailedFields> GetErrorMessages()
{
return ErrorList;
}
}
Running in Visual Studio debug mode, i can see the list has the error list, but i cannot get it to display in the gridview. Bellow is one of the many ways (the one that makes most sense) i tried to set the list as a datasource.
ErrorBoxGridView.DataSource = FailedRecords.GetErrorMessages(). ;
ErrorBoxGridView.DataBind();
Any idea where i am going wrong ?
Also, i don't want to specify a datasource in the aspx page because i only want to display this when the error occurs.
If interested why i am doing this to store error messages, have a look at this:link 1
Solved Here Related Question
I will document a complete project when i finish on the wiki.
This can not be done I think. What I'd do is:
Instead of using Dictionary<string, string> define a class that contains two public properties for field and message
Create an object data source for that class (using Visual Studios "Data Sources" window)
Have GetErrorMessages() return List<ErrorClass> instead of Dictionary
Assign that list to the binding source.
EDIT
This is to clarify things according to the latest comments. What you need is one class that contains the information for one error. For example:
public class ErrorInfo
{
public string Field { get { ... } }
public string Message { get { ... } }
}
After that you place a BindingSource on your form and (in code) set its DataSource property to a list of error message classes. For example:
private List<ErrorInfo> errorList = new List<ErrorInfo>();
errorList.Add(new ErrorInfo() { ... });
errorList.Add(new ErrorInfo() { ... });
errorList.Add(new ErrorInfo() { ... });
bindingSource.DataSource = errorList;
The data grid view is bound to the BindingSource. You should see data now. You can manually create columns and set them to the respective property names of your ErrorInfo class as well, but then you'd have to set dataGridView.AutoCreateColumns to false somewhere in your code.
Databind List of Dictionnary into a GridView
List<Dictionary<string,string>> resultSet = SOME List of Dictionaries...
DataGridView.DataSource = resultSet.Select(x => new {
fieldOne = x["key1"], fieldTwo = x["key2"]
}).ToList();
DataGridView.DataBind();
Now u can Bind fieldOne and fieldTwo in the DataGridView element...
Kindly check the Link for the precise ans...
Thanks
.NET provides a handy KeyValuePair<(Of <(TKey, TValue>)>) structure, that can be used in cases like this. That way you don't have to define your own class. HTH.
Or you could bind to the Value & Key properties of each Dictionary item:
ErrorBoxGridView.DataSource = FailedRecords.GetErrorMessages();
ErrorBoxGridView.DataTextField = "Value";
ErrorBoxGridView.DataValueField = "Key";
ErrorBoxGridView.DataBind();
Is there a way to determine the order of the columns displayed in
a datagridview when binding it to a datasource whitch contains an
underlying IList ?
I thought there was a specific property attribute for this purpose
but can't recall what it actually was.
eg:
public void BindToGrid(IList<CustomClass> list)
{
_bindingSource.DataSource = list;
dataGridView1.DataSource = _bindingSource.DataSource;
}
Type binded should be something like this
class CustomClass
{
bool _selected = false;
//[DisplayOrder(0)]
public bool Selected
{
get { return _selected; }
set { _selected = value; }
}
string _name;
//[DisplayOrder(2)]
public string Name
{
get { return _name; }
set { _name = value; }
}
string _value;
//[DisplayOrder(1)]
public string Value
{
get { return _value; }
set { _value = value; }
}
}
Edit:
I would like to add that I rather not want to add the columns manually to columns list in the designer. I'd like to keep this as dynamic as possible.
In the DataGridView specify an actual list of columns instead of allowing it to auto-databind. You can do this in Design View in Visual Studio by selecting the control and adding the columns. Make sure you specify in each column which property it should bind to. Then you can rearrange the columns any way you want as well as do other customizations.
I think that the DisplayOrder attribute is relatively new and probably not supported in the DataGridView control.
The display order of the columns in the DataGridView is determined by the DisplayIndex properties of the DataGridViewColumn-s. You would have to set these properties on the columns of the grid, in order to change their order.
I also agree with Eilon's answer: you can create the list of the columns yourself, instead of auto-databinding, and that way you can determine the order in which they will be displayed.
The column ordering does not always work. You'll need to turn off AutoColumnCreate to fix inconsistencies:
http://www.internetworkconsulting.net/content/datadridview-displayorder-not-working
I am not sure whether this is a functionality that .Net Offers, but if you just change the order of your properties in the class, the grid renders the columns in the same order.
The below two classes will render in the order they are typed in the class. Strange!!
class CustomClass
{
public bool Selected {get;set;}
public string Name{get;set;}
}
class CustomClass
{
public string Name{get;set;}
public bool Selected {get;set;}
}