I have a class like
internal class CalculationsDataRelations
{
public List<CalculationsDataRelation> Relations;
}
And trying to bind it to a datagridview using following code
relations = new CalculationsDataRelations();
bs = new BindingSource(relations, "Relations");
DgvRelations.DataSource = bs;
But I get exception "DataMember property 'Relations' cannot be found on the DataSource."
How to bind datagridview properly?
Binding has to happen with Properties, but your internal class is only providing a Field. Also, you haven't instantiated the List<CalculationsDataRelation> variable with "new".
Try changing it to something like this:
internal class CalculationsDataRelations {
private List<CalculationsDataRelation> relations = new List<CalculationsDataRelation>();
public List<CalculationsDataRelation> Relations {
get { return relations; }
}
}
Related
I'm currently writing a pretty small program in C# and have this list that I want to bind to a combobox. Now, I've put that list in a class, and want to bind that list to a combobox. The code below shows how far I've come so far:
Form
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Locaties locaties = new Locaties();
List<string> listofLocaties = locaties.retrieveLocations();
cboxLocToevoegen.DataSource = ???;
cboxLocOverzicht.DataSource = ???;
}
}
Class
class Locaties
{
public List<string> retrieveLocations()
{
List<string> LocatieList = new List<string>();
LocatieList.Add("Koelkast");
LocatieList.Add("Keukenlade");
LocatieList.Add("Voorraadruimte");
LocatieList.Add("Overige");
return LocatieList;
}
}
Now, I'm gonna be honest with you: my knowledge and experience with classes and methods is not perfect. That's why the solution might probably be simpler than I think. Please don't judge me on that, I'm still learning!
Anyway, I hope anyone can help me out with this!
Simply
cboxLocToevoegen.DataSource = listofLocaties ;
or directly
cboxLocToevoegen.DataSource = locaties.retrieveLocations();
you can also bind directly to a list of Locaties and then choose the property to display in the CB :
List<Locaties> listofLocaties = new List<Locaties>();
...
//Populate the list
...
cboxLocToevoegen.DataSource = listofLocaties ;
cboxLocToevoegen.DisplayMember = [a property of Locaties class];
// and the value of the CB could be another property of Locaties class:
cboxLocToevoegen.ValueMember = [the value property of Locaties class];
But ofc you have to write a new Locaties class :)
Basically this is a very very similar question to this one, with the big difference that I cannot easily "just use an observable collection inside the model"; a good example is the keycollection of a dictionary.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class Data
{
private Dictionary<String, String> _randomData;
public Data()
{
_randomData = new Dictionary<String, String>();
}
public ICollection<string> RandomDataKeys {
get {
return _randomData.Keys;
}
}
public void AddElement(string k, string v) {
_randomData[k] = v;
}
}
public class DataViewModel
{
private Data _data;
public DataViewModel(Data data)
{
_data = data;
RandomData = new ObservableCollection<String>(_data.RandomDataKeys);
//obviously above wouldn't work, since it just copies the keys.
}
public ObservableCollection<String> RandomData {get; set;}
}
Now what is the common approach here? A backup is to change the "add" function to tell the viewmodel that a new item is added (but that would require the model to have information about the viewmodel, explicitly calling a function on the viewmodel to tell the viewmodel to keep a duplicate of the model's data, this feels wasteful and slow).
The problem is, that when you're doing this RandomData = new ObservableCollection<String>(_data.RandomDataKeys); , you basically create a new instance of collection. So when you update your _data.RandomDataKeys, this changed are not reflected in your observable collection.
Read this, please
The best way would be to use ObservableCollection in your Data class, but if you don't want to do so, you can change your
public ObservableCollection<String> RandomData {get; set;}
To
public ObservableCollection<String> RandomData
{
get { return new ObservableCollection(_data.RandomDataKeys); }
}
But that is definately not a good idea, to create each time new collection.
So you can try to implement INotifyPropertyChanged interface in your Data class and bind directly to your _data.RandomDataKeys
I have three ViewModels: MainViewModel, PreferencesViewModel and ColourControllerViewModel - the latter 2 are properties of the MainViewModel.
ColourControllerViewModel is used for the 'ColourSelector' view, where various colours can be created and deleted. It contains an ObservableCollection of ColourViewModel, which has a property detailing the colour, and a bool property determining if it should be shown on the preferences tab (DisplayOnPreferences).
PreferencesViewModel is used for the 'Preferences' view, which contains a combo box of colours - this is represent by an ObservableCollection of ColourViewModel, and only those ColourViewModels where DisplayOnPreferences == true should be displayed.
My question is, what's the easiest way to do this? Currently, I am using an Action delegate called UpdateList() which passes the updated list from ColourControllerViewModel to MainViewModel, which in turn updates the PreferencesViewModel. I don't really like this though, it feels like there's a better way.
Should there be a single ObservableCollection of ColourViewModel on MainViewModel that is updated/accessed by either instance?
Here are the classes:
public class MainViewModel : ViewModel
{
private ColourMappingControllerViewModel _colourMappingControllerViewModel;
private PreferencesControllerViewModel _preferencesTabViewModel;
public MainViewModel()
{
// Initialise the database Handler
dbHandler = DatabaseHandler.DbHandlerInstance;
_colourMappingControllerViewModel = new ColourMappingControllerViewModel(dbHandler.GetColourMappingsList(), UpdateColourList);
_preferencesTabViewModel = new PreferencesControllerViewModel(dbHandler.GetPreferences, ColourMappingList)
}
public ObservableCollection<ColourMappingViewModel> ColourMappingList
{
get { return ColourMappingControllerViewModel.ColourMappingList; }
}
public void UpdateColourList(ObservableCollection<ColourMappingViewModel> colourList)
{
PreferencesTabViewModel.UpdateColourList(colourList);
}
}
public class ColourMappingControllerViewModel : ViewModel
{
public ColourMappingControllerViewModel(IEnumerable<ColourMapping> colourMappingsList, Action<ObservableCollection<ColourMappingViewModel>> updateColourListAction)
{
InitialiseCommands();
ColourMappingList = new ObservableCollection<IColourMappingViewModel>(InitialiseColourMappingsList(colourMappingsList));
}
public ICommand AddColourMappingCommand { get; set; }
private void InitialiseCommands()
{
AddColourMappingCommand = new DelegatingCommand(AddColourMapping);
}
private void AddColourMapping() // Attached to Command on View
{
var newColourMapping = new ColourMappingViewModel(
new ColourMapping());
ColourMappingList.Add(newColourMapping);
ColourMappingsCollectionView.MoveCurrentToLast();
UpdateColourMappingList();
}
private void UpdateColourMappingList()
{
UpdateColourListAction.Invoke(ColourMappingList);
}
}
public PreferencesControllerViewModel : ViewModel
{
public PreferencesControllerViewModel(object preferenceInfo, ObservableCollection<ColourMappingViewModel> colourMappingsList)
{
var pciTrendBlocks = pciBlocks;
ColourMappingsList = colourMappingsList;
}
public void UpdateColourList(ObservableCollection<ColourMappingViewModel> colourList)
{
ColourMappingsList = colourList;
}
}
I know the ObservableCollection class is being misused - it's probably not necessary on the Preferences as it will only be updated in ColourMappingController.
I would agree that you need a single ObservableCollection that is shared between views. This effectively becomes your "Model" in MVVM.
You may also want to enforce different access semantics by having a ReadOnlyObservableCollection that can be passed to your preferences VM etc. This ensures that only ColourControllerViewModel (Which gets the underlying ObservableCollection) can actually alter the collection.
In my apps I tend to have a separate data layer, but yes, for now it would be simplest to just add them to MainViewModel.
The alternative would be to have ColourControllerViewModel be the thing that owns the collection (and exposes it as a ReadOnlyObservableCollection), and have you MainViewModel just pass the collection into any other VM's that need it.
I have a BindingSource with an object inside bound to my controls. The form has a lot of textboxes generated at runtime from a list. The simplified code goes like this:
public class MyClassname: INotifyPropertyChanged
{
// some more properties here ...
public BindingList<string> mylist{ get; set; }
}
// ...
public MyClassname myclassname = new MyClassname();
private BindingSource bs = new BindingSource();
// ...
bs.DataSource = typeof(myclassname);
bs.Add(myclassname);
// ...
textBox.DataBindings.Add("Text", bs, "mylist[2]");
I get the following error:
DataMember property "mylist[2]" cannot be found on the DataSource
I already tried more types like dictionary, array or list and tried to access it in various ways without any success.
If I bind directly to the element like this:
textBox.DataBindings.Add("Text", myclassname.mylist[2], "");
It works, but the binding is one way only. (won't update the the textbox when modified elsewhere)
P.S. The other members are accessed properly, even with nested objects.
its because myList doenst have item and cant find index#2 .
try adding to your BindingList something like:
mylist.Add(new BindingList{
//itemList = value;
});
and call index something like:
mylist[0];
Take note that index[] are depends on count of the list .
I have a list that contains custom objects. These objects have different properties, and I have ~100 of them. I want to create a list of them in a listbox, but the listbox displays only
MyNamespace.MyClass
MyNamespace.MyClass
MyNamespace.MyClass
MyNamespace.MyClass
...
Is it possible to make the listbox display a certain value for each item? Lets say my objects have an ID string value. Can I display the ID for each item without discarding my objects' other properties?
I currently fill the listbox this way:
lbox.Items.Clear();
lbox.Items.AddRange(list.ToArray());
Set the DisplayMember to the property of your class that you'd like the user to see.
lbox.Items.Clear();
lbox.Items.AddRange(list.ToArray());
lbox.DisplayMember = "ID"; // ID is a public property in MyClass
Lets say you MyClass looks like this:
public class MyClass
{
public int Id { get; set; }
}
There are two options available.
You can use DataBinding for that.
Set the DisplayMember to the propertie of your MyClass which you would like to display
lbox.DisplayMember = "Id";
Set the items using the DataSource propertie of your ListBox
lbox.DataSource = list.ToArray();
You can simple override the ToString method of your MyClass object and return the text you would like to display.
Override the ToString method of your MyClass
public class MyClass
{
public int Id { get; set; }
public override string ToString()
{
return Id.ToString();
}
}
Set the items the same way as you mentioned
lbox.Items.AddRange(list.ToArray());
More Information
MSDN: ListControl.DisplayMember Property
MSDN: Object.ToString Method
Without discarding the object you can attach the object to the tag after.
list.ToList().ForEach(item => lbox.Items.Add(new ListItem(item.ID){Tag = item});
then to retreive it :
var myitem = ((ListItem)lbox.SelectedItem).Tag as MyClass;
Try using Linq.
lbox.Items.AddRange(list.Select(x => x.ID).ToArray());
Where ID is a property with the value you want to show.
You can also override ToString() in the class.