I'm working on a simple IM program as a personal project, and I've hit a bit of a snag. It's really more of a cosmetic thing, but I'm having some trouble with it. I've got a sidebar that lists all of a user's contents in the main window, and I'd like to set it up so that when a user clicks on a contact name, a tab opens in the chat area of the main window with a chat session opened with that contact. The really important part of this is for me to be able to get the UIElement, in this case a Label, that kicked off the MouseDoubleClick event. Once I can access this, I can access the information that I need to make the connection. Unfortunately, I'm a bit rusty with mouse events, and can't figure out how to get back to the Label once the event has been fired. My source code for programmatically creating the label is as follows:
foreach (ContactInfo contact in ContactList)
{
Label currentContact = new Label();
currentContact.Content = contact.ContactName.ToString() + " (" + contact.MachineName.ToString() + ")";
currentContact.MouseDoubleClick += new MouseButtonEventHandler(ContactDoubleClickHandler);
StckPnl_Contacts.Children.Add(currentContact);
}
And the (currently empty) handler is this:
public void ContactDoubleClickHandler(object sender, MouseButtonEventArgs e)
{
}
Am I going about this the wrong way? Any help would be appreciated.
You can inspect the sender (first casting it to the type) to get the element that triggered the event:
Label targetLabel = sender as Label;
if (targetLabel != null)
{
// Do something. I recommend not doing a direct cast in case someone in the future hooks another control type to the event handler.
}
You can use either of following to access the sender details
public void ContactDoubleClickHandler(object sender, MouseButtonEventArgs e)
{
var uiElement = (UIElement) sender; // cast it to UIElement
}
public void ContactDoubleClickHandler(object sender, MouseButtonEventArgs e)
{
var dp = (DependencyObject) sender; // cast it to dependency object.
}
Actually, the sender is your Label, you just have to transform it using:
Label contact = sender as Label;
Be sure to check if contact is null though, before performing any further operations.
Related
In a WPF application, suppose there are 'n'-number of pictures of type image and if on clicking any picture (i.e. of type image), its visibility should collapse. Now a normal way to do this would be to to write the code to collapse for every 'Click' event for every picture.
Is there an alternative way so that the application can understand that whenever any UIelement(picture) of type image is clicked then that particular element(picture) should collapse?
I want to reduce the code, how can I achieve this?
You can take advantage of the fact that these are Routed Events, and set a single handler on a parent element.
This allows a single event handler to handle all events of the child controls. The OriginalSource property of the event args will provide the UIElement that was clicked, if, for example, you subscribed to UIElement.MouseLeftButtonDown or a similar "shared" event.
You would do this by adding, in your XAML, to your container:
<Grid UIElement.MouseLeftButtonDown="CommonClickHandler">
<!-- Your elements here -->
Then, in your code behind:
private void CommonClickHandler(object sender, MouseButtonEventArgs e)
{
Image picture = e.OriginalSource as Image; //OriginalSource is the original element
if (picture != null)
picture.Visibility = Visibility.Collapsed;
}
You can add global handler using EventManager.RegisterClassHandler like this -
public MainWindow()
{
InitializeComponent();
EventManager.RegisterClassHandler(typeof(Image), Image.MouseDownEvent,
new RoutedEventHandler(OnMouseDown));
}
private void OnMouseDown(object sender, RoutedEventArgs e)
{
(sender as Image).Visibility = System.Windows.Visibility.Collapsed;
}
You can register the method you are using on multiple event handlers and get access to the particular control by using Object sender parameter and casting it to the type of control you are using.
myControl.Click += new EventHandler(myGenericClickMethod);
public void myGenericClickMethod(Object sender, EventArgs e)
{
Image myImage = (Image) sender;
myImage..Visibility = System.Windows.Visibility.Collapsed;
}
I'm trying to build a simple interface that allows users to drop files into a listBox to add them to a process, and to drag them out to remove them. Everything is working fine, but I'd like to add one feature to make it just a tad more sophisticated.
Right now, I have the removal of the item tied to the DragLeave event, which means that as soon as the mouse leaves the box, the item is removed. But I'd like for users to be able to change their minds. In other words, if they realize they're dragging the wrong file out, I'd like them to be able to move the mouse back into the listBox and release the mouse to cancel the action. I'm thinking that means I need to be able to capture the MouseUp event instead of the DragLeave event. But that hasn't been successful so far.
Below is the code I'm currently using for removing files dragged out. How can I modify to keep the files from being removed form the list until the user lets the mouse button go?
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
if (listBox1.Items.Count == 0)
{
return;
}
int index = listBox1.IndexFromPoint(e.X, e.Y);
string s = listBox1.Items[index].ToString();
DragDropEffects dde1 = DoDragDrop(s, DragDropEffects.All);
}
private void listBox1_DragLeave(object sender, EventArgs e)
{
ListBox lb = sender as ListBox;
lb.Items.Remove(lb.SelectedItem);
}
Edit 2013/05/16
The comments and answers so far have been useful, but I realize my question isn't clear enough. In this case, I'm displaying a dialog separate from the parent form that is basically as big as the listBox. When someone drags a file out of the list, they're dragging it off the form completely. Have I backed myself into a corner by doing this? I recognize I'm making it harder than it has to be, but I'd still like to see how it would work if it's possible.
Here's a fairly quick hack approach to gaining the functionality you want:
public object lb_item = null;
private void listBox1_DragLeave(object sender, EventArgs e)
{
ListBox lb = sender as ListBox;
lb_item = lb.SelectedItem;
lb.Items.Remove(lb.SelectedItem);
}
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
if (lb_item != null)
{
listBox1.Items.Add(lb_item);
lb_item = null;
}
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
lb_item = null;
if (listBox1.Items.Count == 0)
{
return;
}
int index = listBox1.IndexFromPoint(e.X, e.Y);
string s = listBox1.Items[index].ToString();
DragDropEffects dde1 = DoDragDrop(s, DragDropEffects.All);
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
lb_item = null;
}
Every time the user drags an item out of the box, it's saved temporarily until the user drops it somewhere else or mouses down on a new item in the list.
Note the important part of this is detecting when and where the user let's go of that mouse, which is the rationale behind handling the DragDrop event of Form1, the parent of listBox1.
Depending on the sophistication and density of the rest of your layout, where you handle DragDrop could be much different for you. This is why it's kind of "hacky", but it's also quite simple. It shouldn't matter, though, where or how many times you null lb_item since it pertains only to that specific ListBox.
I suppose another way to do it would be to track the user's mouse states and act accordingly, which may be more appropriate for you if it's inconceivable to handle a lot of DragDrop stuff.
EDIT: If you wanted to be REAL thorough, you could enumerate through every control of the base form using foreach and programmatically append a handler for the DragDrop event to that control, then remove it when done... but that may be getting a little nutty. I'm sure someone has a better approach.
I have a Windows form named Form1 and panel within this form named panel1. I use the panel only to place buttons there so that I can group them and work with them separately from the other buttons in my Form1. For the purpose of my program I need to handle every button click made from the buttons inside panel1. For this purpose I use the same code snippet:
public Form1()
{
InitializeComponent();
// Set a click event handler for the button in the panel
foreach (var button in panel1.Controls.OfType<Button>())
{
button.Click += HandleClick;
}
}
What I need to do is to have a way to identify which button exactly has been clicked. For this purpose I played a little bit with my handler method:
private void HandleClick(object o, EventArgs e)
{
MessageBox.Show("HI" + o.ToString());
}
which gave me some hope because I get this:
It's the second part - Text: button4 which is actually enough information to continue with my work. But I can't find a way to get this piece of information without some complicated string manipulations. So is there a way to get this or other unique information about the button been clicked given the way I have written my code?
private void HandleClick(object sender, EventArgs e)
{
var btn = sender as Button;
if (btn != null)
{
MessageBox.Show(btn.Text);
}
}
One option is to cast the object to a Button, but rather than doing the casting you can change how the event handler is assigned so that you don't need to cast in the first place:
foreach (var button in panel1.Controls.OfType<Button>())
{
button.Click += (_,args)=> HandleClick(button, args);
}
Then just change the signature of HandleClick to:
private void HandleClick(Button button, EventArgs e);
You need to cast sender to the Button class so you can access its properties:
Button b = (Button)sender;
MessageBox.Show(b.Text);
I made a custom button that has a field named Data.
I add this button programatically during runtime to my winform and on adding I also define a click event for them. Well, Actually I only have one method and I subscribe the newly added buttons to this method.
But in the click event I want to access this Data field and show it as a message box, but it seems that my casting is not right:
CustomButton_Click(object sender, EventArgs e)
{
Button button;
if (sender is Button)
{
button = sender as Button;
}
//How to access "Data" field in the sender button?
//button.Data is not compiling!
}
UPDATE:
I am sorry, I ment with "is not compiling" that .Data does not show up in intelisense...
You need to cast to the type of your custom class that has the Data field.
Something like:
YourCustomButton button = sender as YourCustomButton;
Assuming your custom button type is CustomButton, you should do this instead:
CustomButton_Click(object sender, EventArgs e){
CustomButton button = sender as CustomButton;
if (button != null){
// Use your button here
}
}
If you dont want to set a variable the simple way to do is:
((CustomButton)sender).Click
or whatever you want.
I found a funny check assignment in a win forms project on Github:
private void btn_Click(object sender, EventArgs e){
// here it checks if sender is button and make the assignment, all in one shot.
// Bad readability, thus not recommended
if (!(sender is Button senderButton))
return;
var _text = senderButton.Text;
...
I'm trying to add RoutedEventHandler to all the TextBoxes through code, using the following line of code:
this.AddHandler(TextBox.GotFocusEvent, new RoutedEventHandler(textBox_GotFocus));
The code above binds the handler to all the form control on the Window instead of TextBoxes alone. Please can someone
explain why this happens
and how to do it right.
Thank you.
Probably not exactly what you are after because it will still fire on every UIElement. But, you can do the following to get the "end result" you need.
public void textBox_GotFocus(object sender, RoutedEventArgs e)
{
var textBox = e.Source as TextBox;
if (textBox == null)
return;
//what ever you wanted to do
}