I have 2 labels and a property in user control:
Here is property:
private int _SelectIndex;
[Browsable(true)]
public int SelectIndex { get; set; }
and 2 labels:
Label lbl1, lbl2;
void iControl()
{
lbl1 = new Label();
lbl2 = new Label();
lbl1.Name = "lbl1";
lbl2.Name = "lbl2";
lbl1.Click += lbl_Click;
lbl2.Click += lbl_Click;
this.Controls.Add(lbl1);
this.Controls.Add(lbl2);
}
Click:
void lbl_Click(object sender, EventArgs e)
{
Label selectedlbl = sender as Label;
if(selectedlbl.Name == "lbl1")
this.Select = 1;
else
this.Select = 2;
}
Class Event:
public class SelectEventArgs : EventArgs
{
private int index;
public SelectEventArgs(int index)
{
this.index = index;
}
public int ItemIndex
{
get
{
return index;
}
}
}
Custom event in my control:
public event EventHandler SelectEvent;
protected virtual void OnSelectEvent()
{
if (SelectEvent!= null)
SelectEvent(this, new SelectEventArgs(this._SelectIndex));
}
I need an event to get and set property value in MainForm as following:
int index = 0;
public Form1()
{
InitializeComponent();
this.icontrol = new iControl();
this.SelectEvent += Select();
}
void Select(object sender, SelectItem e)
{
//use this to set value of Select
this.icontrol.SelectIndex = index;
//and this to get value of Select
index = this.icontrol.SelectIndex;
}
Select is empty.
How to get it to work?
I post here for any one need it:
1.Declare a delegate:
public delegate void SelectIndexEventHandler(object sender, SelectEventArgs e);
public class SelectEventArgs : EventArgs
{
private int index;
public SelectEventArgs(int index)
{
this.index = index;
}
public int ItemIndex
{
get { return index; }
set { index = value; }
}
}
2. declare an event SelectIndexChanged and a method OnSelectIndexChanged:
public event SelectIndexEventHandler SelectIndexChanged;
protected virtual void OnSelectIndexChanged(SelectEventArgs e)
{
if (SelectIndexChanged != null)
SelectIndexChanged(this, e);
}
3.Call it in setter:
public int SelectIndex
{
get { return _SelectIndex; }
set {
_SelectIndex = value;
OnSelectIndexChanged(new SelectEventArgs(value));
}
}
and then MainForm:
this.gListBox1.SelectIndexChanged += icontrol_SelectIndexChanged;
void icontrol_SelectIndexChanged(object sender, SelectEventArgs e)
{
var current = e.ItemIndex;
}
thank again jbmintjb Reza Aghaei.
The code has multiple issues. Consider these tips to solve the issues:
SelecetEvent does't belong to the Form. The event belongs to icontrol.
this.SelectEvent += Select(); is incorrect, you should use:
icontrol.SelectEvent += Select;
When you have a custom event args, you should define the event this way:
public event EventHandler<SelectEventArgs> SelectEvent;
You should raise the event in setter of your property, using OnSelectEvent method which you created.
To learn more about events take a look at C# Handling and Raising Events.
Take a look at the SelectedIndexChanged event on the listbox control, think that is what you are looking for
Related
I'm trying a code which changes the TextBox values when variable mapped with it changes accordingly without using TextBox changed event. I am not finding any clue to where to start please help me.
Here is the code:
public void varChange(TextBox text)
{
String name;
name="sachin";
text.Text = name;
MessageBox.Show("" + text.Text);
}
You can "extend" TextBox :
public class MeTextBox : TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
//base.Text = value; // use it or not .. whatever
MyTextWasChanged();
}
}
void MyTextWasChanged()
{
String name;
name="sachin";
//text.Text = name;
base.Text = name;
MessageBox.Show("" + text.Text);
}
}
If that's not what you're looking for then give some more details and I'll update this answer.
You can use a BindingSource
public partial class Form1 : Form
{
private System.Windows.Forms.BindingSource form1BindingSource;
public string BindedProp { get; set; } //Variable or property binded with TextBox
public Form1()
{
InitializeComponent();
this.form1BindingSource = new System.Windows.Forms.BindingSource(new System.ComponentModel.Container());
this.form1BindingSource.DataSource = typeof(binding.Form1);
this.textBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.form1BindingSource, "BindedProp", true));
this.form1BindingSource.DataSource = this;
}
//add a button control to assing value code event click
private void btAssingValueProperty_Click(object sender, EventArgs e)
{
BindedProp = "Value assigned";
form1BindingSource.ResetBindings(false);
}
//add a other button control to show value code event click
private void btShowValueProperty_Click(object sender, EventArgs e)
{
MessageBox.Show(BindedProp);
}
}
I have got following UserControl that work just fine but when I publish the project I am facing this error.
.. is not allowed here because it does not extend class 'System.Web.UI.UserControl'
How to fix it?
ASCX
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="DataPagerGridView.ascx.cs" Inherits="VerInformes.DataPagerGridView" %>
C#
public partial class DataPagerGridView : GridView, IPageableItemContainer
{
private static readonly object EventTotalRowCountAvailable = new object();
public int MaximumRows
{
get { return this.PageSize; }
}
public int StartRowIndex
{
get { return this.PageSize * this.PageIndex; }
}
public event EventHandler<PageEventArgs> TotalRowCountAvailable
{
add { base.Events.AddHandler(DataPagerGridView.EventTotalRowCountAvailable, value); }
remove { base.Events.RemoveHandler(DataPagerGridView.EventTotalRowCountAvailable, value); }
}
public void SetPageProperties(int startRowIndex, int maximumRows, bool databind)
{
int newPageIndex = (startRowIndex / maximumRows);
this.PageSize = maximumRows;
if (this.PageIndex != newPageIndex)
{
bool isCanceled = false;
if (databind)
{
// create the event arguments and raise the event
GridViewPageEventArgs args = new GridViewPageEventArgs(newPageIndex);
this.OnPageIndexChanging(args);
isCanceled = args.Cancel;
newPageIndex = args.NewPageIndex;
}
// if the event wasn't cancelled change the paging values
if (!isCanceled)
{
this.PageIndex = newPageIndex;
if (databind)
this.OnPageIndexChanged(EventArgs.Empty);
}
if (databind)
this.RequiresDataBinding = true;
}
}
protected virtual void OnTotalRowCountAvailable(PageEventArgs e)
{
EventHandler<PageEventArgs> handler = (EventHandler<PageEventArgs>)base.Events[DataPagerGridView.EventTotalRowCountAvailable];
if (handler != null)
{
handler(this, e);
}
}
protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
int rows = base.CreateChildControls(dataSource, dataBinding);
// if the paging feature is enabled, determine the total number of rows in the datasource
if (this.AllowPaging)
{
// if we are databinding, use the number of rows that were created,
// otherwise cast the datasource to an Collection and use that as the count
int totalRowCount = dataBinding ? rows : ((ICollection)dataSource).Count;
// raise the row count available event
IPageableItemContainer pageableItemContainer = this as IPageableItemContainer;
this.OnTotalRowCountAvailable(new PageEventArgs
(pageableItemContainer.StartRowIndex, pageableItemContainer.MaximumRows, totalRowCount));
// make sure the top and bottom pager rows are not visible
if (this.TopPagerRow != null)
this.TopPagerRow.Visible = false;
if (this.BottomPagerRow != null)
this.BottomPagerRow.Visible = false;
}
return rows;
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
I just reorganized the code so it is correct now.
public class MyDataPagerGridView : GridView, IPageableItemContainer
{
private static readonly object EventTotalRowCountAvailable = new object();
public int MaximumRows
{
get { return this.PageSize; }
}
public int StartRowIndex
{
get { return this.PageSize * this.PageIndex; }
}
public event EventHandler<PageEventArgs> TotalRowCountAvailable
{
add { base.Events.AddHandler(MyDataPagerGridView.EventTotalRowCountAvailable, value); }
remove { base.Events.RemoveHandler(MyDataPagerGridView.EventTotalRowCountAvailable, value); }
}
public void SetPageProperties(int startRowIndex, int maximumRows, bool databind)
{
int newPageIndex = (startRowIndex / maximumRows);
this.PageSize = maximumRows;
if (this.PageIndex != newPageIndex)
{
bool isCanceled = false;
if (databind)
{
// create the event arguments and raise the event
GridViewPageEventArgs args = new GridViewPageEventArgs(newPageIndex);
this.OnPageIndexChanging(args);
isCanceled = args.Cancel;
newPageIndex = args.NewPageIndex;
}
// if the event wasn't cancelled change the paging values
if (!isCanceled)
{
this.PageIndex = newPageIndex;
if (databind)
this.OnPageIndexChanged(EventArgs.Empty);
}
if (databind)
this.RequiresDataBinding = true;
}
}
protected virtual void OnTotalRowCountAvailable(PageEventArgs e)
{
EventHandler<PageEventArgs> handler = (EventHandler<PageEventArgs>)base.Events[MyDataPagerGridView.EventTotalRowCountAvailable];
if (handler != null)
{
handler(this, e);
}
}
protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
int rows = base.CreateChildControls(dataSource, dataBinding);
// if the paging feature is enabled, determine the total number of rows in the datasource
if (this.AllowPaging)
{
// if we are databinding, use the number of rows that were created,
// otherwise cast the datasource to an Collection and use that as the count
int totalRowCount = dataBinding ? rows : ((ICollection)dataSource).Count;
// raise the row count available event
IPageableItemContainer pageableItemContainer = this as IPageableItemContainer;
this.OnTotalRowCountAvailable(new PageEventArgs
(pageableItemContainer.StartRowIndex, pageableItemContainer.MaximumRows, totalRowCount));
// make sure the top and bottom pager rows are not visible
if (this.TopPagerRow != null)
this.TopPagerRow.Visible = false;
if (this.BottomPagerRow != null)
this.BottomPagerRow.Visible = false;
}
return rows;
}
}
public partial class DataPagerGridView : UserControl
{
public MyDataPagerGridView DataPagerGrid = new MyDataPagerGridView();
protected void Page_Load(object sender, EventArgs e)
{
}
}
Hi,
I'm struggling a bit using the ListBox.DataSource and the INotifyPropertyChanged Interface. I checked several posts about this issue already but I cannot figure out, how to update the view of a ListBox if an element of the bound BindingList is changed.
I basically want to change the color of an IndexItem after the content has been parsed.
Here the relevant calls in my form:
btn_indexAddItem.Click += new EventHandler(btn_indexAddItem_Click);
lst_index.DataSource = Indexer.Items;
lst_index.DisplayMember = "Url";
lst_index.DrawItem += new DrawItemEventHandler(lst_index_DrawItem);
private void btn_indexAddItem_Click(object sender, EventArgs e)
{
Indexer.AddSingleURL(txt_indexAddItem.Text);
}
private void lst_index_DrawItem(object sender, DrawItemEventArgs e)
{
IndexItem item = lst_index.Items[e.Index] as IndexItem;
if (item != null)
{
e.DrawBackground();
SolidBrush brush = new SolidBrush((item.hasContent) ? SystemColors.WindowText : SystemColors.ControlDark);
e.Graphics.DrawString(item.Url, lst_index.Font, brush, 0, e.Index * lst_index.ItemHeight);
e.DrawFocusRectangle();
}
}
Indexer.cs:
class Indexer
{
public BindingList<IndexItem> Items { get; }
private object SyncItems = new object();
public Indexer()
{
Items = new BindingList<IndexItem>();
}
public void AddSingleURL(string url)
{
IndexItem item = new IndexItem(url);
if (!Items.Contains(item))
{
lock (SyncItems)
{
Items.Add(item);
}
new Thread(new ThreadStart(() =>
{
// time consuming parsing
Thread.Sleep(5000);
string content = item.Url;
lock (SyncItems)
{
Items[Items.IndexOf(item)].Content = content;
}
}
)).Start();
}
}
}
IndexItem.cs
class IndexItem : IEquatable<IndexItem>, INotifyPropertyChanged
{
public int Key { get; }
public string Url { get; }
public bool hasContent { get { return (_content != null); } }
private string _content;
public string Content {
get
{
return (hasContent) ? _content : "empty";
}
set
{
_content = value;
ContentChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void ContentChanged()
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public IndexItem(string url)
{
this.Key = url.GetHashCode();
this.Url = url;
}
public override bool Equals(object obj)
{
return Equals(obj as IndexItem);
}
public override int GetHashCode()
{
return Key;
}
public bool Equals(IndexItem other)
{
if (other == null) return false;
return (this.Key.Equals(other.Key)) ||
((hasContent || other.hasContent) && (this._content.Equals(other._content)));
}
public override string ToString()
{
return Url;
}
}
Any ideas what went wrong and how to fix it? I'll appreciate any hint...
It seems to me that the control should redraw when it raises the ListChanged event for that item. This will force it to do so:
lst_index.DrawItem += new DrawItemEventHandler(lst_index_DrawItem);
Indexer.Items.ListChanged += Items_ListChanged;
private void Items_ListChanged(object sender, ListChangedEventArgs e)
{
lst_index.Invalidate(); // Force the control to redraw when any elements change
}
So why doesn't it do that already? Well, it seems that the listbox only calls DrawItem if both DisplayMember changed, and if the INotifyPropertyChanged event was raised from the UI thread. So this also works:
lock (SyncItems)
{
// Hacky way to do an Invoke
Application.OpenForms[0].Invoke((Action)(() =>
{
Items[Items.IndexOf(item)].Url += " "; // Force listbox to call DrawItem by changing the DisplayMember
Items[Items.IndexOf(item)].Content = content;
}));
}
Note that calling PropertyChanged on the Url is not sufficient. The value must actually change. This tells me that the listbox is caching those values. :-(
(Tested with VS2015 REL)
I need to bind a GroupBox to a BindingSource, which in turn is bound to the following object:
public class CustomerType
{
public int Id {get; set;}
public string Name {get; set;}
public MemberType MemberType {get; set;}
}
public enum MemberType {Adult, Child}
I followed this answer to create a custom GroupBox. I also set the data bindings as follows:
groupBoxMemberType.DataBindings.Add("Selected", this.bindingSource, "MemberType");
However, when loading an existing object, I get the following exception:
DataBinding cannot find a row in the list that is suitable for all bindings.
The exception occurs when setting the data source:
customerType = customerTypeRequest.Load(id);
bindingSource.DataSource = customerType; //raises exception
What am I missing? Is there an alternative to get radio buttons to bind to a datasource, specifically a BindingSource?
This is the changed code:
[DefaultBindingProperty("Selected")]
public class RadioGroupBox : GroupBox
{
#region events
[Description("Occurs when the selected value changes.")]
public event SelectedChangedEventHandler SelectedChanged;
public class SelectedChangedEventArgs : EventArgs
{
public int Selected { get; private set; }
internal SelectedChangedEventArgs(int selected)
{
this.Selected = selected;
}
}
public delegate void SelectedChangedEventHandler(object sender, SelectedChangedEventArgs e);
#endregion
private int selected;
[Browsable(false)]
[Bindable(BindableSupport.Yes, BindingDirection.TwoWay)]
[Description("The selected value associated with this control."), Category("Data")]
public int Selected
{
get { return selected; }
set
{
int val = 0;
var radioButton = this.Controls.OfType<RadioButton>()
.FirstOrDefault(radio =>
radio.Tag != null
&& int.TryParse(radio.Tag.ToString(), out val) && val == value);
if (radioButton != null)
{
radioButton.Checked = true;
selected = val;
}
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
var radioButton = e.Control as RadioButton;
if (radioButton != null)
radioButton.CheckedChanged += radioButton_CheckedChanged;
}
protected void OnSelectedChanged(SelectedChangedEventArgs e)
{
if (SelectedChanged != null)
SelectedChanged(this, e);
}
private void radioButton_CheckedChanged(object sender, EventArgs e)
{
var radio = (RadioButton)sender;
int val = 0;
if (radio.Checked && radio.Tag != null
&& int.TryParse(radio.Tag.ToString(), out val))
{
selected = val;
OnSelectedChanged(new SelectedChangedEventArgs(selected));
}
}
}
Further to setting the Tag property to the corresponding int value of the enum, you need to subscribe to the SelectedChanged event in your form, eg:
private void radioGroupBoxMemberType_SelectedChanged(object sender, SelectedChangedEventArgs e)
{
customerType.MemberType = (MemberType)e.Selected;
}
Improvements to this class would be:
Inherit from RadioButton and use a new property instead of the Tag property.
Access and set the bindingsource property directly in the control to avoid subscribing to the event.
I read some about DataBinding, mostly complicated things like SQL or whatever XAML and stuff.
All I want my programm to do is, if the "value" of a variable changes just write it in a textbox or label. (using WindowsForms)
So far what I have:
namespace DataBinding_Test
{
public partial class Form1 : Form
{
BindingSource bs = new BindingSource();
Class1 test = new Class1();
public Form1()
{
InitializeComponent();
test.name = "Hello";
bs.DataSource = test;
label1.DataBindings.Add(new Binding("Text", bs, "name", false, DataSourceUpdateMode.OnPropertyChanged));
}
private void button1_Click(object sender, EventArgs e)
{
test.name = textBox1.Text;
}
}
}
Class1 just has a public property name. On startup lable1 will show my "Hello" string. Then on button click the name property will change. On debug I saw the actual DataSource of "bs" contains the new property value, but the label will not show anything...
Is there any realtivly easy way to do this?
The Backround is: periodically there will be a polling of sensor data throug RS232. If the value of one sensor changes I want to show this in label or textbox. Now a backroundthreaded timer will need invokes and stuff to access the GUI thread; thought this would be easier with databinding but seems not :P
Thanks to all, great site, great work! :)
Another way to make things work without implementing INotifyPropertyChanged
class Class1
{
private string name;
public string Name
{
get { return name; }
set
{
//Check if you are assigning the same value. Depends on your business logic
//here is the simplest check
if (Equals(name, value))
return;
name = value;
OnNameChanged();
}
public event EventHandler NameChanged;
protected virtual void OnNameChanged()
{
var handler = NameChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
}
The trick is to have event with the name combined by name of property and Changed suffix and to raise it whenever value of your property is changed
In order your code would work you should implement INotifyPropertyChanged interface in your binded class. Without it your binding simply doesn't know, when the change occures. There you should implenent the logic, according to which you would notify your subscribers about which when something changed in your class (the setter part) and what has changed (PropertyChangedEventArgs). See example for your class:
class Class1: INotifyPropertyChanged
{
private string name = "";
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
And change the property name from "name" to "Name" in your binding:
label1.DataBindings.Add(new Binding("Text", bs, "Name", false, DataSourceUpdateMode.OnPropertyChanged));
// create winforms project on form1 drag a textbox (testbox1)
// and a button (button1) with a button click event handler
// this updates the textbox when the button is clicked
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
MyClass Myobj = new MyClass();
public Form1()
{
InitializeComponent();
/* propertyname, datasource, datamember */
textBox1.DataBindings.Add("Text", Myobj, "Unit");
}
public class MyClass : INotifyPropertyChanged
{
private int unit = 3;
/* property change event */
public event PropertyChangedEventHandler PropertyChanged;
public int Unit
{
get
{
return this.unit;
}
set
{
if (value != this.unit)
{
this.unit = value;
NotifyPropertyChanged("Unit");
}
}
}
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Myobj.Unit += 4;
}
}
}
I created an extension method for this that I would like to share
Usage
private void Form1_Load(object sender, EventArgs e)
{
ResultLabel.Bind(NameTextBox);
WarningLabel.Bind(NameTextBox,i => i.Length == 0 ? "field required!" : "");
SendButton.Bind(NameTextBox, i => SendButton.Enabled = !(i.Length == 0));
}
Extension
public static class Extention
{
public static void Bind(this Control owner, Control dataSource)
{
List<EventInfo> fields = dataSource.GetType().GetEvents().ToList();
int index = fields.FindIndex(item => item.Name == "TextChanged");
if (index >= 0)
{
Control sender = dataSource as Control;
owner.Text = dataSource.Text;
dataSource.TextChanged += delegate (Object o, EventArgs e) { owner.Text = sender.Text; };
}
}
public static void Bind(this Control owner, Control dataSource, Func<string,string> onChange)
{
List<EventInfo> fields = dataSource.GetType().GetEvents().ToList();
int index = fields.FindIndex(item => item.Name == "TextChanged");
if (index >= 0)
{
Control sender = dataSource as Control;
owner.Text = onChange(sender.Text);
dataSource.TextChanged += delegate (Object o, EventArgs e) { owner.Text = onChange(sender.Text); };
}
}
public static void Bind(this Control owner, Control dataSource, Action<string> onChange)
{
List<EventInfo> fields = dataSource.GetType().GetEvents().ToList();
int index = fields.FindIndex(item => item.Name == "TextChanged");
if (index >= 0)
{
Control sender = dataSource as Control;
onChange(sender.Text);
dataSource.TextChanged += delegate (Object o, EventArgs e) { onChange(sender.Text); };
}
}
}
I'm not sure if that is what you want but you can can write whatever you variable contains into the Textbox or Label by using the control.Text property.
textBox1.Text ="Some other Text"
or
string variable = "Hello 2";
textBox1.Text = variable;
Why dou you want to use Databinding? Its mutch easier this way.