I have a User control with a list box.
This User control located on my window.
how can I detect and get selected item from list box in user control?
I previously tried this but when i select an item from list box e.OriginalSource return TextBlock type.
private void searchdialog_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//This return TextBlock type
var conrol= e.OriginalSource;
//I Want something like this
if (e.OriginalSource is ListBoxItem)
{
ListBoxItem Selected = e.OriginalSource as ListBoxItem;
//Do somting
}
}
Or there is any better way that I detect list box SelectionChanged in From My form?
I think the best soution would be to declare an event on your user control, that is fired whenever the SelectedValueChanged event is fired on the listbox.
public class MyUserControl : UserControl
{
public event EventHandler MyListBoxSelectedValueChanged;
public object MyListBoxSelectedValue
{
get { return MyListBox.SelectedValue; }
}
public MyUserControl()
{
MyListBox.SelectedValueChanged += MyListBox_SelectedValueChanged;
}
private void MyListBox_SelectedValueChanged(object sender, EventArgs eventArgs)
{
EventHandler handler = MyListBoxSelectedValueChanged;
if(handler != null)
handler(sender, eventArgs);
}
}
In your window, you listen to the event and use the exposed property in the user control.
public class MyForm : Form
{
public MyForm()
{
MyUserControl.MyListBoxSelectedValueChanged += MyUserControl_MyListBoxSelectedValueChanged;
}
private void MyUserControl_MyListBoxSelectedValueChanged(object sender, EventArgs eventArgs)
{
object selected = MyUserControl.MyListBoxSelectedValue;
}
}
there are a few ways to handle this:
Implement the SelectionChanged event in your usercontrol, and raise a custom event that you handle in your window:
//in your usercontrol
private void OnListBoxSelectionChanged(object s, EventArgs e){
if (e.AddedItems != null && e.AddedItems.Any() && NewItemSelectedEvent != null){
NewItemsSelectedEvent(this, new CustomEventArgs(e.AddedItems[0]))
}
}
//in your window
myUserControl.NewItemsSelected += (s,e) => HandleOnNewItemSelected();
If you use binding or any form of MVVM, you can use a DependencyProperty to bind the selected item to an object in your viewmodel
//in your usercontrol:
public static readonly DependencyProperty CurrentItemProperty =
DependencyProperty.Register("CurrentItem", typeof(MyListBoxItemObject),
typeof(MyUserControl), new PropertyMetadata(default(MyListBoxItemObject)));
public LiveTextBox CurrentItem
{
get { return (MyListBoxItemObject)GetValue(CurrentItemProperty ); }
set { SetValue(CurrentItemProperty , value); }
}
//in your window xaml
<MyUserControl CurrentItem={Binding MyCurrentItem} ... />
Related
I can't active the event mouse enter because label's on top. I try assign the same event, but when I call MyUserControl myUserControl = (MyUserControl)sender; results in error. Here's my code:
foreach (Control ctrl in MyUserControl.Controls)
{
ctrl.MouseEnter += MyUserControl_MouseEnter;
}
private void MyUserControl_MouseEnter(object sender, EventArgs e)
{
MyUserControl myUC = (MyUserControl)sender;
int test = myUC .Codigo;
}
the event (Form_MouseEnter) works when it occurs in the form, but in the components it returns an error like 'System.InvalidCastException'
public partial class MyUserControl : UserControl
{
int g_intCodEquip;
public int Codigo
{
set { g_intCodEquip = value; }
get { return g_intCodEquip; }
}
}
To ensure that any and all child controls of MyUserControl will be correctly handled, we can iterate the control tree of MyUserControl and subscribe to the MouseEnter event.
We route all of these events to a centralized Any_MouseEnter handler which in turn fires a new custom event that the Form1 subscribes to:
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
IterateControlTree();
}
void IterateControlTree(Control control = null)
{
if (control == null)
{
control = this;
}
control.MouseEnter += Any_MouseEnter;
foreach (Control child in control.Controls)
{
IterateControlTree(child);
}
}
private void Any_MouseEnter(object sender, EventArgs e)
{
// Before calling Invoke we need to make sure that
// MyUserControlMouseEnter is not null as would be
// the case if there are no subscribers to the event.
// The '?' syntax performs this important check.
MyUserControlMouseEnter?.Invoke(this, EventArgs.Empty);
}
// A custom event that this custom control can fire.
public event EventHandler MyUserControlMouseEnter;
public int Codigo
{
set
{
test = value;
}
get
{
return test;
}
}
int test = 0;
}
Note: This is a follow-up question to your previous post so I copied over the 'Codigo' property.
OK, so now in the main Form1, we subscribe to the new event fired by MyUserControl. Now the sender is type MyUserControl, the cast succeeds, and the notification works no matter which control the mouse enters.
private void MyUserControl_MouseEnter(object sender, EventArgs e)
{
MyUserControl myUserControl = (MyUserControl)sender;
Debug.WriteLine(
"MouseEnter Detected: " + myUserControl.Name +
" - Value of Codigo is: " + myUserControl.Codigo);
}
As a test runner, we can set up a 4 x 3 array of MyUserControl (the working example MyUserControl contains both a Label and a Button).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
TableLayoutPanel tableLayoutPanel1 = new TableLayoutPanel() { ColumnCount = 4, RowCount = 4, Dock = DockStyle.Fill };
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
Controls.Add(tableLayoutPanel1);
int row, column;
for (int count = 0; count < 12; count++)
{
row = count / 4; column = count % 4;
MyUserControl myUserControl = new MyUserControl();
myUserControl.Name = "MyUserControl_" + count.ToString("D2"); // Name it! (Default is "")
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Subscribe to custom event fired by MyUserControl
myUserControl.MyUserControlMouseEnter += MyUserControl_MouseEnter;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
myUserControl.Codigo = 1000 + count; // Example to set Codigo
tableLayoutPanel1.Controls.Add(myUserControl, column, row);
}
}
}
The behavior follows this 10-second clip.
Change your code to a safe cast.
private void Form_MouseEnter(object sender, EventArgs e)
{
if (sender is MyUserControl myUC)
{
int test = myUC.Codigo;
}
}
Here you can find the documentation: https://learn.microsoft.com/en-us/dotnet/csharp/how-to/safely-cast-using-pattern-matching-is-and-as-operators
Side note:
You have showed us the registration of the event handler MyUserControl_MouseEnter but provided the code for Form_MouseEnter.
The error occurs here at this line:
MyUserControl myUC = (MyUserControl)sender;
...since you wired up the Labels to also run that code, but in those cases the sender variable will be a Label which cannot be cast to MyUserControl.
You could simply grab the .Parent property which should be the usercontrol since you only went one level deep when you originally wired them up. Just check to see if sender is not the MyUserControl type first:
MyUserControl myUC;
if (sender is MyUserControl)
{
myUC = (MyUserControl)sender;
}
else
{
myUC = (MyUserControl)((Control)sender.Parent);
}
I have 2 controls that inherit UserControl
one is a container and the other is a collection of basic text boxes lables etc. hereby labeled as ContainerControl and ComboControl.
ContainerControl contains a List<ComboControl> and a foreach loop that adds them to a FlowLayoutPanel. ComboControl has a button that I would like to be used to clear itself from its parent's List.
I am not sure what the best way of doing this would be. this.parent and cast to ContainerControl or would Dispose() work? I'm fairly sure I could pass a ref to the List, but that sounds needlessly messy...
public partial class ContainerControl : UserControl
{
List<ComboControl> ComboControls = new List<ComboControl>();
...
//procederaly generate and fill ComboControls here
...
foreach (ComboControl value in ComboControls)
{
this.flowLayoutTable.Controls.Add(value);
}
...
}
public partial class ComboControl : UserControl
{
private void BtnDel_Click(object sender, EventArgs e)
{
//what goes here
}
...
}
Along the lines of what Zohar Peled said, something like this to remove a control to avoid leaking resources.
private void cleanup(Control c)
{
foreach(Control child in c.Controls)
cleanup(child);
if (c.Parent != null)
{
c.Parent.Controls.Remove(c);
c.Dispose();
}
}
For scenario like this i would use a custom event to send a delete request to parent control :
your ComboControl with Custom Event :
//Create Custom Event
public delegate void DeleteControlDelegate(object sender);
public partial class ComboControl : UserControl
{
//Custom Event to send Delete request
public event DeleteControlDelegate DeleteControlDelegate;
public ComboControl()
{
InitializeComponent();
}
//Invoke Custom Event
private void OnDeleteControl(object sender)
{
DeleteControlDelegate?.Invoke(sender);
}
private void BtnDel_Click(object sender, EventArgs e)
{
//On ButtonClick send Delete request
OnDeleteControl(this);
}
}
and in your ContainerControl subscribe to event of each ComboControl :
List<ComboControl> _comboControls = new List<ComboControl>();
public ContainerControl()
{
InitializeComponent();
}
private void ContainerControl_Load(object sender, EventArgs e)
{
_comboControls.Add(new ComboControl());
_comboControls.Add(new ComboControl());
_comboControls.Add(new ComboControl());
foreach (ComboControl value in _comboControls)
{
flowLayoutPanel.Controls.Add(value);
//Subscribe to Custom Event here
value.DeleteControlDelegate += Value_DeleteControlDelegate;
}
}
private void Value_DeleteControlDelegate(object sender)
{
//When Raised Delete ComboControl
flowLayoutPanel.Controls.Remove((Control) sender);
}
}
Using c# and winforms with .NET 4.5 I would like to link the Checked property of a menu item with the Visible property of a form.
Changing any of these two attribute would change the other one to keep them synchronized.
Is there an easy and elegant solution to do that ?
Something like this example with a checkbox and a button:
Wire up to the CheckedChanged event
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
var checkBox = sender as CheckBox;
button1.Visible = !checkBox.Checked;
}
Edit:
Ok, i misunderstood.
Although the solution of 'farid' is a clean solution with separation of concerns using viewmodel and model it also adds more complexity to your application.
If you don't want to use this mvvm pattern and put the logic in the code behind you can implement the INotifyPropertyChanged interface to the form that has the visible property (or add a custom event), add a new Visible property that sets the base.visible property (inherited by the Form from the Control class) and raise the PropertyChanged event. in the form that contains the menu item you can wire up to the event and perform the necessary logic to set the checked state or do some other action.
Here is an example:
Form1 code behind:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
}
private Form2 _frm2;
private void Form1_Load(object sender, EventArgs e)
{
_frm2 = new Form2();
_frm2.MdiParent = this;
_frm2.PropertyChanged += _frm2_PropertyChanged;
_frm2.Show();
}
void _frm2_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Visible")
{
showToolStripMenuItem.Checked = _frm2.Visible;
}
}
private void showToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
{
var menuItem = sender as ToolStripMenuItem;
if (_frm2 != null)
_frm2.Visible = menuItem.Checked;
}
}
Form2 code behind:
public partial class Form2 : Form, INotifyPropertyChanged
{
public Form2()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public new bool Visible
{
get
{
return base.Visible;
}
set
{
base.Visible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
}
private void hideButton_Click(object sender, EventArgs e)
{
Visible = false;
}
}
You can use Windows Forms Data Bindings.
Every windows forms control has a DataBindings property that can be used to bind a property in given data source to one of controls properties.
You can structure your code like example below:
This example shows binding from a ViewModel object to a control property. In your specific case you can bind a ViewModel property to two control property.
public partial class StackOverflowForm : Form
{
public ViewModel Model { get; set; }
public Dictionary<string, Control> BindableControls { get; set; }
public StackOverflowForm()
{
Model = new ViewModel();
Model.PropertyChanged += Model_PropertyChanged;
BindableControls = new Dictionary<string, Control>();
Model.Visible = false;
InitializeComponent();
RegisterBinding(boundButton, "Visible", Model, "Visible");
}
void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
foreach (var item in BindableControls)
{
NotifyChange(item.Value, e.PropertyName);
}
}
private void NotifyChange(Control control, string propertyName)
{
button1.DataBindings[propertyName].ReadValue();
}
private void RegisterBinding(Control control, string controlPropertyName, ViewModel _model, string modelPropertyName)
{
control.DataBindings.Add(controlPropertyName, _model, modelPropertyName, true, DataSourceUpdateMode.OnPropertyChanged);
BindableControls[control.Name] = control;
}
private void SetPropertyButton_Click(object sender, EventArgs e)
{
Model.Visible = true;
}
}
public class ViewModel : INotifyPropertyChanged
{
private bool _IsVisible;
public bool Visible
{
get
{
return _IsVisible;
}
set
{
_IsVisible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I have defined a ViewModel object in the form that is used as binding data source for controls. (that have a Visible property)
private void RegisterBinding(Control control, string controlPropertyName, ViewModel _model, string modelPropertyName)
{
control.DataBindings.Add(controlPropertyName, _model, modelPropertyName, true, DataSourceUpdateMode.OnPropertyChanged);
BindableControls[control.Name] = control;
}
Use RegisterBinding method to register simple binding (parameters are simple enough).
'ViewModel' class implements INotifyPropertyChanged interface in System.ComponentModel. This interface adds a PropertyChanged event to call when any property in ViewModel changes.
In form's constructor I added event listener to PropertyChanged event of ViewModel in listener I forced the binding to read new value for each of controls that a binding registered for it. This portion of code refresh's the bound control and changes the visible state of button.
NOTE: In order to answer simplicity I assumed that properties in ViewModel that bound to a control's property has SAME name with destination property in form control. (Mode.Visible and boundButton.Visible). If you want to implement property name mapping for source and destination properties you can use a Dictionary or something to achieve this functionality.
I want that when I click on a menu item, display a context menu with items such as "delete", "rename", etc.
How to bind itself a context menu when you right-click on the menu item?
The first idea jumping in my mind was hook up some MouseDown event on the ToolStripMenuItem and show the second ContextMenuStrip at the mouse position in screen coordinates. But it's not such simple. The problem is doing so will require hooking up the event for every items, on that event somehow showing the second ContextMenuStrip will close the current ContextMenuStrip (even we add some Closing event handler and set e.Cancel = true;). It's a little tricky here. We could think of the MouseDown event of the current ContextMenuStrip but in fact this event is hardly fired because all the items lie on top of the ContextMenuStrip. That made me think of the deeper stage where we can catch the WM_RBUTTONDOWN and run code there. We can customize the ContextMenuStrip to catch that message in WndProc or we can use a custom NativeWindow. I would like to use a NativeWindow here. It's time for the code (working perfectly):
public class NativeContextMenuStrip : NativeWindow
{
public class ShowContextMenuEventArgs : EventArgs {
public ToolStripDropDown ContextMenuToShow {get; set;}
}
public delegate void ShowContextMenuEventHandler(ShowContextMenuEventArgs e);
public event ShowContextMenuEventHandler ShowContextMenu;
private Color previousItemBackColor;
public ToolStripItem SourceItem { get; set; }
bool keepOpen;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x204) {//WM_RBUTTONDOWN
OnShowContextMenu(new ShowContextMenuEventArgs());
}
}
protected virtual void OnShowContextMenu(ShowContextMenuEventArgs e)
{
var handler = ShowContextMenu;
if (handler != null)
{
handler(e);
if (e.ContextMenuToShow != null)
{
ContextMenuStrip toolStrip = (ContextMenuStrip)Control.FromHandle(Handle);
Point client = toolStrip.PointToClient(Control.MousePosition);
SourceItem = toolStrip.GetItemAt(client);
previousItemBackColor = SourceItem.BackColor;
SourceItem.BackColor = SystemColors.MenuHighlight;
e.ContextMenuToShow.Closed -= restoreItemState;
e.ContextMenuToShow.Closed += restoreItemState;
keepOpen = true;
e.ContextMenuToShow.Show(Control.MousePosition);
keepOpen = false;
}
}
}
protected override void OnHandleChange()
{
base.OnHandleChange();
ContextMenuStrip toolStrip = Control.FromHandle(Handle) as ContextMenuStrip;
if (toolStrip != null)
{
toolStrip.Closing += toolStripClosing;
}
}
private void restoreItemState(object sender, EventArgs e)
{
SourceItem.BackColor = previousItemBackColor;
SourceItem.Owner.Show();
}
private void toolStripClosing(object sender, ToolStripDropDownClosingEventArgs e)
{
e.Cancel = keepOpen;
}
}
Usage:: The important event is ShowContextMenu, hook up this event and set the ContextMenuStrip you want to show. That's all. Here is the detail:
public partial class Form1 : Form {
public Form1(){
InitializeComponent();
//suppose you have a main ContextMenuStrip and a sub ContextMenuStrip
//try adding some items for both
ContextMenuStrip = new ContextMenuStrip();
ContextMenuStrip.Items.Add("Item 1");
ContextMenuStrip.Items.Add("Item 2");
//sub ContextMenuStrip
var subMenu = new ContextMenuStrip();
subMenu.Items.Add("Delete");
subMenu.Items.Add("Rename");
ContextMenuStrip.HandleCreated += (s,e) => {
nativeMenu.AssignHandle(ContextMenuStrip.Handle);
nativeMenu.ShowContextMenu += (ev) => {
ev.ContextMenuToShow = subMenu;
};
};
}
NativeContextMenuStrip nativeMenu = new NativeContextMenuStrip();
}
To get the item clicking on which shows the sub ContextMenuStrip, you can access the SourceItem of the NativeContextMenuStrip.
Hello guys i am working on a Windows Form application in .Net C#.
Now i have a User control with a button inside it.
however i had to write the on-click handler in the main form rather than inside the user-control itself.
Now i want to know if there is anyway i can get the User-control object in the Button's on-click Handler. Since i had to make use of them few more times in the same form. I want to know which User-control's Button was click.
User Control
Button
Thank You :)
Here's an example of the UserControl raising a Custom Event that passes out the source UserControl that the Button was clicked on:
SomeUserControl:
public partial class SomeUserControl : UserControl
{
public event ButtonPressedDelegate ButtonPressed;
public delegate void ButtonPressedDelegate(SomeUserControl sender);
public SomeUserControl()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (ButtonPressed != null)
{
ButtonPressed(this); // pass the UserControl out as the parameter
}
}
}
Form1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
someUserControl1.ButtonPressed += new SomeUserControl.ButtonPressedDelegate(SomeUserControl_ButtonPressed);
someUserControl2.ButtonPressed += new SomeUserControl.ButtonPressedDelegate(SomeUserControl_ButtonPressed);
someUserControl3.ButtonPressed += new SomeUserControl.ButtonPressedDelegate(SomeUserControl_ButtonPressed);
}
void SomeUserControl_ButtonPressed(SomeUserControl sender)
{
// do something with "sender":
sender.BackColor = Color.Red;
}
}
You can use event:
public delegate void ButtonClicked();
public ButtonClicked OnButtonClicked;
You can then subscribe the event anywhere, for instance, in your MainForm, you have a user control called demo;
demo.OnButtonClicked +=()
{
// put your actions here.
}
Just walk the .Parent() chain until you find a Control that is the same Type as your UserControl. In the example below, the UserControl is of Type SomeUserControl:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
someUserControl1.button1.Click += new EventHandler(button1_Click);
someUserControl2.button1.Click += new EventHandler(button1_Click);
someUserControl3.button1.Click += new EventHandler(button1_Click);
}
void button1_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
Control uc = btn.Parent;
while (uc != null && !(uc is SomeUserControl))
{
uc = uc.Parent;
}
uc.BackColor = Color.Red;
MessageBox.Show(uc.Name);
}
}