In my application there is a page with one check box.
What I want is that whenever user press back button button.
If check box is checked then un check it
If check box is not checked then perform its functionality, like going back.
Updated:
This line is in page constructor.
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
This is event.
void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e)
{
if (RechargeAccountPivot.SelectedIndex == 2 && ePayBorder.Visibility == Windows.UI.Xaml.Visibility.Visible)
{
ePayBorder.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
e.Handled = true;
}
}
this code is running on bac press, but after this code executes the application page go back too.
I think its worth telling you that I am using basic page, which has these lines of code by default.
private NavigationHelper navigationHelper;
public RechargeAccount()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
I think this variable is handling navigation some where but un able to identify.
You can use Windows.Phone.UI.Input.HardwareButtons.BackPressed event as suggested here.
private void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e) {
if (checkbox.IsChecked) {
checkbox.IsChecked = false;
e.Handled = true;
}
}
Question seems to be a duplicate though.
Update:
Since you are using the NavigationHelper class, this class handles the BackPressed event and performs navigation by it's own:
/// <summary>
/// Invoked when the hardware back button is pressed. For Windows Phone only.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e) {
if (this.GoBackCommand.CanExecute(null)) {
e.Handled = true;
this.GoBackCommand.Execute(null);
}
}
So setting e.Handled to true has no effect in this case. In order to have control over navigation, you may edit the NavigationHelper class (it exists in "Common" directory of the project).
First, replace that part of the class with this:
public event EventHandler<Windows.Phone.UI.Input.BackPressedEventArgs> BackPressed;
private void OnBackPressed(Windows.Phone.UI.Input.BackPressedEventArgs e) {
if (this.BackPressed != null) {
this.BackPressed(this, e);
}
}
/// <summary>
/// Invoked when the hardware back button is pressed. For Windows Phone only.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the event.</param>
private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e) {
this.OnBackPressed(e);
if (!e.Handled) {
if (this.GoBackCommand.CanExecute(null)) {
e.Handled = true;
this.GoBackCommand.Execute(null);
}
}
}
Then use the new defined BackPressed event of the NavigationHelper class in your page:
private NavigationHelper navigationHelper;
public RechargeAccount()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
this.navigationHelper.BackPressed += this.NavigationHelper_BackPressed;
}
void NavigationHelper_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e)
{
if (RechargeAccountPivot.SelectedIndex == 2 && ePayBorder.Visibility == Windows.UI.Xaml.Visibility.Visible)
{
ePayBorder.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
e.Handled = true;
}
}
At last found this post to control the back button navigation and its working perfectly, Mehrzad Chehraz answer was similar to this but not completed.
Related
I am trying to make a small music player for windows phone. I have added a slider functionality in the player. The slider works fine as the music plays. But I want to change the media according to how much i drag the slider, but cannot find any relevant event for it. I have tried value changed but it does not help. Also I tried Thumb.Dragstarted event but my visual studio gives an error.. this is the code so far:
XAML:
<Slider AllowDrop="True" x:Name="sld1" Thumb.DragStarted="sld1_DragStarted" HorizontalAlignment="Left" Margin="58,213,0,0" VerticalAlignment="Top" Width="351" ValueChanged="sld1_ValueChanged"/>
<MediaElement x:Name="bleep" Source="abcd.wav" AutoPlay="False" Visibility="Collapsed" MediaEnded="bleep_MediaEnded"/>
C#:
public Page1()
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
private bool userIsDraggingSlider = false;
private void timer_Tick(object sender, EventArgs e)
{
if ((bleep.Source != null) && (bleep.NaturalDuration.HasTimeSpan) && (!userIsDraggingSlider))
{
sld1.Minimum = 0;
sld1.Maximum = bleep.NaturalDuration.TimeSpan.TotalSeconds;
sld1.Value = bleep.Position.TotalSeconds;
}
}
private void sld1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
tm_passed.Text = TimeSpan.FromSeconds(sld1.Value).ToString(#"mm\:ss");
}
private void sld1_DragStarted(object sender, DragStartedEventArgs e)
{
userIsDraggingSlider = true;
}
private void sld1_DragCompleted(object sender, DragCompletedEventArgs e)
{
userIsDraggingSlider = false;
bleep.Position = TimeSpan.FromSeconds(sld1.Value);
}
But since the DragCompleted and DragStarted events are not working I cannot provide the drag functionality to the slider.
What I identified from the Thumb class is that, you can't simply add Thumb.DragStarted="sld1_DragStarted within your Slider. You'll be able to add that kind of event only for Thumb control. Refer the bottom of the article for sample code.
what would be the steps to add timer to change selected item's image in listpicker. Any suggestions? FYI, have never used ListPicker before. So i am finding it kind of hard to understand where to start and what to do.
You will need an ObservableCollection of your ImageSources and a DispatcherTimer to fire the events every TimeSpan of your choosing.
Here's some code to help you get started. You can modify it to do exactly what you want. It basically contains a ListPicker that has a collection of images as its ItemTemplate. Every one second the DispatchTimer fires and switches the selectedItem's Image between the 2 default images that are created in about every single WP8.0 application.
Make it a habit to use ObervableCollection when you want to display something to the user instead of a List, it will make your WP8 development life a lot easier.
XAML
<toolkit:ListPicker x:Name="my_listpicker" SelectionChanged="my_listpicker_SelectionChanged_1" Background="Black">
<toolkit:ListPicker.HeaderTemplate>
<DataTemplate/>
</toolkit:ListPicker.HeaderTemplate>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Background="Black">
<Image Source="{Binding ImageSource}" Height="200"></Image>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
C# Namespaces
using System.ComponentModel; // ObservableCollection
using System.Collections.ObjectModel; // INotifyPropertyChanged
using System.Windows.Threading; // Dispatch Timer
C# Model of your Images (pretty basic, but pay attention to the INotifyPropertyChanged
public class MyBindingImage : INotifyPropertyChanged
{
public MyBindingImage() { }
public MyBindingImage(string source)
{
this.ImageSource = source;
}
// Create the OnPropertyChanged method to raise the event
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
string image_source;
public String ImageSource {
get { return image_source; }
set
{
image_source = value;
OnPropertyChanged("ImageSource");
}
}
}
C# (Create the Timer and ObservableCollection and Set the ItemSource)
DispatcherTimer timer;
// Constructor
public MainPage()
{
// create our dispatch timer
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(2000);
timer.Tick += OnTimerTick;
InitializeComponent();
// create our list picker elements
ObservableCollection<MyBindingImage> my_image_list = new ObservableCollection<MyBindingImage>();
my_image_list.Add(new MyBindingImage("Assets/ApplicationIcon.png"));
my_image_list.Add(new MyBindingImage("Assets/AlignmentGrid.png"));
my_listpicker.ItemsSource = my_image_list;
}
C# Events (For the Timer & ListPicker SelectionChange)
// each time the selection has changd: stop the timer, then start it again
private void my_listpicker_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
if (timer != null)
{
timer.Stop();
timer.Start();
}
}
// if the timer is on, cycle the images of the selected item
private void OnTimerTick(object sender, EventArgs e)
{
try
{
MyBindingImage item = (MyBindingImage) my_listpicker.SelectedItem;
// cycle the selected image between to different images
if (item.ImageSource == "Assets/AlignmentGrid.png")
{
item.ImageSource = "Assets/ApplicationIcon.png";
}
else
{
item.ImageSource = "Assets/AlignmentGrid.png";
}
}
catch (Exception ex)
{
string error_message = ex.Message;
}
}
[APPLICATION SCREENSHOT]
How can I make the background agent execution to wait before all ImageOpened() events are fired (3 in this case) in order to update secondary live tile with custom images?
Edit 1:
In the OnInvoke() method of the ScheduledAgent I am calling my own create tile data function implemented in a shared library which in turn subscribes to 3 ImageOpened() events as I am trying to create custom images for all live tile templates i.e. small, medium and wide.
Since these being asynchronous events I have no way to check if all the events have completed successfully so that I can call NotifyComplete() to notify the background agent that its job is now done. So sometimes the tile gets updated while most of the times it doesn't. Also I am using the same function to update the live tiles every time the app is launched so there is no problem with its implementation. I have also tried to take care of all the memory limitations with the ScheduledAgent by disposing Bitmaps and calling GC.Collect() forcefully.
Please help in any possible way to fix this problem.
Add a new class that lets you create custom events -
public class SaveImageCompleteEventArgs : EventArgs
{
public bool Success { get; set; }
public Exception Exception { get; set; }
public string ImageFileName { get; set; }
public SaveImageCompleteEventArgs(bool success, string fileName)
{
Success = success;
ImageFileName = fileName;
}
}
Initialize the events and required variables in the file you are updating the custom live tile from -
public static int countTile = 3;
public event EventHandler<SaveImageCompleteEventArgs> SaveMediumImageComplete;
public event EventHandler<SaveImageCompleteEventArgs> SaveWideImageComplete;
public event EventHandler<SaveImageCompleteEventArgs> SaveSmallImageComplete;
public event EventHandler<SaveImageCompleteEventArgs> SaveAllImagesComplete;
Fire the completion event in the ImageOpened() event handlers for all the tiles and check if the SaveAllImagesComplete event needs to be fired-
public void OnBackgroundBmpOpenedMedium(object sender, RoutedEventArgs e)
{
if (SaveMediumImageComplete != null)
{
countTile -= 1;
CheckIfAllImagesOpened();
SaveMediumImageComplete(this, new SaveImageCompleteEventArgs(true, mediumTileImageUriIronMan));
}
}
private void CheckIfAllImagesOpened()
{
if (countTile == 0)
{
if (SaveAllImagesComplete != null)
{
var args1 = new SaveImageCompleteEventArgs(true, "");
SaveAllImagesComplete(this, args1);
}
}
}
In the ScheduledAgent file -
public static ManualResetEvent evt;
public bool IsPaused { get { return !evt.WaitOne(0); } }
In the OnInvoke() function -
evt = new ManualResetEvent(false);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
//Initialize secondary tile here
if (secondaryTile != null)
{
/*
obj is a object of a helper file that contains all the
functions responsible for updating the custom live tile
Call the function that is responsible for initializing all the
tile image bitmpas and that subscribes to the ImageOpened events
*/
obj.SaveMediumImageComplete += async (s, args) =>
{
if (!IsPaused)
evt.Set();
};
obj.SaveWideImageComplete += async (s, args) =>
{
if (!IsPaused)
evt.Set();
};
obj.SaveSmallImageComplete += async (s, args) =>
{
if (!IsPaused)
evt.Set();
};
obj.SaveAllImagesComplete += async (s, args) =>
{
try
{
if (args.Success)
obj.UpdateTileIcon();
}
catch (Exception) { }
finally
{
if (!IsPaused)
evt.Set();
}
};
}
});
evt.WaitOne();
NotifyComplete();
I am working on a Windows Phone 8 App which should be protected with a passcode. What is the best way to show the passcode screen everytime the app is lauchend or activated?
I think the central point of action shoule be the App.xaml.cs with its Launch and Activation event handlers. But how exactly can I show the passcode screen?
The problem is, that one never know which pages will be displayed when the app launches or is reactivated. It is either the main page or any other page which was last displayed when the app was deactivated.
I tried to intercept the navigation to the first page, cancel it and show the passcode page instead:
// App.xaml.cs
private void InitializePhoneApplication() {
...
RootFrame.Navigating += HandleFirstNavigation;
...
}
private void HandleFirstNavigation(object sender, NavigatingCancelEventArgs e) {
RootFrame.Navigating -= HandleFirstNavigation;
e.Cancel = true;
RootFrame.Dispatcher.BeginInvoke(new Action(this.OpenPasscodePage));
}
private void OpenPasscodePage() {
RootFrame.Navigate(PasscodePageUri);
}
This works, but only when the app lauchend. When the app reactivated (dormant or tombstoned) the e.Cancel is irgnored. Although the navigation to the passcode page is called the original page is shown.
Moving the navigation the the passcode page from Navigating to Navigated does not worth either:
private void InitializePhoneApplication() {
...
RootFrame.Navigated += PasscodePageAfterFirstNavigation;
...
}
private void PasscodePageAfterFirstNavigation(object sender, EventArgs e) {
RootFrame.Navigated-= PasscodePageAfterFirstNavigation;
RootFrame.Navigate(PasscodePageUri);
}
This seems to be some kind of race condition: Sometimes the passcode page is shown, sometimes the original page. Even if the passcode pages comes up this looks bad because one first see the original page for the fraction of a second before the app navigates further to the passcode page.
Both solution do not work. Any idea what is the right way to implement this?
EDIT: Meanwhile I tried a third solution which does not work either. This solution uses the Uri Mapper:
App.xaml.cs
public bool PasscodeWasConfirmed; private void Application_Launching(object sender, LaunchingEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
private void Application_Activated(object sender, ActivatedEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
public Uri InitialPageUri;
public bool ShouldRedirectToPasscodePage(Uri uri) {
if (PasswordWasConfirmend == false) {
InitialPageUri = uri;
return true;
}
return false;
}
UriMapper
public class AppUriMapper : UriMapperBase {
public override Uri MapUri(Uri uri) {
App app = (Application.Current as App);
if (app != null) {
if (app.ShouldRedirectToPasscodePage(uri))
return PasscodeQueryPage.PageUri;
}
// default
return uri;
}
}
PasscodePage
public partial class PasscodePage : PhoneApplicationPage {
...
private void PasscodeConfirmed() {
App app = (Application.Current as App);
app.PasscodeWasConfirmed = true;
NavigationService.Navigate(app.InitialPageUri);
}
}
The Logic is working without any problem, but the app does not navigate to InitialPageUri after the passcode was confirmed. The Uri Mapper is called and correctly and returns the InitialPageUri (no redirect any more). But no navigation happens...
There are no errors, exceptions or debug output. simply nothing happes...
Biggest problem when using Uri Mapper:
When the app is reactivated from Dormant state there is no navigation which could be mapped or redirected...
(I've edited previous answer instead of adding a new one)
I've spend a little time trying to find a solution, and I don't see why your code doesn't run.
In my case it's enough if I do such a change in App.xaml:
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
This works on my example which is under the link http://sdrv.ms/1ajH40E
But - I cannot prevent user from seeing last screen when he holds back buton and is chosing to which app return, and then for a blink he can see the last page before leaving the app. I don't know if it is possible to change this behaviour after clicking MS Button:
windows phone change deactivated app image
Second edit
Ok - maybe I've found solution why it sometiems work and sometimes not in your code. After pressing the Start or Search buton the App can go to two cases: Tombstone and non-tombsone. After return different events happen. Code above works with Tombstone case but not with non-tombstone. To work it with the second you need to add (because page is not initialized again) - (of course it can be different solution):
bool afterActivation = false;
private void Application_Activated(object sender, ActivatedEventArgs e)
{
afterActivation = true;
}
private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
if (afterActivation)
{
afterActivation = false;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
}
Please also ensure of your debug properties in VS: Project->Properties->Debug->Tombstone upon deactiovation checkbox.
You can also find some information here (if you haven't seen it before):
http://blogs.msdn.com/b/ptorr/archive/2010/12/11/how-to-correctly-handle-application-deactivation-and-reactivation.aspx
I made my own slider as user control with some custom properties and one custom event. Everything works fine, but recently I start using Caliburn Micro, and I don't know how to capture my custom event.
Previously I used:
<my:RadialSlider x:Name="slider" WedgeAngle="270" ..... AngleChanged="slider_AngleChanged" />
and
public void slider_AngleChanged(object sender, ValueChangedEventArgs e)
{
.... something ....
}
Now, in Caliburn project I tried:
<my:RadialSlider x:Name="slider" WedgeAngle="270" ..... cal:Message.Attach="[Event AngleChanged] = [Action slider_AngleChanged($eventArgs)]" />
and in my ViewModel:
public void slider_AngleChanged(object sender, ValueChangedEventArgs e)
{
.... something ....
}
But, this doesn't work...
So, how to capture this event?
Slider UC code-behind:
public delegate void AngleChangedEventHandler(object sender, ValueChangedEventArgs e);
public sealed partial class RadialSlider : UserControl
{
public event AngleChangedEventHandler AngleChanged;
private void OnAngleChanged(ValueChangedEventArgs e)
{
if (AngleChanged != null)
AngleChanged(this, e);
}
public static readonly DependencyProperty WedgeAngleProperty = DependencyProperty.Register("WedgeAngle", typeof(double), typeof(RadialSlider), new PropertyMetadata((double)270, new PropertyChangedCallback(OnPropertyWedgeAngleChanged)));
public double WedgeAngle
{
get { return (double)this.GetValue(WedgeAngleProperty); }
set { this.SetValue(WedgeAngleProperty, value); }
}
private static void OnPropertyWedgeAngleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as RadialSlider).UpdateControls();
if (e.NewValue != e.OldValue)
{
(sender as RadialSlider).OnAngleChanged(new ValueChangedEventArgs((double)e.OldValue, (double)e.NewValue));
}
}
}
You need to use a routed event. This has to do with how events bubble up the visual tree and how Caliburn.Micro attaches itself to them. Standard events should be avoided on controls or UI widgets in any tech using Xaml as the loose out on some pretty funky features (bubble up / down).