Where should I unregister events in XAML Behavior? - xaml

In a XAML behavior class, where should be the best place to unregister events? Should I unregister it in Unloaded event or should I do it while Deteching?
public class ShowKeyboardOnFocusBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
AssociatedObject.Unloaded += AssociatedObject_Unloaded;
}
private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
{
AssociatedObject.Loaded -= AssociatedObject_Loaded;
AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
AssociatedObject.Unloaded -= AssociatedObject_Unloaded;
}
protected override void OnDetaching()
{
AssociatedObject.Loaded += AssociatedObject_Loaded;
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
}
...

You should use OnDetaching to unregister your events. This method will always be called and it is the approach used in the official UWP Behaviors library from Microsoft (see for example EventTriggerBehavior).
It used to be an issue (that the associated object got unloaded but behaviors stayed in memory) but it is now accounted for right within the library, so you know for sure Detach will be called (you can see it yourself in the FrameworkElement_Unloaded method in Microsoft.Xaml.Interactivity.Interaction source code)

Related

CompositionTarget.Rendering doesn't like my event handler in XAML

I'm converting a Windows Phone 7 app to Windows Store, so I'm moving over to Xaml. I have a method that runs at a certain point to update the data on the screen. It either assigns or removes an event handler delegate to the CompositionTarget.Rendering event. The message I get is No overload for 'OnCompositionTargetRendering' matches delegate 'System.EventHandler' '
Here's what I have:
private void CheckCompleted()
{
Color completeColor;
if (this.DecryptedText.ToString().ToUpper() == this.ThisPuzzle.QuoteText.ToUpper())
{
// We're done!!! ...
CompositionTarget.Rendering -= this.OnCompositionTargetRendering;// new EventHandler(this.OnCompositionTargetRendering);
...
}
else
{
...
CompositionTarget.Rendering += this.OnCompositionTargetRendering;// new EventHandler(this.OnCompositionTargetRendering);
...
}
}
protected void OnCompositionTargetRendering(object sender, EventArgs args)
{
this.DisplayTime();
if (ThisPuzzle != null)
{
foreach (UIElement thisElement in Letters.Children)
{
...
}
}
}
If you check the documentation CompositionTarget.Rendering is of type EventHandler<object> in Windows Store apps and not of type EventHandler as in Silverlight.
This means you need to change the signature of your event handler accordingly to:
protected void OnCompositionTargetRendering(object sender, object args)

What the right time for registering listener for Share/Search charms

I need to register different share charm listener for every page. I have 2 pages. I added following code in every one:
DataTransferManager.GetForCurrentView().DataRequested += App_DataRequested;
I added it in constructor of one page and in UserControl_Loaded event of another (first page just doesn't have UserControl_Loaded so why I added it directly to constructor). At the moment when second page tryting to load, I got exception:
WinRT information: An event handler has already been registered
Additional information: A method was called at an unexpected time.
Where should I place it and what is "right" time to do this??
Also it looks confusing that we have different DataTransferManager for every view, but only one is active at current time. Ever more, I noticed, if you add only one listener for first page, other pages will share this listener anyway. If I have only one shared listener for all pages, is it correct register it in app.xaml.cs?
The way I resolved this issue was to deregister the event in the onNavigatedfrom event as below:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested -= App_DataRequested;
base.OnNavigatedFrom(e);
}
In BasePage.cs in constructor I added
public BasePage()
{
if (!_isListenToDataRequested)
{
_isListenToDataRequested = true;
DataTransferManager manager = DataTransferManager.GetForCurrentView();
manager.DataRequested += AppDataRequested;
}
}
private async void AppDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
IShareable shareable = Frame.Content as IShareable;
if (shareable != null)
{
DataRequestDeferral deferral = args.Request.GetDeferral();
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => shareable.AppDataRequested(sender, args));
deferral.Complete();
}
}
And all my pages look like
public sealed partial class ContentPage : IShareable
{
public void AppDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{...}
}
Another solution was run this as below
private DataTransferManager dataTransferManager;
Put this in page loaded event
this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
{
this.dataTransferManager = DataTransferManager.GetForCurrentView();
this.dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.OnDataRequested);
}));
And
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Unregister the current page as a share source.
this.dataTransferManager.DataRequested -=
new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>
(this.OnDataRequested);
}
I'd suggest doing it in the navigating events, the OnNavigatingFrom event will be triggered before the OnNavigatingTo of the page you're going to so you won't have this problem.
protected override Task OnNavigatingTo(WinRTXamlToolkit.Controls.AlternativeNavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += dataTransfer_DataRequested;
return base.OnNavigatingTo(e);
}
protected override Task OnNavigatingFrom(WinRTXamlToolkit.Controls.AlternativeNavigatingCancelEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested -= dataTransfer_DataRequested;
return base.OnNavigatingFrom(e);
}
//Note: This is the WinRT Xaml Toolkit version of the events, but the standard events will work the same way.

defining event handler for Tick event of DispatcherTimer in windows 8 app

I am developing an application in windows 8 Visual studio 11, and I want to define an event handler for a DispatcherTimer instance as below:
public sealed partial class BlankPage : Page
{
int timecounter = 10;
DispatcherTimer timer = new DispatcherTimer();
public BlankPage()
{
this.InitializeComponent();
timer.Tick += new EventHandler(HandleTick);
}
private void HandleTick(object s,EventArgs e)
{
timecounter--;
if (timecounter ==0)
{
//disable all buttons here
}
}
.....
}
But I get the following Error :
Cannot implicitly convert type 'System.EventHandler' to 'System.EventHandler<object>'
I am a novice developer to widows 8 apps.
Would you please help me ?
almost had it :) You don't need to instantiate a new eventhandler object, you only need to point to the method that handles the event. Hence, an eventhandler.
int timecounter = 10;
DispatcherTimer timer = new DispatcherTimer();
public BlankPage()
{
this.InitializeComponent();
timer.Tick += timer_Tick;
}
protected void timer_Tick(object sender, object e)
{
timecounter--;
if (timecounter == 0)
{
//disable all buttons here
}
}
Try to read up on delegates to understand events Understanding events and event handlers in C#
Your code is expecting HandleTick to have two Object params. Not an object param and an EventArg param.
private void HandleTick(object s, object e)
NOT
private void HandleTick(object s,EventArgs e)
This is a change that took place for Windows 8.
WinRT makes use of Generics more than the standard .NET Runtime. DispatcherTimer.Tick as defined in WinRT is here:
public event EventHandler<object> Tick
While the WPF DispatcherTimer.Tick is here
public event EventHandler Tick
Also note that you don't have to use the standard named method to create an Event Handler. You can use a lambda to do it in place:
int timecounter = 10;
DispatcherTimer timer = new DispatcherTimer();
public BlankPage()
{
this.InitializeComponent();
timer.Tick += (s,o)=>
{
timecounter--;
if (timecounter == 0)
{
//disable all buttons here
}
};
}

Silverlight & events

namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
datePicker1.SelectedDateChanged -= datePicker1_SelectedDateChanged;
datePicker1.SelectedDate = DateTime.Today;
datePicker1.SelectedDateChanged += datePicker1_SelectedDateChanged;
}
private void datePicker1_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
MessageBox.Show("datePicker1_SelectedDateChanged");
}
}
}
Why message box appears?
datePicker1.SelectedDateChanged -= datePicker1_SelectedDateChanged;
//Defines you use the RemoveHandler function. you might want to remove event handlers during the application lifetime
datePicker1.SelectedDate = DateTime.Today;
// You are Assigning to Datepicker Selected date
datePicker1.SelectedDateChanged += datePicker1_SelectedDateChanged;
// the event handlers on the relevant object are ready for user-initiated events at run time.
So The Message Box Came .
Third Line You Have Call The Eventhadler That Y it will come

CF keyDown event (timed)

I need an event for my CF application, that would trigger after user has pressed an held his finger on the control for 2 seconds. What event can i use, since keyDown event is already used.
Well, KeyDown is pretty irrelevant for capturing the length of time a finger is pressed. The use of the finger relates to the events Click, MouseDown, MouseUp and MouseMove.
To get the behaviour you're after, the events you should be interested in are MouseDown and MouseUp.
I suggest the best way to do this would be to create your own control base class. Here's one I made earlier (not tested, but should give you a general idea of what to do):
public partial class BaseControl : UserControl
{
public BaseControl()
{
InitializeComponent();
base.MouseDown += new MouseEventHandler(BaseControl_MouseDown);
base.MouseUp += new MouseEventHandler(BaseControl_MouseUp);
MouseHeldTimer = new Timer();
MouseHeldTimer.Interval = 2000;
MouseHeldTimer.Tick += new EventHandler(mouseHeldTimer_Tick);
}
protected Timer MouseHeldTimer;
protected bool MouseIsDown;
void mouseHeldTimer_Tick(object sender, EventArgs e)
{
this.MouseHeldTimer.Enabled = false;
if (this.MouseHeldDown != null)
{
this.MouseHeldDown(sender, e);
}
}
void BaseControl_MouseDown(object sender, MouseEventArgs e)
{
this.MouseHeldTimer.Enabled = true;
}
void BaseControl_MouseUp(object sender, MouseEventArgs e)
{
this.MouseHeldTimer.Enabled = false;
}
public event MouseHeldDownHandler MouseHeldDown;
public delegate void MouseHeldDownHandler(object sender, EventArgs e);
}
Basically, the MouseHeldTimer will start with an interval of 2 seconds the moment the user touches their finger to the screen. If the user lifts their finger the timer is stopped. If the user's finger is down for longer than 2 seconds, the delegate event MouseHeldDown will fire. You can then capture this event on your form by doing the following:
control.MouseHeldDown+= new EventHandler(control_MouseHeldDown);
Alternatively, if you only care about the form, you can just use the Form's DoubleClick event as that will fire after holding the mouse down for a second or two.