Update BackColor for ListBox - c#

I need to update BackColor of my ListBox by selecting the color from combobox and when clicked on the button.Here is my code
public Data _current;
public Data Current
{
get
{ return _current; }
set
{
_current = value;
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Current"));
}
}
}
public Form1()
{
InitializeComponent();
Items = new ObservableCollection<Data>();
for (int i = 0; i < names.Count; i++)
{
Items.Add(new Data() {Name=names[i].ToString().TrimStart(new char[] { '[', 'C', 'o', 'l', 'o', 'r', ' ', ']' }).TrimEnd(new char[] { ']' }), color = names[i] });
}
comboBox1.DataSource = Items;
comboBox1.DisplayMember ="Name";
Current = Items.First();
}
public List<Color> names = new List<Color>() { Color.Red,Color.Yellow,Color.Green,Color.Blue };
public event PropertyChangedEventHandler PropertyChanged;
private void button1_Click(object sender, EventArgs e)
{
Current = comboBox1.SelectedItem as Data;
listBox1.DataBindings.Add("BackColor", Current, "color", true, DataSourceUpdateMode.OnPropertyChanged);
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
public class Data:INotifyPropertyChanged
{
private Color _color;
public string name { get; set; }
public string Name
{
get
{ return name; }
set
{
name = value;
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
public Color color
{
get
{
return _color;
}
set
{
_color = value;
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("color"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Exception pop's out and says:"This causes two bindings in the collection to bind to the same property."

For solving your original problem "I need to update BackColor of my ListBox by selecting the color from combobox and when clicked on the button" you do not need databinding at all. You can just do it with
private void button1_Click( object sender, EventArgs e )
{
listBox1.BackColor = (Color)comboBox1.SelectedValue;
}
If you really want to do it with databinding, you can bind directly between listbox and combobox (ok, this does not involve a button click):
listBox1.DataBindings.Add( "BackColor", comboBox1, "SelectedValue", true, DataSourceUpdateMode.OnPropertyChanged );
If you need databinding and buttonclick you can bind combobox with form.Current
comboBox1.DataBindings.Add( "SelectedItem", this, "Current", true, DataSourceUpdateMode.OnPropertyChanged );
and update backcolor on button click:
private void button1_Click( object sender, EventArgs e )
{
listBox1.BackColor = Current.Color;
}
Here is the complete example incuding some more improvements:
public class Data : INotifyPropertyChanged
{
public string Name
{
get { return color.Name; }
}
private Color color;
public Color Color
{
get { return color; }
set
{
color = value;
if( PropertyChanged != null )
this.PropertyChanged( this, new PropertyChangedEventArgs( "Color" ) );
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Form1 : Form
{
private ComboBox comboBox1;
private ListBox listBox1;
private Button button1;
public Form1()
{
InitializeComponent();
List<Data> dataList = new List<Data>
{
new Data { Color = Color.Red },
new Data { Color = Color.Yellow },
new Data { Color = Color.Green },
new Data { Color = Color.Blue },
};
comboBox1.DataSource = dataList;
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "Color";
}
public Data Current
{
get { return comboBox1.SelectedItem as Data; }
}
private void button1_Click( object sender, EventArgs e )
{
listBox1.BackColor = Current.Color;
}
private void InitializeComponent()
{
//...
}
}

You can bind any property of a control only once, otherwise you will receive an ArgumentException:
This causes two bindings in the collection to bind to the same
property. Parameter name: binding
In your code you have added data-binding in click event handler of button and each time you click the code executes.
To solve the problem, move data-binding code to Load event of form.
If for any reason you want to add binding in click event of button, to solve the problem you can check if the binding has not been added then add the binding:
if (listBox1.DataBindings["BackColor"] == null)
listBox1.DataBindings.Add("BackColor", Current, "color", true,
DataSourceUpdateMode.OnPropertyChanged);

Related

WPF DataGrid, Keep Focus on cell if e.Cancel = true in CellEditEnding event

I have a datagrid and using it's CellEditEnding event.
By default In CellEditEnging event if I cancel the commit,it allows to move cursor to other cells or other rows.
My query is there any other way if I cancel the edit,user should not be allowed to move other cells or other rows unless he corrects the entered one in CellEditEnging event.
MainWindow.xaml.cs Code
public MainWindow()
{
InitializeComponent();
List<Student> sList = new List<Student>();
sList.Add(new Student() { Name = "Amar" });
sList.Add(new Student() { Name = "Sagar" });
sList.Add(new Student() { Name = "Kiran" });
dg1.ItemsSource = sList;
dg1.CellEditEnding += Dg1_CellEditEnding;
}
private void Dg1_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
TextBox txtBox = e.EditingElement as TextBox;
if (txtBox != null && txtBox.Text.Equals("Amar"))
e.Cancel = true; //my requirement is,once i cancel ,focus should not move to other rows or other cells,it should be remain on this cell
}
}
public class Student : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
I'm having the same question and I have found an answer:
if (hasError)
{
e.Cancel = true;
(sender as DataGrid).Dispatcher.BeginInvoke((Action)(() =>
{
(sender as DataGrid).SelectedIndex = e.Row.GetIndex(); //select current row
((System.Windows.UIElement)e.EditingElement.Parent).Focus(); //focus current cell
}));
}
Try just to clear the TextBox and move focus programmatically. Lower i give you example, how to do it:
private void PlanningDataGrid_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Tab)
{
try
{
if (PlanningDataGrid.FindVisualChildByName<ComboBox>("EventTypeComboBox") != null)
{
Keyboard.Focus(PlanningDataGrid.FindVisualChildByName<ComboBox>("EventTypeComboBox"));
}
if (PlanningDataGrid.FindVisualChildByName<ComboBox>("shopСomboBox") != null)
{
Keyboard.Focus(PlanningDataGrid.FindVisualChildByName<ComboBox>("shopСomboBox"));
}
if (PlanningDataGrid.FindVisualChildByName<ComboBox>("oilfieldСomboBox") != null)
{
Keyboard.Focus(PlanningDataGrid.FindVisualChildByName<ComboBox>("oilfieldСomboBox"));
}
if (PlanningDataGrid.FindVisualChildByName<ComboBox>("wellClusterСomboBox") != null)
{
Keyboard.Focus(PlanningDataGrid.FindVisualChildByName<ComboBox>("wellClusterСomboBox"));
}
if (PlanningDataGrid.FindVisualChildByName<ComboBox>("oilWellСomboBox") != null)
{
Keyboard.Focus(PlanningDataGrid.FindVisualChildByName<ComboBox>("oilWellСomboBox"));
}
//if ()
}
catch (Exception) { }
}
}
Hope, I helped you.
Good luck)

Only the SelectedRows on Datagrid will be affected

I have a datagridview for my table Identification with a Status on the last column.
So let's assume I selected 5 rows out of 10 rows on my datagridview.
What I'm trying to do is that when I clicked a button, only the selected rows will be affected and their status will be change.
I have tried this code and other codes and none of them seems to work. I'm new in c#, so is there anybody that could help me?
private void button_Click(object sender, EventArgs e)
{
int count = dataGridView1.SelectedRows.Count;
for (int i = count-1; i >=0; i--)
{
if (i == dataGridView1.SelectedRows.Count)
{
Identification it = new Identification();
it.Status = "ACTIVE";
Repository.Identification_UpdateStatus(it);
}
}
}
You might want to loop through
dataGridView1.SelectedRows to get each DataGridViewRow object
Code:
foreach(DataGridViewRow row in dataGridView1.SelectedRows)
{
// implement your logic here
// update selected rows by making changes to 'row' object
}
The PROPER way to do this would be to use DataBinding. Since you're using domain objects such as 'Identification' it would be a proper fit here.
public partial class Form1 : Form
{
//Your form
public Form1()
{
InitializeComponent();
//Wrap your objects in a binding list before setting it as the
//datasource of your datagrid
BindingList<Identification> ids = new BindingList<Identification>
{
new Identification() { status="NEW" },
new Identification() { status="NEW" },
new Identification() {status="NEW" },
};
dataGridView1.DataSource = ids;
}
private void btnChangeStatus_Click(object sender, EventArgs e)
{ //Where the actual status changing takes place
foreach (DataGridViewRow row in dataGridView1.SelectedRows)
{
var identifaction = row.DataBoundItem as Identification;
identifaction.status = "VERIFIED";
}
}
//Model: Class that carries your data
class Identification: INotifyPropertyChanged
{
private string _status;
public string status
{
get { return _status; }
set
{
_status = value;
NotifyPropertyChanged("status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}

c# winforms: how to catch checkbox.checked event in DGV with a bindingsource?

Below is a toy program to illustrate the problem I'm having in my real application. It's a DataGridView with a BindingSource to a list of objects of a class.
The problem is: When I click a CheckBox, the CheckBox visibly changes immediately but the invitation_property_changed() method isn't called until I click on some other cell. I need to get the equivalent of the Checkbox.Checked event as soon as it occurs so I can update another control in the UI. (If I change an OTHERS cell, it's natural for the user to hit Enter which triggers the PropertyChanged event - so this one works naturally for the user.)
Here's a screenshot, fwiw:
Here's my toy code:
namespace dgv_binding_test {
public partial class Form1 : Form {
BindingSource bindingsource_invitations = new BindingSource();
class an_invitation : INotifyPropertyChanged {
static int lastID = 0;
int _id;
string _name;
bool _rsvp;
int _others;
public int id() {
return _id;
}
public string NAME {
get { return _name; }
set {
_name = value;
NotifyPropertyChanged("NAME");
}
}
public bool RSVP {
get { return _rsvp; }
set {
_rsvp = value;
NotifyPropertyChanged("RSVP");
}
}
public int OTHERS {
get { return _others; }
set {
_others = value;
NotifyPropertyChanged("OTHERS");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public an_invitation(string a_name, bool a_rsvp, int others) {
_id = lastID++;
_name = a_name;
_rsvp = a_rsvp;
_others = others;
}
private void NotifyPropertyChanged(String propertyName = "") {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
List<an_invitation> invitations = new List<an_invitation>();
public Form1() {
InitializeComponent();
an_invitation ai = new an_invitation("harry", true, 3);
ai.PropertyChanged += new PropertyChangedEventHandler(invitation_property_changed);
invitations.Add(ai);
ai = new an_invitation("heidi", false, 0);
ai.PropertyChanged += new PropertyChangedEventHandler(invitation_property_changed);
invitations.Add(ai);
ai = new an_invitation("henry", false, 0);
ai.PropertyChanged += new PropertyChangedEventHandler(invitation_property_changed);
invitations.Add(ai);
ai = new an_invitation("hazel", true, 0);
ai.PropertyChanged += new PropertyChangedEventHandler(invitation_property_changed);
invitations.Add(ai);
BindingList<an_invitation> bindingList = new BindingList<an_invitation>(invitations);
bindingsource_invitations = new BindingSource(bindingList, null);
dataGridView1.DataSource = bindingsource_invitations;
dataGridView1.AutoGenerateColumns = true;
}
private void invitation_property_changed(object sender, PropertyChangedEventArgs e) {
Debug.Write("change for id: " + ((an_invitation)sender).id() + " property: " + e.PropertyName + " change: " + ((an_invitation)sender).NAME + " to: ");
if (e.PropertyName == "NAME") {
Debug.WriteLine(((an_invitation)sender).NAME);
} else if (e.PropertyName == "RSVP") {
Debug.WriteLine(((an_invitation)sender).RSVP.ToString());
} else if (e.PropertyName == "OTHERS") {
Debug.WriteLine(((an_invitation)sender).OTHERS.ToString());
}
}
}
}
Thanks for your consideration.
The easiest solution I've discovered for this type of situation is to handle the DataGridView.CellContentClick and DataGridView.CellContentDoubleClick events. You can keep all the current code you have. All you'd need to add is a single handle for both of these events which ends the cell edit when it is a CheckBox cell. Ending the edit will trigger the value change the same as leaving the cell currently does.
this.dataGridView1.CellContentClick += DataGridView1_CellContentClick;
this.dataGridView1.CellContentDoubleClick += DataGridView1_CellContentClick;
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (this.dataGridView1[e.ColumnIndex, e.RowIndex] is DataGridViewCheckBoxCell)
{
this.dataGridView1.EndEdit();
}
}
Prematurely ending a cell's edit can be problematic - say, if it were a TextBox cell - due to validation. But as it's simply True or False, the point is moot in this case.

Winforms Two-Way Databinding: Force to Update the Field

I am hoping to get some pointers on what I am missing in my code.
I have a text box bound to a object property that is an item in the list, and that value doesnt update on the form if I request another item in the list.
To illustrate with example below:
txtGain value is populated after openJSONRequestFileToolStripMenuItem_Click fuction
Once I select something different in cmbSignals combobox, I expect the txtGain value to become updated since SelectedChannel is updated as well, which in turn updates the selectedindex but it doesn't happen.
Basically I want to have my txtGain value updated based on what I select in the cmbSignals. Obviously the binding is there so that I can modify the value in the text box and have it be updated in the property its bound to.
I suspect that I have to somehow force update the bindings but not sure how to do that. Any help would be appreciated.
public partial class MainForm : Form
{
private MyData req;
public MainForm()
{
InitializeComponent();
cmbSignals.DisplayMember = "Name";
cmbSignals.ValueMember = "Value";
}
private void openJSONRequestFileToolStripMenuItem_Click(object sender, EventArgs e)
{
string text = File.ReadAllText("sample.json");
req = new MyData(JsonConvert.DeserializeObject<SerializedRequest>(text));
cmbSignals.DataSource = req.SignalNames;
cmbSignals.SelectedValue = req.SelectedChannel;
SetBindings();
}
private void SetBindings()
{
txtGain.DataBindings.Add(new Binding("Text", req, "Gain"));
}
private void cmbSignals_SelectedValueChanged(object sender, EventArgs e)
{
req.SelectedChannel = Convert.ToInt32(cmbSignals.SelectedValue);
}
}
public class MyData : INotifyPropertyChanged
{
private SerializedRequest Data = new SerializedRequest();
private int selectedIndex = 0;
public int SelectedChannel
{
get
{
return selectedIndex + 1;
}
set
{
this.selectedIndex = value - 1;
}
}
public string Gain
{
get
{
return Data.signals[selectedIndex].gain;
}
set
{
Data.signals[selectedIndex].gain = value;
OnPropertyChanged("Gain");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public List<SignalsCmbItem>SignalNames
{
get
{
List<SignalsCmbItem>channels = new List<SignalsCmbItem>();
for(int i = 0; i<Data.signals.Count;i++)
{
channels.Add(new SignalsCmbItem { Value = i + 1, Name = i+1 + " - " + Data.signals[i].label });
}
return channels;
}
}
}
Pretty annoying "feature", isn't it?.
But no worries, to get around this, add one line of code inside your cmbSignals_SelectedValueChanged(sender, e) method, after you change value of req.SelectedChannel.
txtGain.BindingContext = new BindingContext();

.NET Propertygrid refresh trouble

Property grid do not show new value of selected object.
For example:
o.val = "1";
pg.SelectedObject = o;
o.val = "2";
pg.Refresh();
The property in property grid is still "1";
It is changing only if you click on this property.
Or like that:
o.val = "1";
pg.SelectedObject = o;
o.val = "2";
pg.SelectedObject = o;
but in this case focus will be changed to PropertyGrid.
As I told you in my comment, your code is not enough to understand your issue. Presented like this it should work. Here is mine that works well:
public class Target
{
private int _myInt = 1;
public int MyInt { set; get; }
public static Target target = new Target();
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Button button = new Button()
{
Text = "Click me",
Dock = DockStyle.Bottom
};
Form form = new Form
{
Controls = {
new PropertyGrid {
SelectedObject = Target.target,
Dock = DockStyle.Fill,
},
button
}
};
button.Click += new EventHandler(button_Click);
Application.Run(form);
}
static void button_Click(object sender, EventArgs e)
{
Target.target.MyInt = 2;
Form form = Form.ActiveForm;
(form.Controls[0] as PropertyGrid).Refresh();
}
}
The call to Refresh() actually rebuilds the grid. Remove it and you will see the change only when you click the property.
Cause you just not gave some code, here is a working example.
Just put a Button and a PropertyGrid onto a form.
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication
{
public partial class FormMain : Form
{
Random rand;
MyObject obj;
public FormMain()
{
InitializeComponent();
rand = new Random();
obj = new MyObject();
propertyGrid1.SelectedObject = obj;
}
private void button1_Click(object sender, EventArgs e)
{
obj.MyValue = rand.Next();
obj.IsEnabled = !obj.IsEnabled;
obj.MyText = DateTime.Now.ToString();
propertyGrid1.Refresh();
}
}
public class MyObject : INotifyPropertyChanged
{
private int _MyValue;
public int MyValue
{
get
{
return _MyValue;
}
set
{
_MyValue = value;
NotifyPropertyChanged("MyValue");
}
}
private string _MyText;
public string MyText
{
get
{
return _MyText;
}
set
{
_MyText = value;
NotifyPropertyChanged("MyText");
}
}
private bool _IsEnabled;
public bool IsEnabled
{
get
{
return _IsEnabled;
}
set
{
_IsEnabled = value;
NotifyPropertyChanged("IsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}

Categories

Resources