WPF DataGrid DataBinding is not displayed - c#

I have a DataGrid in my current WPF Application which I would like to bind to a ViewModel that holds a ObservableCollection. The user can enter search values in some TextBoxes and after enter has been hit I am performing an query to our database that retunrs a table of records. From these records I am populate the data for the ObservableCollection. I am now struggeling now that the datagrid is not displaying the data.
I have read a howl bunch of posts about the binding but I am still missing something I think.
Product.cs
public class Product : InotifyPropertyChanged, IEditableObject
{
public string Title { get; set; } = "";
//public Product()
//{
//}
private ProductViewModel _productViewModel = new ProductViewModel();
public ProductViewModel productViewModel { get { return _productViewModel; } set { _productViewModel = value; } }
public DataTable ProductsTable { get; set; }
public void GetProducts(string filter)
{
//< --doing some stuff to fill the table-->
foreach (DataRow row in ProductsTable.Rows)
{
productViewModel.Products.Add(new Product
{
Title = (string)row["TITLE"],
});
}
}
}
ProductViewModel.cs
public class ProductViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Product _SelectedProduct;
private ObservableCollection<Product> _Products = new ObservableCollection<Product>();
public ObservableCollection<Product> Products { get { return _Products; } set { _Products = value; } }
public ProductViewModel()
{
}
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ProductWindow.xaml
<DataGrid
Name="ProductsGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding Products, Mode=TwoWay, NotifyOnSourceUpdated=True}"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}"
CanUserAddRows="False" SelectionUnit="FullRow"
VerticalAlignment="Stretch"
Grid.Row="0"
Margin="10,10,10,10"
>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Title}" Header="Title"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
ProductWindow.xaml.cs
public partial class ProductWindow : Page
{
public object DialogResult { get; private set; }
//public ProductViewModel ProductViewModel;
public ProductWindow()
{
InitializeComponent();
DataContext = new ProductViewModel();//stackflow
//var ProductViewModel = products.ProductViewModel;
//ProductsGrid.DataContext = new ProductViewModel();
}
public ProductViewModel ViewModel => DataContext as ProductViewModel;
private void OnKeydownHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
var tb = sender as TextBox;
Product products = new Product();
string filter = "";//performing some ifelse to create filter
products.GetProducts(filter);
//ProductsGrid.DataContext = products.ProductsTable;
//ProductsGrid.DataContext = products.productViewModel;
}
else if (e.Key == Key.Escape)
{
ProductsGrid.DataContext = null;
foreach (TextBox tb in FindVisualChildren<TextBox>(this))
{
// do something with tb here
tb.Text = "";
}
}
}
}

If DataContext is a ProductViewModel, and the Products collection of that ProductViewModel is populated, you will see rows in your DataGrid. I've tested that. It appears that the viewmodel you're giving it may not have any rows.
That said, there's a problem with your design:
Product creates a ProductViewModel. ProductViewModel creates a collection of Product. Each Product, as I just said, creates a ProductViewModel. Which creates a collection of Product. They keep creating each other until you get a StackOverflowException. If you're not seeing that, you must be calling GetProducts() from somewhere else.
But there's no need for Product to own a copy of ProductViewModel. That's like adding a car to each wheel on your car.
So let's do this instead: ProductViewModel owns a collection of Product. Just that. And we'll call GetProducts() to make sure we get some items in the grid. Your binding is fine. You just weren't populating the collection.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ProductViewModel();
}
// Now you can call ViewModel.GetProducts(filterString) from an event handler.
// It would be more "correct" to use a Command, but let's take one step at a time.
public ProductViewModel ViewModel => DataContext as ProductViewModel;
}
Viewmodels
// You didn't include any implementation of IEditableObject. I presume
// you can add that back in to this version of the class.
public class Product : INotifyPropertyChanged, IEditableObject
{
// You weren't raising PropertyChanged here, or anywhere at all.
// In every setter on a viewmodel, you need to do that.
private string _title = "";
public string Title {
get => _title;
set
{
if (_title != value)
{
_title = value;
NotifyPropertyChanged(nameof(Title));
}
}
}
public Product()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ProductViewModel : INotifyPropertyChanged
{
public ProductViewModel()
{
GetProducts("");
}
public event PropertyChangedEventHandler PropertyChanged;
private Product _SelectedProduct;
public Product SelectedProduct
{
get { return _SelectedProduct; }
set
{
if (value != _SelectedProduct)
{
_SelectedProduct = value;
NotifyPropertyChanged(nameof(SelectedProduct));
}
}
}
public DataTable ProductsTable { get; set; }
public void GetProducts(string filter)
{
//< --doing some stuff to fill the table-->
Products.Clear();
foreach (DataRow row in ProductsTable.Rows)
{
Products.Add(new Product
{
Title = (string)row["TITLE"],
});
}
}
private ObservableCollection<Product> _Products = new ObservableCollection<Product>();
// This setter MUST raise PropertyChanged. See the Title property above for example.
public ObservableCollection<Product> Products { get { return _Products; } private set { _Products = value; } }
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Update
Here's the problem: You create a new Product, which creates its own ProductsViewModel. Nothing is bound to any property of that viewmodel. You fill its collection and the DataGrid doesn't know or care, because you bound its ItemsSource to a property of a different object.
So use my suggestions above, particularly the ViewModel property of the window. I just made a change in ProductsViewModel.GetProducts() that you need to copy: Now it calls Products.Clear() before populating the collection.
if (e.Key == Key.Enter)
{
var tb = sender as TextBox;
// Don't create this
//Product products = new Product();
string filter = "";//performing some ifelse to create filter
ViewModel.GetProducts(filter);
}
else if (e.Key == Key.Escape)
{
// Setting the DataContext to null breaks everything. Never do that.
//ProductsGrid.DataContext = null;
// Instead, just clear the collection. It's an ObservableCollection so it will
// notify the DataGrid that it was cleared.
ViewModel.Products.Clear();
foreach (TextBox tb in FindVisualChildren<TextBox>(this))
{
// do something with tb here
tb.Text = "";
}
}

Related

2 ObservableCollections 1 Combobox WPF

I have a WPF application, that establishes a connection to a different computer. Inside my application, I have a combo box, where the user can enter a hostname of a computer and then connect to this computer. Now once the connection was established the hostname the user entered gets saved into an Observable Collection which is bound to the combo box, so the next time he wants to connect to the same host, he can choose it directly from the combo box.
I have implemented a favorite list. which is a separate observable collection that I too want to bind to the same combo box, so the user can either choose either a favorite or a history item.
In the dropdown list of the combo box I would like 2 Groupings with a Header, something like this:
[Favorites]
My Favourite Host | myfavhost.com
My 2nd Fav | my2ndfav.com
Secretly My Fav | secretlymyfav.com
[History]
hostioncevisited.com
whyamihere.com
thanksforhelping.com
Now I don't really know how to go about that. Is there a way to bind multiple items sources to the combobox, or would I have to merge the two observable collections before I bind them to the combo box?
These are my observable collections
public ObservableCollection<string> HistoryItems { get; set; } = new ObservableCollection<string>();
public static ObservableCollection<FavoriteItem> FavoriteItems { get; set; } = new ObservableCollection<FavoriteItem>();
Here is my FavoriteItem Class
public class FavoriteItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string hostName;
private string description;
public FavoriteItem(){}
public FavoriteItem(string _hostName, string _description)
{
hostName = _hostName;
description = _description;
}
public string Hostname
{
get { return hostName; }
set
{
hostName = value;
OnPropertyChanged("Hostname");
}
}
public string Description
{
get { return description; }
set
{
description = value;
OnPropertyChanged("Description");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public override string ToString()
{
return string.Format("{0} | {1}", description, hostName);
}
}
Here is the XAML for the combo box
XAML
<ComboBox Name="cbHostName" Style="{StaticResource ComboBoxLarge}" Text="{Binding HostName}" ItemsSource="{Binding HistoryItems}"
MinWidth="300" MaxWidth="300" IsEditable="True" Margin="0,0,15,0" VerticalAlignment="Center" materialDesign:HintAssist.Hint="Computer, IP or HostProfileName"/>
You can use CompositeCollection to bind multiple collections to the same source.
Here is an example.
The disadvantage is that I don't think grouping is possible in this scenario (at least not easily).
The alternative would be to have only one list, of objects implementing the same interface, with some property to distinguish type of item, e.g.:
public interface IHost : INotifyPropertyChanged
{
string HostType { get; }
string Hostname { get; set; }
string DisplayText { get; set; }
}
public class HistoryItem : IHost
{
public event PropertyChangedEventHandler PropertyChanged;
public string HostType => "History";
public string Hostname { get; set; }
public string DisplayText => Hostname;
}
public class FavoriteItem : IHost
{
public event PropertyChangedEventHandler PropertyChanged;
public string HostType => "Favorites";
public string Hostname { get; set; }
public string Description { get; set; }
public string DisplayText => Description == null ? Hostname : $"{Description} | {Hostname}";
//other properties....
}
As I find working directly with ObservableCollection annoying, I tend to use a wrapper for it (code at the bottom). It deals with some common issues, such as possible memory leaks and raising CollectionChanged events unnecessary while adding multiple items. It also provides easy access to grouping, sorting, filtering, current item and CurrentChanged & CurrentChanging events from codebehind.
In ViewModel:
public ViewableCollection<IHost> MyItems { get; set; }
Initializing the collection:
this.MyItems = new ViewableCollection<IHost>();
// decide how your items will be sorted (important: first sort groups, then items in groups)
this.MyItems.View.SortDescriptions.Add(new SortDescription("HostType", ListSortDirection.Ascending)); // sorting of groups
this.MyItems.View.SortDescriptions.Add(new SortDescription("Hostname", ListSortDirection.Ascending)); // sorting of items
PropertyGroupDescription groupDescription = new PropertyGroupDescription("HostType");
this.MyItems.View.GroupDescriptions.Add(groupDescription);
this.MyItems.View.CurrentChanged += MyItems_CurrentChanged;
this.MyItems.AddRange(new IHost[] {
new HistoryItem { Hostname = "ccc" },
new HistoryItem { Hostname = "aaa" },
new HistoryItem { Hostname = "xxx" },
new FavoriteItem { Hostname = "vvv" },
new FavoriteItem { Hostname = "bbb" },
new FavoriteItem { Hostname = "ttt" } });
This code will execute when the item is selected:
private void MyItems_CurrentChanged(object sender, EventArgs e)
{
Console.WriteLine("Selected item: " + this.MyItems.CurrentItem?.Hostname);
}
Here is the xaml of ComboBox with grouping (using ViewableCollection, you need to bind ItemsSource to MyItems.View instead of directly to MyItems):
<ComboBox ItemsSource="{Binding MyItems.View, Mode=OneWay}"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="DisplayText">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Items.CurrentItem.HostType, StringFormat=[{0}]}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
</ComboBox>
result:
[DoNotNotify]
public class ViewableCollection<T> : ObservableCollection<T>
{
private ListCollectionView _View;
public ViewableCollection(IEnumerable<T> items)
: base(items) { }
public ViewableCollection()
: base() { }
[XmlIgnore]
public ListCollectionView View
{
get
{
if (_View == null)
{
_View = new ListCollectionView(this);
_View.CurrentChanged += new EventHandler(InnerView_CurrentChanged);
}
return _View;
}
}
[XmlIgnore]
public T CurrentItem
{
get
{
return (T)this.View.CurrentItem;
}
set
{
this.View.MoveCurrentTo(value);
}
}
private void InnerView_CurrentChanged(object sender, EventArgs e)
{
this.OnPropertyChanged(new PropertyChangedEventArgs("CurrentItem"));
}
public void AddRange(IEnumerable<T> range)
{
if (range == null)
throw new ArgumentNullException("range");
foreach (T item in range)
{
this.Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ReplaceItems(IEnumerable<T> range)
{
if (range == null)
throw new ArgumentNullException("range");
this.Items.Clear();
foreach (T item in range)
{
this.Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveItems(IEnumerable<T> range)
{
if (range == null)
throw new ArgumentNullException("range");
foreach (T item in range)
{
this.Items.Remove(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ClearAll()
{
IList old = this.Items.ToList();
base.Items.Clear();
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void CallCollectionChaged()
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
// necessary for xml easy serialization using [XmlArray] attribute
public static implicit operator List<T>(ViewableCollection<T> o)
{
return o == null ? default(List<T>) : o.ToList();
}
// necessary for xml easy serialization using [XmlArray] attribute
public static implicit operator ViewableCollection<T>(List<T> o)
{
return o == default(List<T>) || o == null ? new ViewableCollection<T>() : new ViewableCollection<T>(o);
}
}
The above code is a working example. I'm using nuget package PropertyChanged2.Fody to inject PropertyChanged notifications.
No you cannot bind multiple collections to ItemsSource, you have to merge them

Object databinding c# winforms not working

I've seen a bunch of posts regarding databinding to databases but none of them have helped with databinding to an existing object in memory. I've also looked at several stack overflow posts where people have said the following code should've bound the properties on my combo box:
projectData = new ProjectData();
this.parentTypeComboBox.DataSource = projectData.MobList;
this.parentTypeComboBox.DisplayMember = "MobType";
this.parentTypeComboBox.ValueMember = "MobType";
My data object has public getters/setters for it's various properties and I've added the INotifyPropertyChanged interface on the classes but do not attach any listeners to the event as of now. From what I've read this should've been all I had to do to get the control to bind to my data object. Any idea why I'm not seeing my combo box get populated with data when my object list changes?
Project data class:
public class ProjectData : INotifyPropertyChanged
{
public static string PROJECT_OUTPUT_DIRECTORY = "..\\";
private List<Mob> _mobList;
public List<Mob> MobList
{
get { return _mobList; }
set { _mobList = value; OnPropertyChanged("MobList"); }
}
public ProjectData()
{
MobList = new List<Mob>();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Mob Class:
//Snippet mob of class
public partial class Mob : IEquatable<Mob>, INotifyPropertyChanged
{
public Mob()
{
dataAttributeField = new List<MobDataAttribute>();
}
private List<MobDataAttribute> dataAttributeField;
private string mobTypeField;
private string parentTypeField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("DataAttribute")]
public List<MobDataAttribute> DataAttribute
{
get
{
return this.dataAttributeField;
}
set
{
this.dataAttributeField = value;
OnPropertyChanged("DataAttribute");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string MobType
{
get
{
return this.mobTypeField;
}
set
{
this.mobTypeField = value;
OnPropertyChanged("MobType");
}
}
}
Using this projectData = new ProjectData(); the MobList is an empty list yet.
If you didn't populate data, you should populate your data to list to show it in ComboBox.
Remember that every time you populate data, you should update DataSource property of your ComboBox:
this.comboBox1.DataSource = parent.Childs;
If you are bound to a data source that does not implement the
IBindingList interface, such as an ArrayList, the bound control's data
will not be updated when the data source is updated. For example, if
you have a combo box bound to an ArrayList and data is added to the
ArrayList, these new items will not appear in the combo box.
Here is a sample:
public partial class SampleForm : Form
{
public SampleForm()
{
InitializeComponent();
}
private void SampleForm_Load(object sender, EventArgs e)
{
//Initialize parent and populate its Childs
var parent = new Parent()
{
ParentName = "Parent 1",
Childs = new List<Child>{
new Child(){ChildName= "Child1"},
new Child(){ChildName= "Child2"}
}
};
this.comboBox1.DataSource = parent.Childs;
this.comboBox1.DisplayMember = "ChildName";
this.comboBox1.ValueMember = "ChildName";
}
}
public class Parent
{
public Parent()
{
Childs = new List<Child>();
}
public string ParentName { get; set; }
public List<Child> Childs { get; set; }
}
public class Child
{
public string ChildName { get; set; }
}
Screenshot:

Data binding combobox with list from WCF Service

I am developing an app(MVVM pattern) for windows store using WCF service to receive data from database.
I want to data bind a list of categories into combobox, but it's not working for me, I searched the web and still didn't find a solution.
Class Category:
public Category(Category c)
{
this.Id=c.Id;
this.Name = c.Name;
}
public int Id { get; set; }
public string Name { get; set; }
Xaml:
<ComboBox x:Name="ChooseCategory"
ItemsSource="{Binding ListCategories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedItem, Mode=TwoWay}"/>
ViewModel:
public ObservableCollection<Category> ListCategories { get; private set; }
in the OnNavigatedTo function:
var listCategory = await proxy.GetAllCategoriesAsync();
List<Category> list = new List<Category>();
foreach (var item in listCategory)
{
list.Add(new Category(item));
}
ListCategories = new ObservableCollection<Category>(list);
Anyone???
You need to implement INotifyPropertyChanged in order to let UI know that you have changed the ListCategories collection.
In your ViewModel, implement interface INotifyPropertyChanged
public class YourViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<Category> _categories;
public ObservableCollection<Category> ListCategories
{
get { return _categories; }
set
{
if (_categories != value)
{
_categories = value;
OnPropertyChanged("ListCategories");
}
}
}

Listbox First Item Selected in mvvm

I am new to mvvm. I have a listbox in my silverlight application which is binded to a observable collection in view model i want to make the listbox with first item selected. I tired this but it doesnt work.
<ListBox Height="431" Canvas.Left="17" Canvas.Top="77" Width="215" FontSize="13" ItemsSource="{Binding Path=Categorys, Mode=TwoWay}" DataContext="{Binding}" SelectedItem="{Binding CurrentCategory, Mode=TwoWay}" ItemTemplate="{StaticResource CategoryDataTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Name="lst_category">
then i added this in mainpage load of mainpage viewmodel
CurrentCategory = Categorys[0];
Can any one Help me
Do the following steps:
Make sure that the collection Categorys is filled already. You might need to use AsycCTP, Asynchronous Programming with Async and Await or some other mechanism to first wait for the collection to be filled.
The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes. The task represents ongoing work.
Implement INotifyPropertyChanged in ViewModel exposing the Property, CurrentCategory and raise the event of PropertyChanged from within the Setter of the Property.
private Category _currentCategory = null;
public Category CurrentCategory
{
get { return _currentCategory; }
set
{
if (_currentCategory != value)
{
_currentCategory = value;
// Update bindings
RaisePropertyChanged("CurrentCategory");
}
}
}
Now you can use the same piece of code:
CurrentCategory = Categorys[0];
Try using ICollectionView and IsSynchronizedWithCurrentItem. The CollectionView has all the functionality you need. For example MoveToFirst().
Xaml:
<ListBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
IsSynchronizedWithCurrentItem="True" />
ViewModel:
public class ViewModel :INotifyPropertyChanged
{
private ObservableCollection<Category> _categories = new ObservableCollection<Category>();
private Category _currentCategory;
public ObservableCollection<Category> Categories
{
get { return _categories; }
set { _categories = value; OnPropertyChanged("Categories");}
}
public Category CurrentCategory
{
get { return _currentCategory; }
set { _currentCategory = value; OnPropertyChanged("CurrentCategory");}
}
public ICollectionView CategoriesView { get; private set; }
public ViewModel()
{
Categories.Add(new Category{Id = Guid.NewGuid(), Name = "Cat1"});
Categories.Add(new Category{Id = Guid.NewGuid(), Name = "Cat2"});
Categories.Add(new Category{Id = Guid.NewGuid(), Name = "Cat3"});
CategoriesView = CollectionViewSource.GetDefaultView(Categories);
CategoriesView.CurrentChanged += OnCategoriesChanged;
CategoriesView.MoveCurrentToFirst();
}
private void OnCategoriesChanged(object sender, EventArgs e)
{
var selectedCategory = CategoriesView.CurrentItem as Category;
if (selectedCategory == null) return;
CurrentCategory = selectedCategory;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Category
{
public Guid Id { get; set; }
public string Name { get; set; }
}
You Should Try This Way also.................
List c = new List
CurrentCategory = c.firstOrDefault()

BindingList with my class populating a ComboBox using a property of it?

I have a BindingList with my class where I would like to populate a ComboBox using a property of it so when my list changes the ComboBox would change as well.
public class UserAccess
{
public override string ToString()
{
return Access;
}
public int AccessId { get; set; }
public string Access { get; set; }
public List<string> Command = new List<string>();
public bool HasCommand(string cmd)
{
return this.Command.Any(x => x == cmd);
}
}
public BindingList<UserAccess> accessList = new BindingList<UserAccess>();
On my form load I assign it to the ComboBox:
myComboBox.DataSource = accessList;
I want to populate the box with Access or with the AccessId as value and Access as the printed name.
Problem is that it will print only the last item of the list to the combobox what am I doing wrong ?
Use DisplayMember to specify what field to use for display in the ComboBox.
Make accessList readonly to guarantee that you never recreate a new instance of the list. If you don't make it readonly, this may introduce a subtle bug, if you don't reassign DataSource whenever you recereate accessList.
private readonly BindingList<UserAccess> accessList = new BindingList<UserAccess>();
public Form1()
{
InitializeComponent();
comboBox1.ValueMember = "AccessId";
comboBox1.DisplayMember = "Access";
comboBox1.DataSource = accessList;
}
private void button1_Click(object sender, EventArgs e)
{
accessList.Add(new UserAccess { AccessId = 1, Access = "Test1" });
accessList.Add(new UserAccess { AccessId = 2, Access = "Test2" });
}
If you need to be able to change items properties in accessList (like accessList[0].Access = "Test3") and see the changes reflected in UI, you need to implement INotifyPropertyChanged.
For example:
public class UserAccess : INotifyPropertyChanged
{
public int AccessId { get; set; }
private string access;
public string Access
{
get
{
return access;
}
set
{
access = value;
RaisePropertyChanged("Access");
}
}
private void RaisePropertyChanged(string propertyName)
{
var temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}

Categories

Resources