I would like to create a simple custom control (actually a type of button). I have created the control but the step I am missing is how to add the binding. The control code I have looks like this:
public partial class PopUpButton : UserControl
{
public PopUpButton()
{
InitializeComponent();
_checked = false;
DrawButton();
}
public delegate void ChangedEventHandler(object sender, EventArgs e);
public event ChangedEventHandler OnValueChanged;
private bool _checked;
private String _text;
private void UserControl1_Resize(object sender, EventArgs e)
{
DrawButton();
}
[Bindable(true)]
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
DrawButton();
if (OnValueChanged != null)
{
OnValueChanged.Invoke(this, new EventArgs());
}
}
}
[Bindable(true)]
public String DisplayText
{
get
{
return _text;
}
set
{
_text = value;
DrawButton();
}
}
private void DrawButton()
{
// do some stuff
}
private void PopUpButton_Click(object sender, EventArgs e)
{
_checked = !_checked;
DrawButton();
if (OnValueChanged != null)
{
OnValueChanged.Invoke(this, new EventArgs());
}
}
}
The call to bind to the control looks like this:
regControl1.DataBindings.Clear();
regControl1.DataBindings.Add("Checked", CustomButton1, "Checked");
I know that I need to define a data source and member but cannot see how to implement this. When the above binding is called then regControl1 updates with the value of "Checked" however the function "OnValueChanged" is always null so the binding has failed, thus when "Checked" changes "regControl1" is not updated.
Ideas anyone?
Finally got something working. This solution handles a click both on the body and on the label and re-sizes the component during design. I was almost there but hope this helps:
public partial class PopUpButton : UserControl, INotifyPropertyChanged
{
public PopUpButton()
{
InitializeComponent();
_checked = false;
DrawButton();
}
private bool _checked;
private String _text;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler ButtonClick;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
protected void Button_Click(object sender, EventArgs e)
{
_checked = !_checked;
DrawButton();
NotifyPropertyChanged("Checked");
if (this.ButtonClick != null)
this.ButtonClick(this, e);
}
private void UserControl1_Resize(object sender, EventArgs e)
{
DrawButton();
}
[Bindable(true)]
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
DrawButton();
NotifyPropertyChanged("Checked");
}
}
[Bindable(true)]
public String DisplayText
{
get
{
return _text;
}
set
{
_text = value;
DrawButton();
}
}
private void HandleClick( )
{
_checked = !_checked;
DrawButton();
NotifyPropertyChanged("Checked");
}
private void DrawButton()
{
//do some drawing stuff here
}
private void PopUpButton_Resize(object sender, EventArgs e)
{
DrawButton();
}
}
Related
I have a simple TabControl inside my UserControl which is embedded in the parent control like this:
<StackPanel>
<myviews:myusercontrol></myviews:myusercontrol>
</StackPanel>
Inside myusercontrol I have the following implementation:
public partial class MyUserControl
{
public delegate void SelectedTabItemHandler(string selectedTabItem);
public event SelectedTabItemHandler TabItemSelected;
public MyUserControl()
{
InitializeComponent();
}
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(TabItemSelected != null)
{
TabItemSelected("some selected Item");
}
}
}
How can I hook up the TabItemSelected event in my XAML like this:
<myviews:myusercontrol TabItemSelected="TabSelectionChanged"></myviews:myusercontrol>
UPDATE 2:
For passing custom parameters I am doing something like this:
public partial class PatientEditTabView : UserControl
{
// Routed Event
public static readonly RoutedEvent SelectedTabItemEvent = EventManager.RegisterRoutedEvent("TabItemSelected", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PatientEditTabView));
public PatientEditTabView()
{
InitializeComponent();
}
public event RoutedEventHandler TabItemSelected
{
add { AddHandler(SelectedTabItemEvent, value); }
remove { RemoveHandler(SelectedTabItemEvent, value); }
}
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedTabRoutedEventArgs eventArgs = new SelectedTabRoutedEventArgs(PatientEditTabView.SelectedTabItemEvent,tabControl.SelectedValue as String);
RaiseEvent(eventArgs);
}
}
UPDATE 3:
public class SelectedTabRoutedEventArgs : RoutedEventArgs
{
private readonly String _selectedItem;
public SelectedTabRoutedEventArgs(RoutedEvent routedEvent,string selectedItem)
: base(routedEvent)
{
_selectedItem = selectedItem;
}
public string SelectedItem
{
get { return _selectedItem; }
}
}
You need to create custom Routed event in case you want to hook handler from XAML.
Refer to the article here - How to create custom routed event.
public partial class MyUserControl
{
public static readonly RoutedEvent TabItemSelectedEvent =
EventManager.RegisterRoutedEvent("TabItemSelected",
RoutingStrategy.Bubble, typeof(RoutedEventHandler),
typeof(MyUserControl));
public event RoutedEventHandler TabItemSelected
{
add { AddHandler(TabItemSelectedEvent, value); }
remove { RemoveHandler(TabItemSelectedEvent, value); }
}
void RaiseTabItemSelectedEvent()
{
RoutedEventArgs newEventArgs =
new RoutedEventArgs(MyUserControl.TabItemSelectedEvent);
RaiseEvent(newEventArgs);
}
private void TabControl_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
RaiseTabItemSelectedEvent();
}
}
Note: You need to create class deriving from RoutedEventArgs in case you want to pass some custom data like string in your case and pass your custom event args while raising an event.
UPDATE:
Like I said you have to create derived class from RoutedEventArgs in case want to pass string value. This is how it will go:
public class SelectedTabRoutedEventArgs : RoutedEventArgs
{
private readonly string selectedItem;
public SelectedTabRoutedEventArgs(RoutedEvent routedEvent,
string selectedItem)
:base(routedEvent)
{
this.selectedItem = selectedItem;
}
public string SelectedItem
{
get
{
return selectedItem;
}
}
}
and update other methods like this:
void RaiseTabItemSelectedEvent(string selectedItem)
{
SelectedTabRoutedEventArgs newEventArgs =
new SelectedTabRoutedEventArgs(SampleUserControl.TabItemSelectedEvent,
selectedItem);
RaiseEvent(newEventArgs);
}
private void TabControl_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
RaiseTabItemSelectedEvent(tabControl.SelectedValue.ToString());
}
Also you target event handler in window will look like this:
private void TabSelectionChanged(object sender, RoutedEventArgs e)
{
SelectedTabRoutedEventArgs args = (SelectedTabRoutedEventArgs)e;
string selectedItem = args.SelectedItem;
}
The issue I am having is that when I update my object, the ListBox automatically removes and then re-adds the object to the list, thus calling the index and value changed events. I was able to prevent this by creating a custom ListBox control and when the PropertyChangedEvent was called, I would raise a flag that would prevent those events in the base class from being called. What is happening now is that my entire reference is being replace by a new reference and unless I re-select the item in the ListBox, I have the wrong reference.
What I basically want to do, is to change the Display Value in my object and then have it update only the text in the list box. I do not want it to remove and to re-add the object/reference/whatever it does. It's quite annoying.
Here is the example code I am working with...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.myListBox1.SelectedValueChanged += this.onchange;
}
private void Form1_Load(object sender, EventArgs e)
{
this.myListBox1.Add(new strobj("z"));
this.myListBox1.Add(new strobj("a"));
this.myListBox1.Add(new strobj("b"));
this.myListBox1.Add(new strobj("f"));
this.myListBox1.Add(new strobj("n"));
this.myListBox1.Add(new strobj("h"));
this.myListBox1.Add(new strobj("p"));
this.myListBox1.Add(new strobj("t"));
this.myListBox1.Add(new strobj("c"));
this.myListBox1.Add(new strobj("q"));
}
private void onchange(object sender, EventArgs e)
{
MessageBox.Show("Hello World");
}
int i = 0;
private void button1_Click(object sender, EventArgs e)
{
if (this.myListBox1.SelectedItem != null)
{
strobj item = (strobj)this.myListBox1.SelectedItem;
item.Name1 = i++.ToString();
}
}
}
public partial class MyListBox
{
public MyListBox()
{
InitializeComponent();
}
public void Add(strobj item)
{
item.OnNameChanged += this.MyDispalyMemberChanged;
this.Items.Add(item);
}
bool refreshing = false;
public void MyDispalyMemberChanged(strobj itemChanged)
{
this.refreshing = true;
this.RefreshItem(this.Items.IndexOf(itemChanged));
this.refreshing = false;
}
protected override void OnSelectedValueChanged(EventArgs e)
{
if (!this.refreshing)
{
base.OnSelectedValueChanged(e);
}
}
}
class strobjCollection : List<strobj>
{
NameChangeEventHandler NameChangedEvent;
}
delegate void NameChangeEventHandler(strobj sender);
public class strobj
{
internal NameChangeEventHandler OnNameChanged;
private string _Name1;
public string Name1
{
get { return this._Name1; }
set
{
this._Name1 = value;
if (this.OnNameChanged != null)
{
this.OnNameChanged(this);
}
}
}
public int i = 0;
public string str = "p";
public strobj(string name)
{
this._Name1 = name;
}
public strobj()
{
this._Name1 = "You did not create this object";
}
public override string ToString()
{
return this._Name1;
}
}
This is what the INotifyPropertyChanged interface was made for.
Instead of raising your custom event, you'd raise the PropertyChanged event with the name of the property you changed set in the event args and the listbox would update.
See MSDN.
I am having an issue with the propertyGrid.
I have a class called DummySettings that is mapped to the propertyGrid
I have a property "Name"
When typing something in the propertygrid EG the Name property I would like to raise an event that "TextChanged"
Despite implementing the INotifyPropertyChanged event and raising it and despite hooking all sorts of events I can think of on the propertyGrid
none of this events fires when A text changes.
Am I missing the obvious?
EDITED
Psuedo Code
public class DummySettings : INotifyPropertyChanged
{
private string name;
[DisplayName("Name")]
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
//UserControl
public partial class DummyControl : UserControl
{
private DummySettings settings;
///Constructor
public DummyControl()
{
InitializeComponent();
settings = new DummySettings();
propertyGrid1.SelectedObject = settings;
settings.PropertyChanged += OnDummyPropertyChanged;
//All the events I have hooked up but not firing when text is changed
private void OnDummyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
btnToEnable.Enabled = HasName();
}
private void propertyGrid1_Leave(object sender, EventArgs e)
{
btnToEnable.Enabled = HasName();
}
private void propertyGrid1_Validating(object sender, CancelEventArgs e)
{
btnToEnable.Enabled = HasName();
}
private void propertyGrid1_SelectedObjectsChanged(object sender, EventArgs e)
{
btnToEnable.Enabled = HasName();
}
private void propertyGrid1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
btnToEnable.Enabled = HasName();
}
private bool HasName()
{
return settings.Name.IsNotNullOrEmpty();
}
You can use PropertyValueChanged event to get notified of selected object property changed in PropertyGrid. So, when you are changed Name property value from "Foo" to "Bar", this event will be raised:
void propertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
// previous value is e.OldValue, "Foo"
// property name is e.ChangedItem.Label, "Name"
// new value is e.ChangedItem.Value, "Bar"
}
I have null exception error on sending ValueChanged() event when creating this custom control and testing it in a client:
Source of custom control:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
namespace customevent
{
[DefaultEvent("ValueChanged")]
public partial class UserControl1 : UserControl
{
private int m_value;
public delegate void ValueChangedHandler();
[Category("Action")]
[Description("Value changed.")]
public event ValueChangedHandler ValueChanged;
public int Value
{
get { return m_value; }
set {
m_value = value;
ValueChanged();
}
}
public UserControl1()
{
InitializeComponent();
}
public UserControl1(int iValue)
{
this.Value = iValue;
InitializeComponent();
}
}
}
Then in test form:
private void Form1_Load(object sender, EventArgs e)
{
userControl11.Value = 100;
}
private void userControl11_ValueChanged()
{
MessageBox.Show(userControl11.Value.ToString());
}
Or instead of form_load, do this in constructor:
private void InitializeComponent()
{
this.userControl11 = new customevent.UserControl1(100);
You should declare the event handling as this:
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(object sender, EventArgs e)
{
if (ValueChanged != null)
{
ValueChanged(sender, e);
}
}
public int Value
{
get { return m_value; }
set {
if (m_value == value) return;
m_value = value;
OnValueChanged(this, EventArgs.Empty);
}
}
PS: there is an interface INotifyPropertyChanged, you should use this instead to comply with standard .NET databinding rules.
You're not checking for nulls:
public int Value
{
get { return m_value; }
set {
m_value = value;
if(ValueChanged != null)
{
ValueChanged();
}
}
}
Furthermore, you're also not hooking into this event in your Form:
private void Form1_Load(object sender, EventArgs e)
{
userControl1.ValueChanged += userControl11_ValueChanged;
userControl11.Value = 100;
}
private void userControl11_ValueChanged()
{
MessageBox.Show(userControl11.Value.ToString());
}
Simon got you almost there:
protected virtual void OnValueChanged(object sender, EventArgs e)
{
var tmp = ValueChanged;
if (tmp != null)
{
tmp(sender, e); // With the tmp, we won't explode if a subscriber changes the collection of delegates underneath us.
}
}
This question is related to c#.The scenario is that when i click the button the operations like File reading,Data manipulation ,and file dumping are going to be happened.After the completion of each operation i will update the status(i.e,File reading completed,data manipulation completed) in the label which is in UI(FORM-frmTesting)
The button click event is
namespace frmTesting
{
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 l_objClass1 = new class1();
l_objClass1.DoOperation();
}
}
public class class1
{
public int DoOperation()
{
ReadTextFile();
ParsingData();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
return 0;
}
private int ParsingData()
{
// Data manipulation
return 0;
}
private int SaveTextFile()
{
// save the file
return 0;
}
}
}
Is it possible by using Delegates and Events....if you have any queries plz revert back me
You'll have to modify class1 to broadcast events that other classes can listen to:
public class class1
{
// Not necessary, but will allow you to add custom EventArgs later
public delegate void StatusChangedEventHandler(object sender, EventArgs e);
public event StatusChangedEventHandler FileRead;
public event StatusChangedEventHandler FileParsed;
public event StatusChangedEventHandler FileSaved;
public int DoOperation()
{
ReadTextFile();
ParsingData();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
OnFileRead(EventArgs.Empty);
return 0;
}
private int ParsingData()
{
// Data manipulation
OnFileParsed(EventArgs.Empty);
return 0;
}
private int SaveTextFile()
{
// save the file
OnFileSaved(EventArgs.Empty);
return 0;
}
protected virtual void OnFileRead(EventArgs e)
{
if(FileRead != null)
FileRead(this, e);
}
protected virtual void OnFileParsed(EventArgs e)
{
if(FileParsed != null)
FileParsed(this, e);
}
protected virtual void OnFileSaved(EventArgs e)
{
if(FileSaved != null)
FileSaved(this, e);
}
}
And then have your form listen for those events and change its label appropriately:
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 l_objClass1 = new class1();
l_objClass1.FileRead +=
delegate { lblStatus.Text = "File Read..."; };
l_objClass1.FileParsed +=
delegate { lblStatus.Text = "File Parsed..."; };
l_objClass1.FileSaved +=
delegate { lblStatus.Text = "File Saved..."; };
l_objClass1.DoOperation();
}
}
The short answer is yes. You add events to class1 and add handlers to Form1 with the appropriate logic. Below is a sample of how to do this
public partial class Form1 : Form
{
private void button1_Click_1(object sender, EventArgs e)
{
class1 obj = new class1();
obj.FileReadingComplete += HandleFileReadingComplete;
obj.DataManipulationComplete += HandleDataManipulationComplete;
obj.DoOperation();
obj.FileReadingComplete -= HandleFileReadingComplete;
obj.DataManipulationComplete -= HandleDataManipulationComplete;
}
private void HandleFileReadingComplete(object sender, EventArgs args){
//code here
}
private void HandleDataManipulationComplete(object sender, EventArgs args)
{
//code here
}
}
public class class1
{
public event EventHandler FileReadingComplete;
public event EventHandler DataManipulationComplete;
public int DoOperation()
{
ReadTextFile();
OnFileReadingComplete();
ParsingData();
OnDataManipulationComplete();
SaveTextFile();
return 0;
}
private int ReadTextFile()
{
//Read the text File
return 0;
}
private int ParsingData()
{
// Data manipulation
return 0;
}
private int SaveTextFile()
{
// save the file
return 0;
}
public void OnFileReadingComplete()
{
EventHandler handler = FileReadingComplete;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
public void OnDataManipulationComplete()
{
EventHandler handler = DataManipulationComplete;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
}