In C# events were always very protected: Only the owner of the event could trigger them. However, this seems to be completely different in WPF - Anyone can throw any event at any time. To test that, I've written the code in the bottom.
When I used RaiseEvent to raise Button.Click, the event above caught it. Is that the planned behavior of WPF events? Just letting anyone throw any events they wish? Also, if so, then what is the meaning of the OwnerType when you register the event? I thought it is some kind of protection, yet if it is, it is a poor one since anyone can access the public event and use AddOwner function to add more owners.
Thanks!
XAML
<StackPanel Button.Click="ButtonBase_OnClick">
<Button Name="RealButton">Real button</Button>
<WpfWindow:VitalyControl MouseDown="UIElement_OnMouseDown">
I am almost a button
</WpfWindow:VitalyControl>
</StackPanel>
Code behind
The custom control:
class VitalyControl : Label
{
public VitalyControl()
{
this.MouseDown += new MouseButtonEventHandler(VitalyControl_MouseDown);
}
void VitalyControl_MouseDown(object sender, MouseButtonEventArgs e)
{
RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
}
}
And the handler:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button was pressed");
}
This is by design, and is actually one of the reasons for RoutedEvents. They are called routed events because they are routed across the element tree. The behavior you are experiencing is called 'singular handler attachment point' on msdn. You specify that StackPanel should listen to all Button.Click events.
In your custom control, you raise a button click event. This 'bubbles' up to the stackpanel, which handles it.
UPDATE:
For this routing to work, I assume every UIElement needs to be able to raise any routed event. Routed Events are only used by UI elements, and are an answer to complexities with WinForms implementations. They aren't a replacement for CLR events.
The owner type is used internally when resolving an event by name.
Related
I have registered to a LostFocus event on a TextBox and yet the event is not catch - my guess is that someone else handled it.
I've tried using snoop but it only shows me the MouseDown and MouseUp events (and I need the LostFocus).
Any ideas on how can I find out?
Thanks
Update:
Not so clear but the code where I register is:
eventInfo.AddEventHandler(cloningObject, eventDelegate);
In the XAML, make sure you assign a name to your TextBox:
<TextBox Name="MyTextBox" />
Create a function in your code behind to handle the event:
public void MyLostFocusHandler(object sender, RoutedEventArgs e) {
// ...
}
And then in your window's constructor (assuming this is in a window):
MyTextBox.LostFocus += MyLostFocusHandler;
Note also there is another event, LostKeyboardFocus.
I need to change a certain DataGridView's property (a DataSourceUpdateMode for one of its binding) only when ALL of its initial data bindings are completed.
I tried subscribing to the "DataBindingComplete" event, but it's fired too many times (one or more time for each binding associated to the control); what I need is a more global "AllDataBindingsComplete" event, fired when the control is ready to be displayed to the user.
As a temporary workaround, I'm using the MouseDown event (I've assumed that when the user is able to click the control, it means that the control is displayed... :) and the events I'm playing with - SelectionChanged - are fired after the MouseDown):
protected override void OnMouseDown(MouseEventArgs e)
{
Binding selectedItemsBinding = this.DataBindings["SelectedItems"];
if (selectedItemsBinding != null)
{
selectedItemsBinding.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
}
base.OnMouseDown(e);
}
It works, but it smells like an ugly hack A LOT (and it's called too many times, only one time is enough for my needs).
Is there a better way?
(yes, I'm trying to adopt MVVM in a Windows Forms project, and I've added a bindable "SelectedItems" property to the DataGridView...)
What I've done at the Windows Forms form level, and may be improvised down to just the control(s) you want, is to subclass the Windows Forms baseclass into my own. Then, in its constructor, attach an extra event call to the Load() event.
So when everything else is completely loaded, only THEN will it hit my custom method (of the subclass). Since it is the bottom of the call-stack chain being attached to the event queue, I know it's last and everything else is done... Here's a snippet of the concept.
public class MyForm : Form
{
public MyForm()
{
this.Load += AfterEverythingElseLoaded;
}
private void AfterEverythingElseLoaded(object sender, EventArgs e)
{
// Do my own things here...
}
}
This concept can be applied to the Init() function too if that's more appropriate for your control... Let everything else within it get initialized(), then do you the "AfterInitialized()" function.
I'm developing a Windows Mobile 5.0 or above application with .Net Compact Framework 2.0 SP2 and C#.
I have a Winform (Form1) with a control (Control1) that contains another control (Control2). For example, a winform with a panel and inside this panel there is a button, but in my case Control1 and Control2 are custom controls.
Control2 has an event, Click, that is thrown when the user does click over it. This click event must be handled by Form1. To do it, first I handle the event on Control1 that throws a new event that is handled on Form1. This my code:
On Control1:
public event EventHandler Control2Click;
private void control2_Click(object sender, EventArgs e)
{
if (Control2Click != null)
{
Control2Click(sender, e);
}
}
On Form1:
private void control1_Control2Click(object sender, EventArgs e)
{
// Do something interesting.
}
Is there a better way to handle Control2_Click directly in Form1? I don't know if my way has a bad performance and this kind of events can be handled better.
Thank you!
No, you are doing it right. It is the correct way the bubble an event out of a nested control that isn't directly accessible from a container control. You'd normally use the PerformClick() method to fire the Click event but this doesn't appear to be available in CF.
Perf is not an issue, calling a delegate target is very fast, a dozen nanoseconds or so on a desktop machine. Click is a "human-time" event, anything less than 20 milliseconds is perceived as "instant".
What's stopping you from hooking up the Control2 Click event directly from Form1? Does Control1 expose Control2 via a property? Or perhaps expose an event on Control1 which actually hooks up to the Control2 Click event? For example:
// In Control1
// Assuming Control2 is some sort of Save button, for example
public EventHandler SaveClicked
{
add { control2.Click += value; }
remove { control2.Click -= value; }
}
Note that:
If you change the value of control2 within Control1, the event handlers won't be "transferred" which would be be unfortunate
The sender parameter in the event handler will refer to Control2, not Control1
To be honest I wouldn't expect this to be a performance problem however you handle it - it's just a delegate invocation or two.
Is there any standard way to route all Key events from the control A to other control B? I wish that the keyboard focus will still be on A however the event handler of A would trigger the all event handlers of B for the key events.
edit: Clarification: calling a specific event handler I wrote for B is not enough. I need to mimic the actual event. So for example I want that if a key is sent to a TextBox, it would be written to the TextBox. The solution given below does not do that (not to mention the fact that if new event handlers are added to B it completely fails).
I'm aware that WPF differentiates between logical focus and keyboard focus, but I need both focuses to remain on control A, but in a certain cases route its incoming event to other controls.
Couldn't you do something like this?
private void button1_Click(object sender, RoutedEventArgs e)
{
// Check if the event needs to be passed to button2's handler
if (conditionIsMet)
{
// Send the event to button2
button2.RaiseEvent(e);
}
else
{
// button1's "Click" code
}
}
private void button2_Click(object sender, RoutedEventArgs e)
{
// button2's "Click" code
}
Edit: Modified code to use the RaiseEvent() method to programmatically raise a specific event, rather than just calling the event handler for button2.
I've create a WinForms control that inherits from System.Windows.Forms.UserControl...I've got some custom events on the control that I would like the consumer of my control to be able to see. I'm unable to actually get my events to show up in the Events tab of the Properties window during design time. This means the only way to assign the events is to programmatically write
myUserControl.MyCustomEvent += new MyUserControl.MyCustomEventHandler(EventHandlerFunction);
this is fine for me I guess but when someone else comes to use my UserControl they are not going to know that these events exist (unless they read the library doco...yeah right). I know the event will show up using Intellisense but it would be great if it could show in the properties window too.
Make sure your events are exposed as public. For example...
[Browsable(true)]
public event EventHandler MyCustomEvent;
A solution using delegate. For example i used for a custom ListView which handle item added event :
Declare your delegate :
public delegate void ItemAddedHandler(object sender, ItemEventArgs e)
then declare the event which use the delegate :
[Browsable(true)]
public event ItemAddedHandler ItemAdded;
Note : ItemEventArgs is a custom EventArgs
Hope can help you, works fine for me