WPF execute a function before a particular event occurs? - c#

Is there a way to execute a piece of code before an event occurs?
example when we say Expanded="OnExpand"
here the code inside OnExpand occurs after the Expanded event occurs. What if I want to execute a piece of code before that?

You can use the Preview Events
A possible work around for the expander not having a PreviewExpanded event is to handle the PreviewMouseDown event and do a hit test to see if its on the Toggle Button.
Alternatively it may be possible to extend the Expander Class something along the lines of
I did not test this at all no idea is it really works
public class MyExpander: Expander
{
public event EventHandler<EventArgs> PreviewExpanded;
public void OnPreviewExpanded()
{
PreviewExpanded(this,new EventArgs());
}
public override void OnExpanded()
{
PreviewExpanded()
base.OnExpanded();
}
}

If you're talking about the Expander control, you could subclass it and override the IsExpanded property, raising your own PreviewExpanded event before calling base.IsExpanded = value;

If whatever object you are working with supports this behavior it will be in a matching "Preview" event. So, these two events are a before and after matched set.
KeyDown()
PreviewKeyDown()
Expander does not have a preview event for Expanded, if that is what you are working with.

Related

Is there an elegant way to attach a single event handler to all the preview mouse events in WPF?

I am writing a WPF behavior that is supposed to block all mouse events that occur when the mouse is not inside a specific square. I wanted the behavior to attach a PreviewMouse*** event handler that checks the mouse position and sets Handled=true if the mouse is not inside the specific square. The problem is that there are a lot of different mouse events and I prefer that my code be elegant and clean. Is there a way to catch all preview mouse events in one elegant line?
Thnx,
What you can do is use reflection to attach to all events containing "PreviewMouse" in their name. To achieve that you can simply create an extension method for UIElement and implement it like this
public static class UIElementExtensions
{
public static void HandleAllPreviewMouse(this UIElement uiElement, RoutedEventHandler handler)
{
var elementType = uiElement.GetType();
foreach (var eventInfo in elementType.GetEvents().Where(ei => ei.Name.Contains("PreviewMouse")))
{
var specificHandler = Delegate.CreateDelegate(eventInfo.EventHandlerType, handler.Method);
eventInfo.AddEventHandler(uiElement, specificHandler);
}
}
}
And then in your Window code behind attach your handling code to your root layout Grid (or whavetever parent element you want to hook) using the extension
public MainWindow()
{
InitializeComponent();
var handler = new RoutedEventHandler(delegate(object sender, RoutedEventArgs e)
{
Console.WriteLine("Preview event fired");
// Uncomment if you want to block event propagation
//e.Handled = true;
});
this.LayoutRoot.HandleAllPreviewMouse(handler);
}
This solution is a one-liner solution to your issue but keep in mind it is not extra clean, especially on the part where events are filtered based on their name. But you can surely work it a bit to make it better.
PreviewMouse events are tunnelling events so they tunnel from root element to the actual sender. So, you can hook the PreviewMouse*** event handler on root element if you don't want to attach explicitly to all child elements.
And in handler you can check e.OriginalSource to get control which actually raises that event and can code accordingly.
<Grid PreviewMouseLeftButtonDown="Handler">
<Button/>
<TextBox/>
<TextBlock/>
<AnotherControl/>
</Grid>
Update for comment:
What I am looking for is a single C# line of code that attaches a
single event handler to many different mouse events on the same
element (PreviewMouseDown, PreviewMouseUp, PreviewLeftMouseDown,
PreviewLeftMouseUp, PreviewMouseWheel...). Instead of attaching the
same handler to each event specifically, I would like to attach it to
all of them at once.
I think you are complicating things here just to avoid some lines of code. Those events are independent of each other, so one or other way you have to assign a handler to it (may be with reflection as proposed in other answer). But, I don't think it's a nice approach to do that considering reflection is bit slower. Moreover, with that approach you will end up having same lines of code.
If lines of code is a concern, then you can encapsulate in a method and pass on common handler to it and hook the handler to interested events.
private void HookPreviewMouseEvents(MouseButtonEventHandler handler)
{
PreviewMouseDown += handler;
PreviewMouseLeftButtonUp += handler;
PreviewMouseUp += handler;
}

Simulating events on UIElement's without inheriting on Windows Phone 7

I have a very simple requirement, i.e. I want to send a synthetic event to a UIElement, in this case, a Grid. What I want is simply that I be able to send a synthetic event to an UIElement.
For example,
StackPanel myPanel;
StackPanel topPanel;
topPanel.MouseLeftButtonUp += new MouseButtonEventHandler(topPanel_MouseLeftButtonUp);
private void topPanel_MouseLeftButtonUp(object sender, MouseEventArgs args) {
// Here I want to send the MouseLeftButtonUp event to myPanel
}
It is possible using RaiseEvent, but it is a protected event and hence I cannot just call it on an instance of any UIElement. So how do I go about sending a synthetic event on existing classes?
P.S: The reason that I cannot create custom inherited classes is that the current code base is too huge and the number of changes that will be required in case I take such an approach are not feasible.
Any help is greatly appreciated.
Regards,
roahn
Instead of raising events, you could just move the relevant code from the event handler to a method. Then, you can just call the method whenever you want to simulate a button click. However, if you want to simulate the button click on an element, you could do this:
//Assuming myPanel_MouseLeftButtonUp is the event handler for myPanel
myPanel_MouseLeftButtonUp(null, null);

Interop WPF in WinForms, how to handle events from WPF control

I'm working with some WPF Interoperability in a WinForms application. I have the following set up.
WinForms UserControl WFControl
WPF UserControl GalleryControl
ListBox GalleryItems
ListBox ItemTemplate GalleryItem
Winforms hosting the GalleryControl, which has GalleryItems (ListBox) that has a ItemTemplate of GalleryItem.
Now in the WFControl I want to see when GalleryItems has it's SelectionChanged Event triggered.
My current attempts have tried to:
Handle the SelectionChanged Event in GalleryControl and have it raise a seperate public event that my winforms can read, but I can't handle the event like that since it's not a routed event. This would work if I could figure out how to handle that. applicable code:
public event ClaimGallery SelectedClaimChanged;
public ViewModels.InsuranceClaimViewModel ClaimViewModel { get; set; }
public int SelectedClaimID
{
get
{
return ((Models.InsuranceClaim) ClaimList.SelectedItem).ID;
}
}
public ClaimGallery()
{
InitializeComponent();
ClaimViewModel = new ViewModels.InsuranceClaimViewModel();
DataContext = ClaimViewModel;
ClaimList.ItemsSource = ClaimViewModel.InsuranceClaims;
ClaimList.SelectionChanged += ClaimSelectionChanged;
}
private void ClaimSelectionChanged(object sender, EventArgs e)
{
//This is the part that doesn't work
ClaimList.RaiseEvent(new RoutedEventArgs(SelectedClaimChanged, this));
}
I've also seen that I could potentially find the ListBox via some control tree browsing the subscribe to the actual event in the WFControl but I can't seem to figure how to do this in an interop'd control.
I have similar problems in my current project, and I'm solving it the way you describe.
The WPF controls re-raises a public (normal) event, that is then handled by the WinForms control.
Honestly I don't get the part where you are stating that is has to be routed in order to be handled by Winforms.
my winforms can read, but I can't handle the event like that since it's not a routed event
you use "+=" to handle this one ...

Is it possible to subscribe one control to the same event handler multiple times?

I would like to know if I bind the TextChanged event handler to a TextBox control, then how can I ensure that won't be allowed to bind this event handler again?
You can't ensure that. You would theoretically be allowed to bind the same event handler to a textbox (or other control) more than once. The only thing that events allow you to do is add a handler and remove a handler—there's no additional means provided to check for existing subscribers. If you don't believe me, Jon Skeet provides the authoritative answer here, and in his article on events.
If you need to ensure that you don't accidentally subscribe a control to the same event twice, you'll need to keep track of it yourself. Honestly, you should never end up in a situation where you don't know what event handlers are subscribed. Not only does this reflect sloppy design, but it probably also means that you aren't taking care to remove your event handlers when they are no longer necessary.
A possible solution is provided in the answers to this question, but I caution you from using something like this blindly. As others have argued, this code is something of an anti-pattern.
You can bind it programatically as many times as you'd like. If you want to prevent this you can use a List<object> to keep references in, for example:
private List gotRefs = new List();
public void MyMethod()
{
if (!gotRefs.Contains(txtTextBox1)) {
txtTextBox1.TextChanged += txtTextBox1_TextChanged;
gotRefs.Add(txtTextBox1);
}
}
You can actually use a hack.
You can make textbox private to your class to ensure, that only your single class is able to access the text box and add handlers to it.
private TextBox txtChanging;
Then, in your class, you can create a custom event like textBoxTextChanged
public event Action TextBoxTextChanged;
Create a standart method OnTextBoxTextChanged
private OnTextBoxChanged( object sender, EventArgs args )
{
if( TextBoxTextChanged != null )
TextBoxTextChanged();
}
Bind this OnTextBoxChangedMethod to TextChanged event of the TextBox
txtChanging.TextChanged += OnTextBoxChanged;
Ensure that NO OTHER METHOD is bound to TextChanged event of the text box
Bound all your main payload text changed handlers to your new custom event instead of text box textchanged event directly
TextBoxTextChanged += txtChanged_TextChnaged;
All that paperwork is because events actually provide a lot of information, but only to the methods inside the class where they are defined.
You can check bound delegates with
TextBoxTextChanged.GetInvocationList()
and their count with
TextBoxTextChanged.GetInvocationList().Count() //(C# 4.0/LINQ)
or just count them through foreach.

When to fire a custom control event?

I am creating a custom control. Let's say I'm reinventing the wheel and creating a custom Button control that derives from the UserControl class(only for example)
Well, it of course has a Click event handler. Now my problem is, when do I call this event handler? I know it happens sometime between the Pages OnLoad and OnLoadComplete, but I'm not quite sure what event I can hookup to so that the Click event is raised at the same time as other control events.
When are you suppose to call custom control events?
In general you should raise the event as soon as you know that the underlying event has happened.
For example, how does the ASP.NET Button control know that the it was clicked by the use? It implements IPostBackEventHandler and the RaisePostBackEvent() method. That method will get called if there is postback event data associated with the control. I believe the association is determined by the "name" attribute that it renders. In RaisePostBackEvent() it then raises the Click event.
The question you need to answer is: How does your control know that it should raise its event? Once you find that our the rest is easy.
If your control has similarities to existing ASP.NET controls I would recommend stepping through the ASP.NET source code and seeing how those controls work.
If you implement IPostbackEventHandler, you can do something like this, taken from decompiling System.Web.Ui.WebControls.Button
protected virtual void RaisePostBackEvent(string eventArgument)
{
base.ValidateEvent(this.UniqueID, eventArgument);
if (this.CausesValidation)
{
this.Page.Validate(this.ValidationGroup);
}
this.OnClick(EventArgs.Empty);
this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
}

Categories

Resources