Delete / Kill / Remove UserControl and its events handlers - wpf

I dynamically create UserControls using Reflection:
UserControl myConmtrol = (UserControl)Activator.CreateInstance(t);
The UserControl may handle a Closing event but I do not know the name of the handler.
When the Window hosting the UserControl closes I remove the UserControl from its parent Window and it disappears from the Window: Everything seems OK.
But if I open and close again the UserControl I can see in the debugger the Closing event is handled twice, one time by the current UserControl but also by the previous UserControl that is still alive.
Theorically the UserControl being no longer referenced should be GarbageCollected.
How can I force it to be Killed/Deleted/Disposed ? At least is there a way to forbid it handles events ?
Thanks

Not sure without more detail but i would start and check if you have any event handlers that isn't removed
Do I need to remove event subscriptions from objects before they are orphaned?

Answer about removing handlers without knowing their names:
public void RemoveHandlerOfUserControl(UserControl uc)
{
MulticastDelegate dlg = MyEvent;
Delegate[] handlers = dlg.GetInvocationList();
foreach (Delegate d in handlers)
{
if (d.Target == uc)
{
this.RemoveHandler(MyEvent, d);
}
}
}
This method must reside in the object where the event is declared.

I had to cope with the same situation in Winforms where I dynamically create a user control inside another user control (Let's say a "DynControl" inside "HostControl").
There is no Closing event in "HostControl". So I used the Disposed event of HostControl to release resources :
this.Disposed += (s, e1) =>
{
DynControl.Click -= += new EventHandler(MyClickHandler);
this.Controls.Remove(DynControl);
DynControl.Dispose();
};

Related

wpf textbox won't throw textInput event

I have a window with a textbox that is not throwing textInput events when typing.
I have been looking at it with Snooper. Only the KeyDown and KeyUp events get thrown.
It IS responding to a few keys: Space, backspace, Insert, home, delete, end
It responds to copy & paste commands, as well as Select All
It is NOT responding to any character, symbol or number
And here's the kicker: This window is opened via a shared method that is called from two different places in the code. When called from one location the textbox works perfectly, when called from other location it doesn't.
I have ruled out binding, data converters, styles, control location.
I stripped the window down to just a single plain textbox with no bindings, and the problem persists.
I've tried all I can think of to track down this mysterious bug. I can't see what could be handling my event before the previewTextInput even gets throw, or why it could possibly be only occurring half the time.
Any ideas about the cause of this bug, or other tools I could try to trace the events would be greatly appreciated!
Edit: Adding some code to demonstrate. This has been stripped down to the barest code required, and the issue is still occurring.
<Window x:Class="EventViewEmail"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="880" Height="600">
<TextBox VerticalAlignment="Top"/>
</Window>
notice the lack of bindings, styles, or anything else that may be interfering the control
Public Class EventViewEmail
'all code removed from the view-behind'
End Class
this is the static class that builds the window. The two separate calls to this class build the arguments differently. I've removed the code that uses the arguments to show that they aren't affecting the issue at hand.
Public Class EventManager
Public Shared Sub Show(e As EventEdit, p As WorkplanPageViewModel)
Dim w = New EventViewEmail
If w Is Nothing Then Return
'removed datacontext for testing'
'w.DataContext = e '
'w.Tag = p'
w.Show()
End Sub
End Class
The only other thing I can add is that the code calling the Show() sub are coming from two separate solutions. Not sure what impact that could possibly have after I've removed all the arguments
Edit 2:
I have been trying to trace the events sequence to narrow down where the event is getting handled. So far I can see that between the keyDown and keyUp events, there is a sequence of events that should be happening that isn't:
PreviewInputReport / InputReport (no source)
PreviewTextInputStart / TextInputStart (textbox)
PreviewTextInput / TextInput (textbox)
PreviewInputReport / InputReport (textboxView)
the keydown event isn't being handled, so I'm not sure why the PreviewInputReport is not getting fired
Disclaimer
I'll post C# code because I'm not fluent enough in VB to write code (only to read it in a limited scope). I'll try to translate it if I find some time, or anyone of you can feel free to do it.
I have managed to reproduce the behavior you described by handling the PreviewTextInput event up the visual tree on the window (remember it's a tunneling routed event):
<Window (...)>
<StackPanel>
<TextBox x:Name="myTextBox"></TextBox>
</StackPanel>
</Window>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
PreviewTextInput += (s, e) => e.Handled = true;
}
}
So in my opinion it's a strong suspect for the cause of your problems. It also explains the comment to #AQuirky's answer that your PreviewTextInput handler is not called. You can confirm that that's the case by subscribing differently (the handler is called even if the event was marked as handled):
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
PreviewTextInput += (s, e) => e.Handled = true;
myTextBox.AddHandler(
routedEvent: PreviewTextInputEvent,
handler: new TextCompositionEventHandler((s, e) =>
{
if (e.Handled)
throw new Exception("Gotcha!");
}),
handledEventsToo: true);
}
}
If this turns out to be correct diagnosis, then there's one thing that's somewhat mysterious - who and where does handle the PreviewTextInput event? I guess that's for you to investigate...
This comment from #l33t might come in handy:
Look for global event handlers like EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewTextInputEvent, ...) and/or behaviors. Are you using any third-party UI library? Look in their code as well.
Update
Since it seems like PreviewTextInput is not the culprit after all, here's what I'd do next.
If I'm not mistaken, there's a whole chain of events being fired prior to TextInputEvent, and I believe handling any of those can break the chain. It also looks like InputManager is responsible for managing this event cycle (you can inspect its C# source code here).
That being said I suggest doing the following:
Using InputManager.Current subscribe to its PreProcessInput and/or PostProcessInput events (optionally also PreNotifyInput and PostNotifyInput)
Record the events chain in the working scenario (especially inspect the args' StagingItem.Input.RoutedEvent, which holds the routed event currently being processed)
Repeat the procedure for the not working scenario
Pinpoint the first difference - the first routed event being processed in the working scenario, but not being processed in the not working scenario
Investigate both last common routed event and the first one that's different - perhaps one of those is handled in your code?
In this case it turned out that the form that launched the window in the not working case was a winform. The winform was blocking the keystrokes. This was fixed by opening the window as a modal.
I had the same problem here. The difference between the two calls was: one time the WPF-Windows was called modal (works) and one time nonmodal (doesnt work), everytime from a WindowsForms Window.
As Shaboboo wrote in his anwer, this is the difference, but what, if you need to call nonmodal.
The answer is given this thread. (stupid me, I had the same problem 2 years ago, asked and got an answer). We have to use
ElementHost.EnableModelessKeyboardInterop(wpfWindow);
Thanks again to V.Leon
The problem is that while the TextInput is being fired (not thrown...exceptions are thrown) you are not seeing it because the text box itself is using it. To catch this event you need to use the PreviewTextInput. So for example
<TextBox TextInput="UIElement_OnTextInput" PreviewTextInput="UIElement_OnPreviewTextInput"></TextBox>
With event handlers...
private void UIElement_OnTextInput(object sender, TextCompositionEventArgs e)
{
Console.WriteLine($"In text input event, {e.Text}");
}
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
Console.WriteLine($"In preview text input event, {e.Text}");
}
The first handler is never called, the second one always.
Your handler is not called because other handlers have marked the event as handled. You can add an handler with AddHandler() and pass true for the third argument:
public MainWindow()
{
var handler = new RoutedEventHandler(OnTextInput);
myTextBox.AddHandler(TextInputEvent, handler, handledEventsToo: true);
}
private static void OnTextInput(object sender, RoutedEventArgs e)
{
}

Why can i use Thumb.DragStarted in other controls? [duplicate]

I will appreciate if some body can explain with a simple example.
Imagine a Window containing a dense hierarchy of child controls. Now let's say you want to do something, there's a right click anywhere in your window.
With normal events, you'd have to handle a Click event for all controls, because you're not sure where the user might click.
With WPF's routed events, the events either "bubble" or "tunnel" (i.e travel up the UI tree or down) if they dont find an event handler, which "handles" it at the current level. So you could write one handler for the window's event i.e. TopLevel. (WPF has a convention of event pairs, PreviewXXX and XXX - the PreviewXXX event fires first and tunnels down from root to control which received the stimulus and the counterpart XXX event then bubbles up from child control back upto Root). So if you right click a button, WPF travels up the UI hierarchy, invoking all handlers that it finds (unless someone marks the event has "handled" in the event args.)
Routed events are events with more 'traveling abilities', as mentioned in a Gishu's answer. Routed events are represented by an instance of a RoutedEvent class + ordinary .NET event, which wraps it:
public class MyClassWithARoutedEvent : UIElement
{
public static readonly RoutedEvent DoSomethingEvent;
public event RoutedEventHandler DoSomething
{
add { base.AddHandler ( MyClassWithARoutedEvent.DoSomethingEvent, value );
remove { base.AddHandler ( MyClassWithARoutedEvent.DoSomethingEvent, value );
}
}
You would typically use touted events in such situations:
Implementing your own control which seamlessly integrates with WPF's infrastructure
Processing events, fired by different controls at a common root
Sort of communication between elements in an element tree
In most situations you will probably use the routed events infrastructure without even noticing it.
In addition it's worth to mention, that you can use RoutedEvent in your control even if it does not define it or even inherits from an element, which does. That's because you can really think about a RoutedEvent instance as a strong typed name of an event. So, if you have an access to this 'name' (this is why an instance of a routed event is usually made public), you can owe it:
public class MyClassWithARoutedEvent : UIElement
{
public static readonly RoutedEvent ClickEvent;
static MyClassWithARoutedEvent ( )
{
ClickEvent = ButtonBase.ClickEvent.AddOwner( typeof ( MyClassWithARoutedEvent ) );
}
// A wrapper should be placed here as described above
}

WPF - Detect when UserControl is not visible anymore and prompt user

So, I have a class, which goes as follows:
public class EditorUserControl : UserControl
{
public EditorUserControl()
: base()
{
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(
EditorUserControl_IsVisibleChanged);
}
void EditorUserControl_IsVisibleChanged(
object sender,
DependencyPropertyChangedEventArgs e)
{
if (IsEditing && !((bool)e.NewValue))
{
PressedButton pressedButton = PromptUser(new Buttons[] {
"Save changes to the object you just edited?",
Buttons.Yes,
Buttons.No,
Buttons.Cancel });
if(pressedButton == Buttons.Cancel)
{
CANCELTHETHING();
}
}
}
}
In words - this class is a base for all entity editing controls and when it goes invisible (e.g. window is closed, tab changed etc.) I need to check if the user has made changes and prompt the user whether to save/discard/cancel. The save/discard are easy. The problem is with the third option (and it must be there) - I cannot figure out a way how could I cancel the source event that caused the visibility to change (as there is no way to get to that actual event). Is there a better way to do this functionality (that would not require signing up for all of the possible sources of events)?
I don't think it is possible to cancel the source (event) as you want to.
Consider this line of code - EditorUserControl.Visibility = Visisibility.Hidden;
This will also cause the IsVisibleChanged event to fire, but there is no way to cancel a single line of code.
Your only option is to move the logic inside the IsVisibleChanged event handler to a method that will be called as appropriate by the application. For instance if you close the window then in the window_closing event handler you call the method and if the result is Button.Cancel then you cancel the closing event. If you change tabs then you handle a SelectionChanged event and again call the method and if you need to cancel then you set the selected tab index back to the previous value etc.

WPF event that fires after element is fully parented, but before it's arranged

I'm writing a WPF control that dynamically changes its contents depending on what types of Window/UserControl descendants are in its parentage list (part of an experiment with convention vs. configuration).
As such, I need some code to run after my control is fully parented (i.e. all the way up to the Window that's being shown). Ideally, I'd also like my code to run before the first Measure/Arrange pass, since my code is going to change the control's contents and force another Measure/Arrange pass.
I've looked at EndInit, but it fires after the control is loaded from XAML, at which time it might not be fully parented. (For example, if my control was on a UserControl, then EndInit will fire once the UserControl is loaded -- but before it's parented to anything else. I want to wait until the UserControl is parented to something, and that's parented to something else, all the way up.)
Currently I'm just hooking the Loaded event from my control's constructor, and running my code there (oddly enough, WPF doesn't have an OnLoaded method to override):
public class MyControl
{
public MyControl()
{
Loaded += (sender, e) => { ... };
}
}
This works -- it fires when the parents are fully populated -- but it's slightly less than optimal, because there's a Measure/Arrange pass that happens before Loaded.
Is there a good place I can put my code so that it runs after the Parents are set all the way up, but before the first Measure/Arrange pass?
Extra coolness points for solutions that would also work in Silverlight, ElementHost, the Blend/VS designer, and VisualBrush (i.e., not assuming that the top-level parent is a Window, or in the case of VisualBrush, not assuming that there even is a parent -- just that it's as parented as it's gonna be before showing up on the screen, or being sent to the printer, or whatever).
I believe the parents are all set in a single dispatcher operation, so you should be able to get that behavior by putting your logic in a delegate and queuing it up as the next dispatcher operation after the parent is set:
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
this.Dispatcher.BeginInvoke(new Action(OnReady));
}
private void OnReady()
{
// Element should be fully parented here
}
You could also do that from EndInit rather than OnVisualParentChanged if you want to handle the case of no parent, although EndInit appears to be called more than once so you will need to check for duplicates:
private bool readyQueued;
public override void EndInit()
{
base.EndInit();
if (!readyQueued)
{
this.Dispatcher.BeginInvoke(new Action(OnReady));
readyQueued = true;
}
}
private void OnReady()
{
readyQueued = false;
// Element should be fully parented here
}

When to unhook events in Silverlight

One-line summary: What is the best practice for unhooking event handlers created in the constructor of a UserControl in Silverlight2?
Background:
I am currently building a line-of-business application in Silverlight2. As Silverlight is a browser plugin, there is no concept of a Window - everything is done within UserControls. The way I'm handling different "forms" in the application is to have a top-level usercontrol that contains a Viewbox. To show different forms, I set the Child property of the Viewbox to different UserControls. My app has a singleton PageManager class that is called to open and close forms. The forms (UserControls) are stored in a stack. Opening a form puts it on the top of the stack, closing it removes it from the stack and shows the one below it.
I'm trying to follow the Model-View-ViewModel pattern. In each form (derived from UserControl), I have a ViewModel that manages all the data for the View. The ViewModel exposes events so the UI can be notified when operations such as load and save have completed.
In my form, I subscribe to the event in the constructor, after I've got the ViewModel
public partial class MyPage : UserControl
{
public MyViewModel ViewModel{get; set;}
// other constructors, which create the viewmodel and call the constructor below.
public MyPage(MyViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
this.LayoutRoot.DataContext = this.ViewModel;
// subscribe to event so we can do stuff
this.ViewModel.LoadCompleted += new MyViewModel.LoadCompletedEventHandler(ViewModel_LoadCompleted);
}
My question is: Now that I've subscribed to this event, when do I remove the handler? Do I create a destructor and do it there, or does that create a chicken-and-egg situation where the garbage collector wont destroy the object until all references (ie: the event handlers) are gone? Do I create an interface that the forms must implement that specifies an UnhookEvents function that's called when the form is closed by the PageManager?
Edit: Thanks for the responses. What about the situation where the ViewModel lasts longer than the form (UserControl)? Part of my app allows users to create what is quite a complex structure, but in 95% of cases it's much simpler. What I've did was create 2 forms that use the same ViewModel. Users can start filling out the simple form, then switch to advanced mode, which creates a new form, passing the ViewModel to it.
In the simple setup form:
private void AdvancedSessionSetupButton_Click(object sender, RoutedEventArgs e)
{
PageManager.GetPageManager().Close(this);
PageManager.GetPageManager().Open(new CreateSessionPage(this.ViewModel), "Create Session");
}
In the advanced setup form:
private void BasicSessionSetupButton_Click(object sender, RoutedEventArgs e)
{
PageManager.GetPageManager().Close(this);
PageManager.GetPageManager().Open(new CreateBasicSessionPage(this.ViewModel), "Create Session");
}
After PageManager.Close, the only things referencing the form are the events within the ViewModel. I guess that's where I should be unhooking them.
A destructor, more commonly known to C# programmers as Finalizers, is not necessary in this case. Assuming that ViewModel_LoadCompleted is a member function, it contains a pointer to "this" which you are giving to the ViewModel object which is fully contained by "this". The garbage collector should intelligently ignore this.
In this case, the correct thing to do is to not waste time unbinding them.
In general, you need to unbind an event handler when you pass "this" (explicitly, or implicitly) to some object which will hold that reference longer than the intended lifetime of "this". For example, if you set a handler on a parent control's event. Now the parent has a reference to you via the handler as well as in its Children controls collection. In this case, you should unbind when you are removed from the parent.
When in doubt, implement IDisposable and unbind in the call to Dispose().
Events are automatically unbinded when the garbage collector goes through your object.
But you can explicitly unbind them with the "-=" syntax at anytime:
this.ViewModel.LoadCompleted -= ViewMode_LoadCompleted;
You can implement a destructor:
~MyPage
{
this.ViewModel.LoadCompleted -= ViewMode_LoadCompleted;
}

Resources