WPF Custom Routed event question - wpf

How do you get two unrelated controls to raise the same custom event? All examples I have seen so far have an event defined within a single control, should I be taking a different approach?
Eg. I'd like to raise a custom bubbling event from an OnFocus handler for a button and a textbox.

First off let me say your question doesn't make it clear that you don't want to use the existing UIElement.GotFocusEvent, but I'll assume you know about it and have your reasons for not using it.
You can always register a custom event on a static class, and raise it wherever you want. The Keyboard class does with all of its events (e.g. Keyboard.KeyDownEvent).
public static class RoutedEventUtility
{
public static readonly RoutedEvent MyCustomEvent = EventManager.RegisterRoutedEvent("MyCustomEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutedEventUtility));
}
You raise the event just like you would any other RoutedEvent.
RoutedEventArgs args = new RoutedEventArgs(RoutedEventUtility.MyCustomEvent);
RaiseEvent(args);
If you want another class to own the event as a public field then you will need to add an owner.
public class MyCustomControl : Control
{
public static readonly RoutedEvent MyCustomEvent = RoutedEventUtility.MyCustomEvent.AddOwner(typeof(MyCustomControl));
}

Related

How to subscribe to an event in a child usercontrol in Silverlight?

I've got a usercontrol (MyUC) that is programatically added to a page (MainPage) several times.
In MyUC I set the DataContext to a view model like this:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DataContext = new MyUCViewModel();
}
In my view model (MyUCViewModel) I have a collection of data items:
public MyDataItemCollection MyDataItems { get; private set; }
And in the constructor I have:
public MyUCViewModel()
{
this.MyDataItems = new MyDataItemCollection();
this.MyDataItems.ChosenItems.CollectionChanged += new NotifyCollectionChangedEventHandler(ChosenItemsChanged);
this.MyDataItems.Add(new DataItem());
}
From the above MyDataItems has another collection in it for ChosenItems and I added a NotifyCollectionChangedEventHandler to it.
Other parts of my code add and remove from the ChosenItems collection and that part seems to work ok.
Next I have the event handler method in the view model:
private void ChosenItemsChanged(object sender, EventArgs e)
{
MessageBox.Show("Chosen Items Changed");
}
This also works and I get a messagebox everytime the user makes a change in the UI that affects the ChosenItems collection.
The part I'm trying to figure out now is how do I set it up so that my MainPage does something when the ChosenItemsChanged event fires in my user controls. What I want to do is have the MainPage loop through the generated MyUC controls on the page and make each usercontrol call a method.
You can add more event listeners in the MainPage like this:
MyUCViewModel viewModel = myUC.DataContext;
viewModel.MyDataItems.ChosenItems.CollectionChanged
+= new NotifyCollectionChangedEventHandler(MainPage_ChosenItemsChanged);
This is based on the comment as the question was a little misleading:
While not strictly MVVM, as your question appears to be, your should write your User Controls as if it was a third-party control and simply expose a custom event on it. User Controls should always be a black-box with a public interface. For a reusable control that is self-contained (as many are) MVVM is overkill.
e.g.
in your User Control add:
public event EventHandler<MyEventArgs> MyEvent
Create a MyEventArgs class deriving from EventArgs and get it to hold useful parameters (like the selected item).
In your main page add a handler to MyEvent on each User Control you dynamically add.
I actually think the MVVM model is flawed and all this sort of controlling logic and event handlers belong in a Controller class (MVCVM!), but that's another story. :)

How does the WPF event system know about the event route?

I am trying to understand how RoutedEvents work.
Well - I walked through some tutorials and understood why RoutedEvents are useful and how they work.
But there is one thing, that I don't get:
Let's say I wrote a class (e.g. "MyClass") , which has a RoutedEvent property, sth. like this:
public class MyClass
{
public static readonly RoutedEvent myEvent;
...
}
Well - just giving a property is not enough - so I have to register the RoutedEvent with the help of EventManager:
...
myEvent = EventManager.RegisterRoutedEvent("MyEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyClass));
...
Okay - now the WPF event system knows about THIS event.
If I do it that way, each class I write will have it's own RoutedEvent. But that makes no sense to me.
What I want, is that other classes listen to the same event - without being a type of MyClass.
For example:
I have a stackpanel and within the stackpanel is a button. Clicking the stackpanel will raise the onClick event. Clicking the button will raise the onClick event of the button - and then the onClick event on the stackpanel.
But how?
Sorry - it's hard for me to describe the problem - I am just too confused :)
Thx a lot.
CodeCannibal
What I want, is that other classes listen to the same event - without being a type of MyClass.
You expect the right from this and this is what it delivers. I mean by registering a RoutedEvent you are not strongly binding it to the type; instead you are bridging it using the string "MyEvent" EventManager.RegisterRoutedEvent("MyEvent", ...
RoutedEvent traverse through the logical tree and stops traversing when handled (exceptions are there).
So, StackPanel need not to be derived from MyClass. You just need to register the RoutedEvent at StackPanel by specifying the action/handler. Whenever the RoutedEvent traverse through StackPanel it will call the corresponding action.
For example:
UserControl1.cs
//Routed Event
public static readonly RoutedEvent ThisIsEvent = EventManager.RegisterRoutedEvent("ThisIs", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(UserControl1));
// .NET wrapper
public event RoutedEventHandler ThisIs
{
add { AddHandler(ThisIsEvent, value); }
remove { RemoveHandler(ThisIsEvent, value); }
}
//local handler where RaiseEvent is called
private void button1_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(ThisIsEvent));
}
And below is how you subscribe to that event in you XAML. You can also do this in your code file...
<StackPanel Orientation="Vertical" **local:UserControl1.ThisIs="StackPanel_ThisIs"** >
<local:UserControl1></local:UserControl1>
</StackPanel>
I hope this clear your doubts.

Difference between normal customized event the static RoutedEvent

Please check the code below which defines the same scroll event in two different ways:
---------------------------------normal way--------------------------------------
public event RoutedEventHandler CloseTab;
------------------------------static RoutedEvent---------------------------------
public static readonly RoutedEvent CloseTabEvent =
EventManager.RegisterRoutedEvent("CloseTab", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(CloseableTabItem));
public event RoutedEventHandler CloseTab
{
add { AddHandler(CloseTabEvent, value); }
remove { RemoveHandler(CloseTabEvent, value); }
}
1.What's the difference between the normal customized event and the static RoutedEvent?
2.Why the event in the first way can be raised by a method like "if(CloseTab!=null){CloseTab(o,e);}",but in the second way ,it only can be raised by the UIElement.RaiseEvent() method?
3.I know this question must be very very silly,but it dose make me confused.
It is that Why the event in the first way can be raised by the "if(CloseTab!=null){CloseTab(o,e);}" method only in the class which the event is defined?And when it is outside the owner class,the event can only add or remove handlers with "+=/-=" ?
Thanks for your time!!
You implementation basics i.e. the way we raise each one differently, are correct.
But apart from that, as far as my knowldge on routed events goes...
The first is NOT a routed event. It is a simple CLR event declared with its type as the RoutedEventHandler delegate. When this event is raised it will not bubble \ tunnel up to the ancestor/child UI elements respectively.
At practical level, I guess if you try using the first one in EventTrigger it will not work.

Capture Click Event in a Custom Control

I have a WPF Custom Control inherited from Button.
How do I programatically get the custom control to capture the Click Event (so that I can record the action and do some internal work)
(basically I want to catch the event and set a certain property to a certain value) and make this part of the classes standard functionality.
From my understanding the custom control should be able to catch it's own even and do some work.
Help appreciated
try one of the overrides
public class CustomButton : Button {
protected override void OnPreviewMouseDown(MouseButtonEventArgs e) {
base.OnPreviewMouseDown(e);
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) {
base.OnPreviewMouseLeftButtonDown(e);
}
}
I have realised why Ihad a problem.
I could not see the Click event
This was because I was not explicit enought in my class declaration:
I put
public class StateButton : Button
obviously picked the wrong button .. as
public class StateButton : System.Windows.Controls.Button
works
Then I just override the event
Thanks

How to add event handler to an inherited winform control?

I need to add an event handler to an inherited control like a datagridview but Visual Studio doesn't allow me. Isn't there a way for an inherited control to fire a base event handler AND the inherited one? in a sequence I specify?
Your question is unclear. Assuming that you're trying to handle the base class' event in the inherited control, you can override the OnEventName protected virtual method. In the override, make sure to call the base method or the event won't fire.
This method exists (AFAIK) for every event in every control in System.Windows.Forms. If the control you're inheriting does not have this virtual method, you can subscribe to the vent in the constructor.
For example:
class MyButton : Button {
//First way
protected override void OnClick(EventArgs e) {
base.OnClick(e); //Without this line, the event won't be fired
//...
}
//Second way
public MyButton() {
base.Click += Base_Click;
}
void Base_Click(object sender, EventArgs e) {
//...
}
}
EDIT:
If you're trying to raise the base class' event, you can call these OnEventName methods.
If the base class doesn't have one, the only way to do it is to declare a new event, and add a handler to the original event from the base class that raises your new event. Note that if you have other code that uses the base class' event, and the evnet is not virtual and doesn't have a raiser method, you're out of luck, unless you can decompile the base clas and find out where the event is raised.
Events in WinForms typically have a corresponding "On*" method. Simply call that method, and it'll raise the event. If you want to raise "CellClick" for example, call "base.OnCellClick(new DataGridViewCellEventArgs(row, column))".

Resources