Updating a Label with DataBinding - c#

After my balance label is initially bound to a number, changing the datasource again doesn't update the value again.
I want to update the a Windows Form Label automatically after the database object is changed and I re-pull it into the constructorData.BankAccount.
public class ConstructorData
{
public Client Client { get; set; }
public BankAccount BankAccount { get; set; }
}
private void frmTransaction_Load(object sender, EventArgs e)
{
// Pretend we populated constructor data already
// This line of code is working
bankAccountBindingSource.DataSource = constructorData.BankAccount;
}
private void lnkProcess_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
constructorData.BankAccount = db.BankAccounts.Where(x => x.BankAccountId == constructorData.BankAccount.BankAccountId).SingleOrDefault();
// What do I do here
// Doesn't work
bankAccountBindingSource.EndEdit();
bankAccountBindingSource.ResetBindings(false);
}
Auto generated code:
//
// lblAccountBalance
//
this.lblAccountBalance.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.lblAccountBalance.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bankAccountBindingSource, "Balance", true));
this.lblAccountBalance.Location = new System.Drawing.Point(482, 71);
this.lblAccountBalance.Name = "lblAccountBalance";
this.lblAccountBalance.Size = new System.Drawing.Size(196, 23);
this.lblAccountBalance.TabIndex = 7;
this.lblAccountBalance.Text = "label1";

Since here (inside the form load):
bankAccountBindingSource.DataSource = constructorData.BankAccount;
you bind directly to the BankAccount instance, even implementing INotifyPropertyChanged in ConstructorData class (as suggested in the comments) will not help.
With that design, anytime you assign a new BankAccount instance to the ConstructorData.BankAccount property (as in the shown code), you need also to set it as DataSource of the BindingSource used:
constructorData.BankAccount = db.BankAccounts.Where(x => x.BankAccountId == constructorData.BankAccount.BankAccountId).SingleOrDefault();
// What do I do here
bankAccountBindingSource.DataSource = constructorData.BankAccount;

Without Implementing INotifyPropertyChanged Ivan's answer is exactly what you need.
The reason is because you put an object in DataSource of binding source this way: BindingSource.DataSource = constructorData.BankAccount, so it uses the object which is in BankAccount property as data source. If you change the value of constructorData.BankAccount, you disd't changed the data source of BindingSource and it will contain the previous object. For example take a look at this code:
var a = new MyClass("1"); // ← constructorData.BankAccount = something;
var b = a; // ← bindingSource.DataSource = constructorData.BankAccount.
a = new MyClass("2"); // ← constructorData.BankAccount = something else;
What should contain b now? Do you expect b contains MyClass("1")? Surely no.
For more information take a look at this post:
Data Binding doesn't work when I assign a new object instance to the bound variable
Can I use INotifyPropertyChanged to solve the problem?
If you implement INotifyPropertyChanged in ConstructorData and change bindings this way, yes:
bankAccountBindingSource.DataSource = constructorData;
//...
this.lblAccountBalance.DataBindings.Add(new System.Windows.Forms.Binding("Text",
this.bankAccountBindingSource, "BankAccount.Balance", true));

Related

List of an object type that prevents backcodes ( like... tbControl.Text = "value" ) from working in asp

I have been working with these properties for a while and have never had this problem before. I have a property like this:
public List<AirlineTickets_DOL> lstAirlineTickets
{
get
{
if (!(ViewState["lstAirlineTickets"] is List<AirlineTickets_DOL>))
{
ViewState["lstAirlineTickets"] = new List<AirlineTickets_DOL>();
}
return (List<AirlineTickets_DOL>)ViewState["lstAirlineTickets"];
}
set
{
ViewState["lstAirlineTickets"] = (List<AirlineTickets_DOL>)value;
}
}
When the data is returned somehow inside the OnTextChanged event I have to fill it in as shown below :
protected void tbFlightNumber_Book_Ticket_TextChanged(object sender, EventArgs e)
{
AutoFillAirlineTicket(sender);
}
private void AutoFillAirlineTicket(object sender)
{
TextBox tbFlightNumber_Book_Ticket = ((Control)sender).NamingContainer.FindControl("tbFlightNumber_Book_Ticket") as TextBox;
TextBox tbFrom_Book_Ticket = ((Control)sender).NamingContainer.FindControl("tbFrom_Book_Ticket") as TextBox;
TextBox tbTo_Book_Ticket = ((Control)sender).NamingContainer.FindControl("tbTo_Book_Ticket") as TextBox;
FillFlightData(tbFlightNumber_Book_Ticket, tbDate_Book_Ticket, tbFrom_Book_Ticket, tbTo_Book_Ticket);
UpdatePanel upAirlineTicket = ((Control)sender).NamingContainer.FindControl("upAirlineTicket") as UpdatePanel;
upAirlineTicket.Update();
//List<AirlineTickets_DOL> lstAirlineTickets = new List<AirlineTickets_DOL>();
lstAirlineTickets.Add(new AirlineTickets_DOL
{
counter = (nCounter > 0 ? nCounter : 1),
FlightNumber = tbFlightNumber_Book_Ticket.Text,
From = tbFrom_Book_Ticket.Text,
To = tbTo_Book_Ticket.Text,
});
nCounter++;
ListView lstviewAirlineTickets = ((Control)sender).NamingContainer.FindControl("lstviewAirlineTickets") as ListView;
lstviewAirlineTickets.DataSource = lstAirlineTickets;
lstviewAirlineTickets.DataBind();
}
When I remove the comment, the FillFlightData function fills the controls (TextBoxes), but when using the property as I explained above, the process of filling the fields does not work and does not give any output on the browser.
If you need more explain just tell me. I will be happy if someone help.
I found the solution, I changed the event to normal onClick Event and make sure the class had to be defined as [Serializable].

How do I delete from the list?

I'm kind of new to programming and come to quite of a problem.
I'm sure the solution is quite simple but I just can't get my head around it.
I just want to know how to Delete from the listbox and delete the data from the list entirely, because whenever I delete from the list box it disappears but when I add a new layer all the layers that I have deleted come back (so I'm guessing it doesn't really delete from the list?.
This is at the start of my code at the top:
List<Layer> layers = new List<Layer>();
This is my Layers Class:
public class Layer
{
private Image mLayerData = null;
private string mLayerName = "Layer";
public string LayerName
{
get { return mLayerName; }
set { mLayerName = value; }
}
public Image LayerData
{
get { return mLayerData; }
}
public Layer(int width = 500, int height = 500, string layername = "Layer")
{
mLayerData = new Bitmap(width, height);
mLayerName = layername;
}
}
and this is my addLayer Function:
private void addNewLayer()
{
string layerName = "Layer";
layerName += layers.Count;
// Create a default layer in our stack of layers
layers.Add(new Layer(pictureBox1.Width, pictureBox1.Height, layerName));
// Make the picture box talk to the default layer
pictureBox1.Image = layers[0].LayerData;
pictureBox1.Invalidate();
// Update the list of layers
listLayers.Items.Clear();
foreach(Layer l in layers)
{
listLayers.Items.Add(l.LayerName);
}
listLayers.SelectedIndex = listLayers.Items.Count - 1;
}
for my deleteLayer function I have this:
private void deleteLayer()
{
listlayers.Items.RemoveAt(listlayers.SelectedIndex);
}
A better approach would be to use BindingList, which is supports Data Binding and use DataSource property of Listbox to bind the collection.
For example,
BindingList<Layer> layers = new BindingList<Layer>();
listBox.DataSource = layers;
listBox.DisplayMember = nameof(Layer.LayerName);
Also, note that instead of binding/add names of Layer to the Listbox, you should instead bind the Collection of Layer and use the DisplayMember property to ensure the LayerName is displayed in the Listbox.
Now you could Add to the listbox as the following
var layer = new Layer(pictureBox1.Width, pictureBox1.Height, layerName);
layers.Add(newLayer);
Remove
layers.Remove((Layer)listBox.SelectedItem);
The BindingList would refresh the Listbox by itself.

Access data in datagridview from a class

I've read a lot of topics on this issue but I'm not finding an answer. I'm fairly new to this so please bear with me.
I'm trying to pass values from datagridview to a list. And then in a new class I want to make som methods accessing that list. Trouble is that when I pass the datagridview it returns it without content and values which means I can't do anything with it.
The code under ////TESTING//// works like I want. I create an instance of the specified list and it's counting the amount of rows properly, see screenshot.
public List<vertEl> getVertList = new List<vertEl>();
//Opens the file dialog and assigns file path to Textbox
OpenFileDialog browseButton = new OpenFileDialog();
private void browse_Click(object sender, EventArgs e)
{
browseButton.Filter = "Excel Files |*.xlsx;*.xls;*.xlsm;*.csv";
if (browseButton.ShowDialog() == DialogResult.OK)
{
//SOME CODE TO GET DATA FROM EXCEL AND SOME METHODS TO CALCULATE
//VALUES TO PASS TO THE TAB VERTIKALELEMENTER TAB IN MY DATAGRIDVIEW
//VERTIKALELEMENTER IS vertElementerDgv IN MY CODE
////TESTING////
GetVertElementasList TEST = new GetVertElementasList();
getVertList = TEST.vertList(vertElementerDgv);
MessageBox.Show(getVertList.Count.ToString());
}
else return;
}
I now want to do this in a seperate class and call a method from that class to do the same but when I try that with code underneath I do not get the same count as when I have the code in form1 (public partial class BridgeGeometry). It return count of 0. The method foo() is assigned to the button 1 in the form.
class GetKoord
{
public GetVertElementasList getList = new GetVertElementasList();
BridgGeometry obj = new BridgGeometry();
public void foo()
{
var TEST = getList.vertList(obj.vertElementerDgv);
//var TEST = obj.getVertList;
MessageBox.Show(TEST.Count.ToString());
}
}
I also tried to get the values directly from the datagridview but there's nothing in it when I access it from a class which is not the form1/BridgeGeometry class.
Form - screenshot
You could run a loop and store the information with selected rows into a public var with something like this:
string itemOne = dataGridView1.SelectedRows[0].Cells[1].Value + string.Empty;
string itemTwo= dataGridView1.SelectedRows[0].Cells[2].Value + string.Empty;
string itemThree = dgMasterGridBun.SelectedRows[0].Cells[3].Value + string.Empty;
Variables
public var varItemOne = itemOne;
public var varItemTwo = itemTwo;
public var varItemThree = itemThree;
Based on the link I managed to get this working. Probably not the best solution, but a working one. I tried to wrap my head around databinding, listbinding etc. but since the class with the input values are a messy one I gave that up for now. The datagriview input values are a little from lists and some from other datagridview.
MSDN-forum: Accessing Form1 controls from a different class
Explanations are given in the link so I'll just provide how I did it in my program.
If my GetKoord class are like this:
public class GetKoord
{
private BridgGeometry bridgeGeometry;
public GetKoord(BridgGeometry form1)
{
bridgeGeometry = form1;
}
public List<vertElementerParam> getListvertElementer(List<vertElementerParam> theList)
{
//var vertElementerDgv = bridgeGeometry.vertElementerDgv;
GetVertElementasList getVertElementasList = new GetVertElementasList();
List<vertElementerParam> MyDgvListList = new List<vertElementerParam>();
MyDgvListList = getVertElementasList.vertList(bridgeGeometry.vertElementerDgv);
//MessageBox.Show(MyDgvListList.Count.ToString());
theList = MyDgvListList;
return theList;
}
}
then I can get the list in Button1_Click like this, check the screenshot in the first post:
public List<vertElementerParam> getVertList = new List<vertElementerParam>();
private void button1_Click(object sender, EventArgs e)
{
GetKoord getKoord = new GetKoord(this);
List<vertElementerParam> testList = new List<vertElementerParam>();
testList = getKoord.getListvertElementer(getVertList);
MessageBox.Show(testList.Count.ToString());
}

Accessing Data Controls from a different form

i have two forms, form A and form B, to access form B from from A i pass form B in the constructor of form A, that way i am able to access a gridview.
Suggestions were made directing me towards exposing my gridview in a public property and passing it to the other form i want to access it from.
This is my understanding of what was suggested to me:
public RadGridView Grid
{
get { return GridViewDisplay; }
}
then i pass this property to my second form:
Form1 f1 = new form1();
Form2 f2 = new form2(f1.Grid);
This is my issue here:
public void DockAllWindows()
{
SideBar sb = new SideBar();
Summary sm = new Summary();
SalesPoint sp = new SalesPoint(sb, sm); // This is where my issue is, Point A
StartPage start = new StartPage();
radDock.DockControl(sp, (DockPosition.Fill), DockType.Document);
radDock.DockControl(start, (DockPosition.Fill), DockType.Document);
radDock.DockControl(sm, (DockPosition.Right), DockType.ToolWindow);
}
At Point A i am passing an object instance of my summary form to my SalePoint form.
therefore i cannot execute the following code as it would generate an error:
Summary sm = new Summary(sp.Grid); // Error right here
SalesPoint sp = new SalesPoint(sb, sm);
I would like some help getting around the above error.
You need to set the Summary.Grid property after you've created your SalesPoint class.
Summary sm = new Summary();
SalesPoint sp = new SalesPoint(sb, sm);
sm.Grid = sp.Grid.
To be clear you need a public grid property on your SalesPoint class and one on your Summary class. And make you implement the set on the property of the Summary class so you know when someone else changes it.
public RadGridView Grid
{
get { return grid; }
set
{
if (grid != value)
{
grid = value;
// Add any special processing that summary needs to do to pull data from the SalesPoint Grid property.
}
}
}

Trying to Bind List<T> to CheckedListBox in WinForms c#

I am using WinForms C#
Is there any way to get following behavior:
bind List to CheckedListBox
When I add elements to list CheckedList box refereshes
When I change CheckedListBox the list changes
I tried to do the following:
Constructor code:
checkedlistBox1.DataSource = a;
checkedlistBox1.DisplayMember = "Name";
checkedlistBox1.ValueMember = "Name";
Field:
List<Binder> a = new List<Binder> { new Binder { Name = "A" } };
On button1 click:
private void butto1_Click(object sender, EventArgs e)
{
a.Add(new Binder{Name = "B"});
checkedListBox1.Invalidate();
checkedListBox1.Update();
}
But the view does not update .
Thank You.
Change this line:
List<Binder> a = new List<Binder> { new Binder { Name = "A" } };
to this:
BindingList<Binder> a = new BindingList<Binder> { new Binder { Name = "A" } };
It will just work without any other changes.
The key is that BindingList<T> implements IBindingList, which will notify the control when the list changes. This allows the CheckedListBox control to update its state. This is two-way data binding.
Also, you could change these two lines:
checkedListBox1.Invalidate();
checkedListBox1.Update();
to this (more readable and essentially does the same thing):
checkedListBox1.Refresh();
Two things you may wish to look at:
Use a BindingList
Add a BindableAttribute to your Name property
Does your List<Bender> need to be some kind of observable collection, like ObservableCollection<Bender> instead?
The proper way of binding a checked listbox is:
List<YourType> data = new List<YourType>();
checkedListBox1.DataSource = new BindingList<YourType>(data);
checkedListBox1.DisplayMember = nameof(YourType.Name);
checkedListBox1.ValueMember = nameof(YourType.ID);
Note to self.
The issue i have every time binding it is that the properties DataSource, DisplayMember and ValueMember are not suggested by intellisense and i get confused.

Categories