I'm trying to Databind to my custom dictionary class. In formLoad, I can bind to Car.Desc but I cannot bind to RatesCache.Desc.
They are both public string properties.
What am I missing?
Thanks!
System.ArgumentException was unhandled
Message="Cannot bind to the property or column Desc on the DataSource.\r\nParameter name: dataMember"
Source="System.Windows.Forms"
ParamName="dataMember"
public class RatesCache : Dictionary<int, Rate>
{
public string Desc { get; set; }
}
public class Car
{
public string Desc { get; set; }
}
static Car car = new Car();
static RatesCache rc = new RatesCache();
private void Form1_Load(object sender, EventArgs e)
{
rc.Desc = "hello too";
car.Desc = "Im a car";
textBox1.DataBindings.Add("Text", rc, "Desc");
}
private void Form1_Load(object sender, EventArgs e)
{
rc.Desc = "hello too";
car.Desc = "Im a car";
textBox1.DataBindings.Add("Text", rc, "Desc");
textBox1.TextChanged .TextChanged += _textBox1_TextChanged;
}
private void _messagesReceviedLabel_TextChanged(object sender, EventArgs e)
{
_textBox1.Text = rc.Desc.ToString();
}
public class RatesCache : Dictionary<int, Rate>
{
public string Desc { get; set; }
public override string ToString()
{
return Desc;
}
}
My guess is that because your class is inheriting from a Dictionary which is a Collection, it throws off the DataBinding for the textbox. Windows Forms has it's own way of dealing with databinding to a collection different then when binding directly to a property of a class. Not much of an answer, I know, but I don't think there's really a way around it. My suggestion would be to either not directly inherit from Dictionary; rather keep an internal Dictionary, and expose methods as needed. OR, don't databind the texbox directly. Rather, raise an event whenever your "Desc" property changes in your RatesCache class, and then in your form listen to that event. When it changes, update your textbox.
Related
So I have one main Form that works as the navigation bar and two UserControls that display some controls.
In UserControlsA I have some fields that require to be filled. With that data I create an Object that contains some information. I require to pass that object to UserControlsB so I can display some data there.
My idea was to make three instances of the object, one in the UserControlsA to get the information required for the object, one in the main form to get a "copy" of the object from UserControlsA, and one in UserControlsB that can get the information from the main Form.
However, this seems redundant and doesn't even work. Here's some code:
Main Form:
public partial class main : Form
{
public Object object { get; set; }
public UCA uca;
public UCB ucb;
public Form1()
{
InitializeComponent();
uca = new UCA();
ucb = new UCB();
panel2.Controls.Add(uca);
panel2.Controls.Add(ucb);
ucb.Visible = false;
uca.Visible = true;
}
private void button1_Click(object sender, EventArgs e)
{
ucb.Visible = false;
uca.Visible = true;
}
private void button2_Click(object sender, EventArgs e)
{
ucb.Visible = true;
uca.Visible = false;
}
}
UserControlsA:
public partial class UCA : UserControl
{
public Object object { get; set; }
public UCA()
{
InitializeComponent();
}
private void bUsage_Click(object sender, EventArgs e)
{
//Data is provided
object = new Object(data);
//I use var parent to try and access the object from the main form.
var parent = Parent as Form1;
object = parent.object;
}
}
UsercontrolB:
public partial class UCB : UserControl
{
public Object object { get; set; }
public UCB()
{
InitializeComponent();
}
public void updateData()
{
//I try to assign the object from the main form to this form's object.
var parent = Parent as Form1;
object = parent.object;
}
}
Using var Parent doesn't work. What can I do?
A couple of examples using the INotifyPropertyChanged Interface and an implementation that makes use of standard public events.
Related Documentation:
Windows Forms Data Binding
Change Notification in Windows Forms Data Binding
Interfaces Related to Data Binding
Using INotifyPropertyChanged:
The UserControl exposes a public Property (here, named CustomDataObject, simple string Type in the first example, object in the second. It can another Type of course).
The Property is decorated with the Bindable attribute. The BindingDirection here is more a description of the intent, there's no Template attached to it.
Two other standard Attributes are added:
DefaultValue defines the default value of a Property (the value assigned to the Property when the Control is created). It's used by the Code Generator to determine whether the current value should be serialized: it's not serialized if it matches the value set by the Attribute.
It's also used by the PropertyGrid to show, in bold, a non-default value selection or assignment.
DesignerSerializationVisibility specifies the how the Property should be serialized at design-time. Here, is set to DesignerSerializationVisibility.Visible, to signify that the Property should be serialized.
The INotifyPropertyChanged Interface can be seen as a simplified way to add Property bindings to more than one property, using the same event handler, to notify a change in value.
The default implementation of the Interface simply requires that a a public Event of type PropertyChangedEventHandler is added to the class.
When a Property value is changed, the setter just invokes the Event. There are slightly different ways to perform this action; here I'm using a OnPropertyChanged() method that uses the CallerMemberName Attribute to acquire the name of the Property that calls it. It's fairly common in both WinForms and WPF.
UCA UserControl:
The UserControl (see the visual example), has two Buttons that change the bound CustomDataObject Property value. Their Click action is handled by ButtonsAction_Click.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
public partial class UCA : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string m_DataObject = string.Empty;
public UCA() => InitializeComponent();
[Bindable(true, BindingDirection.TwoWay), DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string CustomDataObject {
get => m_DataObject;
set {
if (m_DataObject != value){
m_DataObject = value;
OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private void ButtonsAction_Click(object sender, EventArgs e)
{
var btn = sender as Button;
CustomDataObject = (btn == SomeButton) ? txtInput1.Text : txtInput2.Text;
}
}
UCB UserControl:
This other UserControl is the receiver. It just exposes a public Property (ReceiverDataObject) that will be bound to the CustomDataObject Property of UCA.
The ReceiverDataObject property is also defined as [Bindable], with the intention of making it one-way only. The property doesn't raise any event. It receive a value, stores it in a private Field and sets an internal UI element.
public partial class UCB : UserControl
{
private string m_RecvDataObject = string.Empty;
public UCB() => InitializeComponent();
[Bindable(true, BindingDirection.OneWay)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ReceiverDataObject {
get => m_RecvDataObject;
set {
m_RecvDataObject = value;
txtPresenter.Text = m_RecvDataObject;
}
}
}
Using Standard Events notifications:
You can also generate Property change notifications using standard Events.
The difference is that you need an Event for each Property that should notify changes.
If you already have Event delegates used for this, then it's probably a good choice, since there's very few to add: just call the protected method that raises the Event in the Property setter.
Here, I'm, using the common .Net Event handling, using the EventHandlerList defined by the underlying Component class and exposed by its Events property, to add remove event subscriptions.
The Events are usually raised calling a protected method that has the same name of the Event, except the On prefix.
Here, CustomDataObjectChanged Event => OnCustomDataObjectChanged() method.
You can see this pattern in all standard Controls.
▶ The CustomDataObjectChanged name assigned to the Event is not a choice: this event must have the same name of the Property and the Changed suffix.
This is the pattern, it's enough to just follow it.
UCA UserControl:
public partial class UCA : UserControl
{
private static readonly object Event_CustomDataObjectChanged = new object();
private object m_DataObject = null;
public UCButtonActions() => InitializeComponent();
[Bindable(BindableSupport.Yes, BindingDirection.TwoWay), DefaultValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public object CustomDataObject {
get => m_DataObject;
set {
if (m_DataObject != value){
m_DataObject = value;
OnCustomDataObjectChanged(EventArgs.Empty);
}
}
}
public event EventHandler CustomDataObjectChanged {
add {
Events.AddHandler(Event_CustomDataObjectChanged, value);
}
remove {
Events.RemoveHandler(Event_CustomDataObjectChanged, value);
}
}
protected virtual void OnCustomDataObjectChanged(EventArgs e)
{
if (Events[Event_CustomDataObjectChanged] is EventHandler evth) evth(this, e);
}
}
UCB UserControl:
The second UserControl doesn't change. It's just the receiver.
The Form class (or another class used as Handler):
In the Form Constructor, or any other method called after the Form initialization, use the DataBindings property of UCB to link the Properties of the two UserControls:
public SomeForm()
{
InitializeComponent();
ucb1.DataBindings.Add("ReceiverDataObject", uca1, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
You can also use a BindingSource to mediate:
BindingSource ucsSource = null;
public SomeForm()
{
InitializeComponent();
ucsSource = new BindingSource(uca1, null);
ucb1.DataBindings.Add("ReceiverDataObject", ucsSource, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
Sample functionality:
Maybe you should redesign your data flow. UserControl should not usually make assumptions of what its parent would be, that's why it's a "customized control". It can be a Form1 but not necessary. So you shouldn't do casting like in your example.
To provide the information from A to B, one way is to create public Get/Set methods or properties for those controls. And the main form works with those public members, pseudo-code can be:
class main{
UCA uca;
UCB ucb;
public void RefreshData(){
object data = uca.GetData();
ucb.UpdateData(data);
}
}
So I just learned how to properly use events I guess. Here's how the code looks now:
Main form:
public partial class main : Form
{
public UCA uca;
public UCB ucb;
public delegate void passObject(object source, someObject u);
public Form1()
{
InitializeComponent();
uca = new UCA();
ucb = new UCB();
panel2.Controls.Add(uca);
panel2.Controls.Add(ucb);
ucb.Visible = false;
uca.Visible = true;
}
private void Form1_Load(object sender, EventArgs e)
{
uca.objectRequired += ucb.ucb_objectRequired;
}
private void button1_Click(object sender, EventArgs e)
{
ucb.Visible = false;
uca.Visible = true;
}
private void button2_Click(object sender, EventArgs e)
{
ucb.Visible = true;
uca.Visible = false;
}
}
Usercontrol A:
public partial class UCA : UserControl
{
public someObject o { get; set; }
public event passObject objectRequired;
public UCA()
{
InitializeComponent();
}
private void bUsage_Click(object sender, EventArgs e)
{
//Data is provided
o = new someObject(data);
usageRequired?.Invoke(this, o);
}
}
Usercontrol B:
public partial class UCB : UserControl
{
public SomeObject o { get; set; }
public UCDetails()
{
InitializeComponent();
}
public void ucn_objectRequired(object sender, sObject u)
{
o = u;
//Use the data from the object.
}
}
I have a Class where there a 2 variables int ID and string name. I made a list of several objects and loaded them onto a listbox. The listbox only show the name. Is there a way to retrieve the ID from the listbox?
class Show
{
private int _Id;
private string _Naam;
private string _Genre;
public override string ToString()
{
return Naam;
}
}
from a database i make a list of objects.
private void bttn_zoek_Click(object sender, EventArgs e)
{
foreach (object a in List<show> List)
{
listbox1.Items.Add(a);
}
}
I hope this is enough
Assuming WinForms, here is a super simple example of overriding ToString() to control how the class is displayed in the ListBox, and also how to cast the selected item in the ListBox back to your class type so you can extract values from it. There are other ways to accomplish this task, but you should understand a bare bones example like this first:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
SomeClassName sc1 = new SomeClassName();
sc1.ID = 411;
sc1.Name = "Information";
listBox1.Items.Add(sc1);
SomeClassName sc2 = new SomeClassName();
sc2.ID = 911;
sc2.Name = "Emergency";
listBox1.Items.Add(sc2);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex != -1)
{
SomeClassName sc = (SomeClassName)listBox1.Items[listBox1.SelectedIndex];
label1.Text = "ID: " + sc.ID.ToString();
label2.Text = "Name: " + sc.Name;
}
}
}
public class SomeClassName
{
public int ID;
public string Name;
public override string ToString()
{
return ID.ToString() + ": " + Name;
}
}
Posting some of your code would be nice. Have you tried listBox.items[index].ID?
Here I'm assuming that index is whatever index you're currently searching for.
You can also try listBox.SelectedItem[index].ID if you're doing something like an event.
I've defined an Observable collection as shown below,
public class PropertyFieldsInExcel
{
public string LongNames { get; set; }
public string ShortNames { get; set; }
public string CNames { get; set; }
}
static ObservableCollection<PropertyFieldsInExcel> Properties =
new ObservableCollection<PropertyFieldsInExcel>();
I have a method which changes the the value of some of the elements in that class like so,
public static void AutofillCell()
{
((INotifyPropertyChanged)Properties).PropertyChanged +=
new PropertyChangedEventHandler(PropertyChangedEvent);
Properties[i].CNames = "It works";
Properties[i].CNames = "Ha ha ha";
((INotifyPropertyChanged)Properties).PropertyChanged -=
new PropertyChangedEventHandler(PropertyChangedEvent);
}
When I assign a value to a particular element as shown above, the event does not fire. Why? What is the mistake I've committed?
The code of the event handler is like so,
private static void PropertyChangedEvent(object sender, PropertyChangedEventArgs e)
{
//Some code to be executed
}
Two problems:
1) PropertyFieldsInExcel does not implement INotifyPropertyChanged
2) ObservableCollection can inform you when items are changing, but only after you manually subcribe to the changed event of all items.
The link in the comment from Uwe Keim gives an exellent explanation with examples...
I need to create custom comboboxitem wich i can process among other controls that inherit System.Windows.Forms.Control class. So my comboboxitem must inherit System.Windows.Forms.Control because i use cast to that type and refer to Text property withn a loop.
There is some posts on how to create custom item but that one inherits Object class which is not working for me? I tried but it didnt work?Here is my try:
public class ComboItem : System.Windows.Forms.Control
{
public object Value { get; set; }
public override string Text { get; set; }
// public object Value { get; set; }
public ComboItem(string text) { this.Text = text; }
public ComboItem(string text, string value) { this.Text = text; this.Value = value; }
public override string ToString()
{
return Text;
}
}
nothing is displayed in combo box after following code
private void Form1_Load(object sender, EventArgs e)
{
ComboItem asd = new ComboItem("qweqwwqeq");
ComboItem asd2 = new ComboItem("2222222");
comboBox1.Items.Add(asd);
comboBox1.Items.Add(asd2);
comboBox1.SelectedIndex = 1;
}
this is context in which i need to use it:
System.Windows.Forms.Control ctrl = (System.Windows.Forms.Control)asd["Kontrola"];
ctrl.Text = (String)asd["Engleski"];
I assume you are not really talking about the ComboBoxItem Class, which is from WPF but simply of the Winforms Combobox which contains objects.
For some reason Controls don't get displayed in Collections unless they are actually placed in a Container Control.
So you probably have to wrap your Control in a minimal wrapper class like this:
class CtlWrapper
{
public Control theControl { get; private set; }
public CtlWrapper(string text)
{
theControl = new Control();
theControl.Text = text;
}
public CtlWrapper(Control control) { theControl = control; } // for fun
public override string ToString() { return theControl.Text; }
}
Not sure what you'll do with such a generic thing as Control in the ComboBox's Items list, but maybe you'll add some code to create different Control types..? With the wrapper the ToString text gets displayed as expected..
Of course you an add a Value string or whatever you need to the class as needed or you could use your own class as the type of 'theControl'..
Edit: For fun I have added a 2nd constructor to allow for adding existing Controls (of any type ;-)
I'm new to c# and I think I want to do this but maybe I don't and don't know it!
I have a class called SyncJob. I want to be able to create an instance to backup files from My Documents (just an example). Then I'd like to create another instance of SyncJob to backup files in another folder. So, in other words, I could have multiple instances of the same class in memory.
I'm declaring the object var first in my code so it is accessible to all the methods below it.
My question is: while using the same instance name will create a new instance in memory for the object, how can I manage these objects? Meaning, if I want to set one of the properties how do I tell the compiler which instance to apply the change to?
As I said in the beginning, maybe this is the wrong scheme for managing multiple instances of the same class...maybe there is a better way.
Here is my prototype code:
Form1.cs
namespace Class_Demo
{
public partial class Form1 : Form
{
BMI patient; // public declarition
public Form1()
{
InitializeComponent();
}
private void btnCreateInstance1_Click(object sender, EventArgs e)
{
patient = new BMI("Instance 1 Created", 11); // call overloaded with 2 arguments
displayInstanceName(patient);
}
private void displayInstanceName(BMI patient)
{
MessageBox.Show("Instance:"+patient.getName()+"\nwith Age:"+patient.getAge());
}
private void btnCreateInstance2_Click(object sender, EventArgs e)
{
patient = new BMI("Instance 2 Created", 22); // call overloaded with 2 arguments
displayInstanceName(patient);
}
private void btnSetNameToJohn_Click(object sender, EventArgs e)
{
// this is the issue: which instance is being set and how can I control that?
// which instance of patient is being set?
patient.setName("John");
}
private void btnDisplayNameJohn_Click(object sender, EventArgs e)
{
// this is another issue: which instance is being displayed and how can I control that?
// which instance of patient is being displayed?
displayInstanceName(patient);
}
}
}
Class file:
namespace Class_Demo
{
class BMI
{
// Member variables
public string _newName { get; set; }
public int _newAge { get; set; }
// Default Constructor
public BMI() // default constructor name must be same as class name -- no void
{
_newName = "";
_newAge = 0;
}
// Overload constructor
public BMI(string name, int age)
{
_newName = name;
_newAge = age;
}
//Accessor methods/functions
public string getName()
{
return _newName;
}
public int getAge()
{
return _newAge;
}
public void setName(string name)
{
_newName = name;
}
}
}
You can have public List<BMI> PatientList { get; set; } instead of BMI patient;
if you have one patient you not sure which item accessing and when you assign it will replace previous one
public List<BMI> PatientList { get; set; }
public Form1()
{
InitializeComponent();
PatientList = new List<BMI>();
}
with list of BMI you can add items like below
PatientList.Add(new BMI("Instance 1 Created", 11));
PatientList.Add(new BMI("Instance 2 Created", 22));
if you need to set name of instance 1, you can get the item by index
PatientList[0].setName("John");
Or you can find the patient by one of the property by loop though the PatientList
if you need to display the patient details of "John", by using LINQ
displayInstanceName(PatientList.FirstOrDefault(p=>p.Name =="John"));
If you need to manage a collection of instances, use a List<BMI> or similar. The generic List<T> class can hold (almost) any type of object, is easy to work with, etc. It's also a vital part of the .NET toolkit that you will use many, many times.
Also, consider rewriting your BMI class to use properties more effectively:
class BMI
{
public string NewName { get; set; }
public int NewAge { get; protected set; }
public BMI()
: this("", 0)
{ }
public BMI(string name, int age)
{
NewName = name;
NewAge = age;
}
}
The accessor methods are not required unless you need them for interop with some other system. Using modifiers on the get and set accessors on the properties themselves you can make public-read/private-write properties, etc.