Removing Event Handler Not Working in Windows 10 Universal App - xaml

Hi i have a dynamically created button on click which will download a video in windows universal app, while creation of button i am assigning on event handler like this:
videoIcon.Click += (s, ev) => { Download_Video(s, ev, SomeStringParameter1, SomeStringParameter2); };
Once user clicks on button, in Download_Video, I am removing the event handler to download the video, like this:
Button videoIcon = sender as Button;
videoIcon.Click -= (s, ev) => { Download_Video(s, ev, videoUrl, messageId); };
and the assigning a new event handler to play video on click of same button like this:
videoIcon.Click += (s, ev) => { Video_Click(s, ev, savedFile.Name); };
The problem is previously assigned handler Download_Video also fires along with Video_Click. How to stop this?

As far as I know, this has nothing to do with Windows 10. You simply cannot unsubscribe from an anonymous event handler, as this question states.
Instead, simply keep a reference to the delegate:
RoutedEventHandler handler = (s, ev) => { Download_Video(s, ev, videoUrl, messageId); };
videoIcon.Click += handler;
videoIcon.Click -= handler;

Related

How to set cursor positioned on focused Entry tag in XAML?

I tried using the CursorPosition value but it doesn't appear to be working. The value is a single digit and the cursor needs to update to be in front of the digit when the value is tapped (or focused upon). Anyone know what might be the issue here?
public PaymentOccurrencesControl()
{
InitializeComponent();
PaymentNumberEntry.Focused += PaymentEntryField_Focused;
}
private void PaymentEntryField_Focused(object sender, FocusEventArgs e)
{
Device.BeginInvokeOnMainThread(() =>
{
PaymentNumberEntry.Focused += (s, e) =>
{
PaymentNumberEntry.CursorPosition = 1;
};
});
}
Wrap the code that sets CursorPosition in:
Device.BeginInvokeOnMainThread( () => {
…
});
This delays the cursor change until after xamarin and the phone’s OS have completed the focus action - which sets cursor position, overriding what you did.
ONLY wrap the line that sets cursor position:
public PaymentOccurrencesControl()
{
InitializeComponent();
PaymentNumberEntry.Focused += (s, e) =>
{
Device.BeginInvokeOnMainThread(() =>
{
PaymentNumberEntry.CursorPosition = 1;
};
});
}
Explanation: When the control gains focus, Android will begin executing your code after PaymentNumberEntry.Focused. It sees BeginInvoke, which tells Android "delay what's in here, until the current UI action (the control gaining focus) finishes". It queues this action:
() =>
{
PaymentNumberEntry.CursorPosition = 1;
};
This allows Android to do whatever it wants to CursorPosition (actually, to its native control's equivalent field). That's what makes the situation confusing - CursorPosition is not controlling the actual cursor position, until the next time it is given a value.
When it finishes, it returns to the MainThread loop, which looks for the next action to do. Which is your CursorPosition logic. Because your code runs last, it successfully updates Android's native cursor position.

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)

XAML: Tap a textbox to enable?

In XAML and WinRT, Is there a way to set up a textbox so that it is disabled for text input until it is tapped.
I tried setting up the Tapped event and then setting the IsEnabled=true, but that only seems to work if the IsEnabled=true in the first place.
I found this on MSDN:
http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/708c0949-8b06-40ec-85fd-201139ca8b2d
Talks about adding the TappedEvent manually to the event handled for each TextBox, which is cumbersome, but also doesn't seem to work unless IsEnabled was already set to true.
Basically, I want a form where all textboxes display data but are disabled unless the user taps to enable the box and then type.
You can use IsReadOnly instead of IsEnabled to achieve what you are looking for. In addition, you can set up the tapped event handlers in code easily. I'm not sure if setting up handlers in code is a requirement for this to work, as you noted above; however, it does simplify things.
Here are the details.
In the constructor of your page class (here it is MainPage), call the setup function:
public MainPage()
{
this.InitializeComponent();
// call the setup for the textboxes
SetupTextBoxes();
}
Here is where we do the magic - make all textboxes on this page readonly and set up tap handler:
private void SetupTextBoxes()
{
var tbs = GetVisualChildren<TextBox>(this, true);
foreach (var tb in tbs)
{
tb.IsReadOnly = true;
tb.AddHandler(TappedEvent, new TappedEventHandler(tb_Tapped), true);
}
}
Utility function to get a list of all children of the given type (T) of the passed in parent.
private List<T> GetVisualChildren<T>(DependencyObject parent, bool recurse = true)
where T : DependencyObject
{
var children = new List<T>();
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
DependencyObject v = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
var child = v as T;
if (child == null && recurse)
{
var myChildren = GetVisualChildren<T>(v, recurse);
children.AddRange(myChildren);
}
if (child != null)
children.Add(child);
}
return children;
}
Finally, the event handler. This enables each textbox when tapped.
private void tb_Tapped(object sender, TappedRoutedEventArgs e)
{
((TextBox)(sender)).IsReadOnly = false;
}

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.

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.