I have an ErrorPage.xaml page in my WP7 application. When an unhandled exception occurs, the method in app.xaml.cs is called and that passes the exception to the ErrorPage.xaml.cs and the error is displayed to the user with a link to go back to the main page.
If I click on the AppBar IconButton too fast, then the Navigation Failed event is raised, errorpage is still called but nothing is displayed on the error page. The AppBar is the only thing visible.
Cannot figure out why
Here is the code in my app.xaml.cs
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
e.Handled = true;
ErrorPage.Exception = e.ExceptionObject;
(RootVisual as Microsoft.Phone.Controls.PhoneApplicationFrame).Source =
new Uri(Navigation.PAGE_ERROR, UriKind.Relative);
}
Error.xaml is like this
<Grid x:Name="LayoutRoot" Background="{StaticResource GlobalPageBackgroundBrush}" CacheMode="BitmapCache">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">
<TextBlock x:Name="ApplicationTitle" Text="{StaticResource AppName}" Style="{StaticResource PhoneTextNormalStyle}" FontWeight="SemiBold"/>
<TextBlock x:Name="PageTitle" Text="error" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1">
<TextBlock x:Name="ErrorText" Style="{StaticResource PhoneTextTitle3Style}" TextWrapping="Wrap" />
<StackPanel x:Name="FriendlyErrorStackPanel" Margin="0,100,0,0" Orientation="Vertical">
<TextBlock Style="{StaticResource PhoneTextTitle3Style}" TextWrapping="Wrap">
<TextBlock.Text>
Please click the link below to return to the main page of the application.
</TextBlock.Text>
</TextBlock>
<HyperlinkButton Style="{StaticResource PhoneHyperlinkStyle}" Content="Start Over" NavigateUri="/Views/MainPage.xaml" />
</StackPanel>
</Grid>
</Grid>
and finally the ErrorPage.xaml.cs is
public partial class ErrorPage : PhoneApplicationPage
{
public ErrorPage()
{
InitializeComponent();
}
public static Exception Exception;
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (Exception != null)
{
if (System.Diagnostics.Debugger.IsAttached)
{
FriendlyErrorStackPanel.Visibility = System.Windows.Visibility.Collapsed;
ErrorText.Text = Exception.ToString();
}
else
{
FriendlyErrorStackPanel.Visibility = System.Windows.Visibility.Visible;
ErrorText.Text = GlobalConstants.DEFAULT_ERROR_MESSAGE;
}
}
base.OnNavigatedTo(e);
}
}
I suspect once it hits Application_UnhandledException and the Exception is set as Handled = true, it might get flaky if you get two in rapid succession - the way around this would be to make sure that the AppBar Button allows only one press while its trying to do something (so disable while its trying to navigate, and re-enable if it fails)
Also, consider using Dispatcher.BeginInvoke() for your Navigate method call if you aren't already doing so.
Related
I am following MSDN examples to add a settings page to my first Windows Phone 8 application (warning - I am completely new to XAML, I'm a C++ guy).
The xaml looks like this:
<phone:PhoneApplicationPage
x:Class="PicoSDU.AppSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PicoSDU"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<local:AppSettings x:Key="PicoSettings"></local:AppSettings>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="PicoSDU" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="Settings" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Margin="30,0,0,0">
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtIpAddress" Text="IP Address" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbIpAddress" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=IpSetting, Mode=TwoWay}"/>
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtPort" Text="Port Number" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbPort" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=PortSetting, Mode=TwoWay}"/>
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtSysId" Text="System ID" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbSysId" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=SysIdSetting, Mode=TwoWay}"/>
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtWsId" Text="Station ID" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbWsId" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=WsIdSetting, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
So, pretty simple. four text boxes. It rendered perfectly OK until I added the resource clause
<phone:PhoneApplicationPage.Resources>
<local:AppSettings x:Key="PicoSettings"></local:AppSettings>
</phone:PhoneApplicationPage.Resources>
As soon as I add that the XAML parser throws a wobbler and the root PhoneApplicationPage gets the old blue squiggly and reports our favourite "element is already the child of another element" error. If I remove the resource clause, that error goes away and the xaml renders, but of course the textbox bindings then all throw an error because they cannot see their resources.
I've been googling this the last three hours and I can't see what's wrong, and none of the answers I've found here and elsewhere seem to fit. Can some kind soul show me the blindingly stupid thing I've done and please put me out of my misery?
Edit
Here's the AppSettings class. It's just the Microsoft code sample, hacked into the code-behind:
namespace PicoSDU
{
public partial class AppSettings : PhoneApplicationPage
{
// Our settings
IsolatedStorageSettings settings;
// The key names of our settings
const string IpSettingKeyName = "IpSetting";
const string SysIdSettingKeyName = "SysIdSetting";
const string WsIdSettingKeyName = "WsIdSetting";
const string PortSettingKeyName = "PortSetting";
// The default value of our settings
const string IpSettingDefault = "81.179.24.51";
const string SysIdSettingDefault = "1";
const string WsIdSettingDefault = "511";
const string PortSettingDefault = "1846";
public AppSettings()
{
InitializeComponent ();
try
{
settings = IsolatedStorageSettings.ApplicationSettings;
}
catch (System.IO.IsolatedStorage.IsolatedStorageException e)
{
// handle exception
}
}
public bool AddOrUpdateValue(string Key, Object value)
{
bool valueChanged = false;
// If the key exists
if (settings.Contains(Key))
{
// If the value has changed
if (settings[Key] != value)
{
// Store the new value
settings[Key] = value;
valueChanged = true;
}
}
// Otherwise create the key.
else
{
settings.Add(Key, value);
valueChanged = true;
}
return valueChanged;
}
public T GetValueOrDefault<T>(string Key, T defaultValue)
{
T value;
// If the key exists, retrieve the value.
if (settings.Contains(Key))
{
value = (T)settings[Key];
}
// Otherwise, use the default value.
else
{
value = defaultValue;
}
return value;
}
public void Save()
{
settings.Save();
}
public string IpSetting
{
get
{
return GetValueOrDefault<string>(IpSettingKeyName, IpSettingDefault);
}
set
{
if (AddOrUpdateValue(IpSettingKeyName, value))
{
Save();
}
}
}
public string SysIdSetting
{
get
{
return GetValueOrDefault<string> ( SysIdSettingKeyName, SysIdSettingDefault );
}
set
{
if (AddOrUpdateValue ( SysIdSettingKeyName, value ))
{
Save ();
}
}
}
public string WsIdSetting
{
get
{
return GetValueOrDefault<string> ( WsIdSettingKeyName, WsIdSettingDefault );
}
set
{
if (AddOrUpdateValue ( WsIdSettingKeyName, value ))
{
Save ();
}
}
}
public string PortSetting
{
get
{
return GetValueOrDefault<string> ( PortSettingKeyName, PortSettingDefault );
}
set
{
if (AddOrUpdateValue ( PortSettingKeyName, value ))
{
Save();
}
}
}
}
}
Your code is quite bizzare. You are trying to embed one Page (your AppSettings class inherits from PhoneApplicationPage) into another. A much better approach would be to use the MVVM pattern.
Do not make the AppSettings inherit from PhoneApplicationPage and make it into a ViewModel. More info at http://msdn.microsoft.com/en-us/library/windowsphone/develop/gg521153(v=vs.105).aspx
How to get Tap event on Hub section Header?
<HubSection.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="" Foreground="#009CDE" Width="250"/>
<Image x:Name="SendToSap" Source="image/Sync_To_SAP.png" Width="40" Height="40" Margin="5,0,50,0" Tapped="SendToSap_Tapped"></Image>
</StackPanel>
</DataTemplate>
</HubSection.HeaderTemplate>
private void SendToSap_Tapped(object sender, TappedRoutedEventArgs e)
{
}
How to get this on image tap event?
Is it not working as is? Maybe you need to set IsTapEnabled="True"
If you need to add more code behind in your DataTemplate - usually you can move all the contents of the template into a UserControl and handle the events there.
I have changed my code to this:
the view:
<phone:Panorama.ItemTemplate>
<DataTemplate>
<ScrollViewer Width="800" HorizontalContentAlignment="Left" Margin="0,50,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="list_of_images" ItemsSource="{Binding ImagesUrls}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Width="300" Height="300" Source="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
<TextBlock Text="{Binding Title}"
Grid.Row="1"
Loaded="TextBlock_Loaded_1"
Margin="50,0,0,0"
FontSize="23"
TextWrapping="Wrap"
Width="360"
HorizontalAlignment="Left"
Foreground="Black"/>
<TextBox Text="{Binding ContactEmail}"
Grid.Row="2"
BorderBrush="Black"
Width="340"
HorizontalAlignment="Left"
BorderThickness="1"
Margin="40,0,0,0"
Foreground="Black" />
<TextBlock Text="{Binding Body}"
Grid.Row="3"
TextWrapping="Wrap"
Foreground="Black"
Margin="50,5,0,0"
Width="360"
HorizontalAlignment="Left"
FontSize="20" />
</Grid>
and I build a new object with different properties, with one of the properties being a list of strings which represents the imageurls, but I cannot get the images to show?
I have attached screenshots, what in my xaml must I change so that I can display the images, cause at the moment it doesn't show any images but it shows all the other details
code for populating collection:
ObservableCollection<ClassifiedAds> klasifiseerd_source = new ObservableCollection<ClassifiedAds>();
ImagesClassifieds new_Classifieds = new ImagesClassifieds();
ObservableCollection<string> gallery_images = new ObservableCollection<string>();
new_Classifieds.Title = klasifiseerd_source[0].Title;
new_Classifieds.ContactEmail = klasifiseerd_source[0].ContactEmail;
new_Classifieds.Body = klasifiseerd_source[0].Body;
foreach (var item in klasifiseerd_source[0].Gallery.Images)
{
var deserialized = JsonConvert.DeserializeObject<GalleryImages>(item.ToString());
gallery_images.Add(deserialized.ImageUrl);
//new_Classifieds.ImageUrls.Add(deserialized.ImageUrl);
}
new_Classifieds.ImageUrls = gallery_images;
// classifiedPanorama.ItemsSource = new_list;
new_Classifieds_list.Add(new_Classifieds);
classifiedPanorama.ItemsSource = new_Classifieds_list;
public class ImagesClassifieds
{
public string Title { get; set; }
public string ContactEmail { get; set; }
public string Body { get; set; }
public ObservableCollection<string> ImageUrls { get; set; }
}
here is the imageurl format, this works (in another par tof my app I simply bind to 1 image in this format and it works perfectly)
Depending on whether you want to just display a list of images or if you also want to be able to select them, you may either choose an ItemsControl or a ListBox. In both case you have to define a DataTemplate that controls how each item is displayed.
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or
<ListBox ItemsSource="{Binding Images}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then you should think about how you define your item class. Just in case you want to be able to dynamically add or remove images from the list and let the UI be automatically updated, you should use an ObservableCollection as container type. As there is a built-in type conversion from string to ImageSource (the type of the Image control's Source property), you may simply use an ObservableCollection<string>.
public class Gal
{
public ObservableCollection<string> Images { get; set; }
}
You may create an instance of that class like so:
var gallery = new Gal();
gallery.Images = new ObservableCollection<string>(
Directory.EnumerateFiles(#"C:\Users\Public\Pictures\Sample Pictures", "*.jpg"));
Now you may directly bind to that instance by simply setting the DataContext of your Window (or wherever the image list should be shown) to this instance:
DataContext = gallery;
Note the binding declaration in the ItemsControl or ListBox above was {Binding Images}. If you really have to have a property Gallery (which I assume is in MainWindow) and bind like {Binding Gallery.Images} you may set the DataContext like this:
DataContext = this;
so I basically created a loaded event on the listbox and added this code
private void list_of_images_Loaded_1(object sender, RoutedEventArgs e)
{
ListBox lst = (ListBox)sender;
lst.ItemsSource = new_Classifieds_list[0].ImageUrls;
}
which binds the itemssource of the listbox to the list of imageurls. since I cannot access the listbox from codebehind due to it being inside the panorama's itemstemplate
Trying to learn Win Phone 8, following along an online tutorial. In the tutorial, the guy uses the ListBox to show files, which is working fine for me.
However, I thought we're supposed to use LongListSelector, so I added that; but nothing shows up.
If I put the LongListSelector first in the markup, neither displays when I run the app in the emulator, so I think I'm getting an exception from binding the LongListSelector. I don't understand why though.
It's pretty simple, click a button and read files in a directory, displaying them back.
<StackPanel x:Name="ContentPanel" Margin="12,0,12,0" Grid.Row="1" >
<Button Content="Show files" Click="Button_Click_1"/>
<ListBox x:Name="lb">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<Image x:Name="img" Source="{Binding Path}" Width="100" Height="100"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<phone:LongListSelector HorizontalAlignment="Left"
x:Name="llsFiles"
ItemTemplate="{StaticResource FilesDataTemplate}"
/>
</StackPanel>
and the LLS template:
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="FilesDataTemplate">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
then the code-behind:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
GetPackageFiles();
}
private async Task GetPackageFiles()
{
//Get the folder where the app is installed on the phone.
var installFolder = Package.Current.InstalledLocation;
var imagesFolder = await installFolder.GetFolderAsync("Images");
var fileList = await imagesFolder.GetFilesAsync();
lb.ItemsSource = fileList;
llsFiles.ItemsSource = fileList.ToList();
}
Try this
//add this declaration
List<FirstList> source = new List<FirstList>();
public class FirstList
{
[DataMember]
public string cItem { get; set; }
public FirstList(string item)
{
this.cItem = item;
}
}
Then to add anything you would just do this.
source.Add(new FirstList(fileList.ToString());
make you sure you have the binding for it
I know that this question has been posted about a thousand times, but I did not find a solution that solved my problem.
I've got a LongListSelector with this ItemTemplate:
<DataTemplate x:Key="AddrBookItemTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Name}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Grid.Column="0" Grid.Row="0" />
<Button x:Name="itembutton" CommandParameter="{Binding ItemID}" Content="{Binding ButtonCaption}" Width="150" HorizontalAlignment="Right" Grid.Column="1" Grid.Row="0" Click="ItemButtonClick"/>
</Grid>
</DataTemplate>
So what happens is simply that I get this beautiful error message that I posted in the title. And I do not have a clue, why?!
private void ItemButtonClick(object sender, RoutedEventArgs e)
{
if (sender == this.itemButton) {
....
From the DataTemplate it looks like you're showing this button in a container like a ListBox or LongListSelector.
That means that there isn't an itembutton in this scope (presumably this is either UserControl or PhoneApplicationPage): there is in fact one for each of your ListBoxItems!
You'll have to find another way of finding your button.
Update:
You should find that the DataContext of your button matches the item of the list it is contained in. That's probably the easiest way of proceeding: however if all else fails you can use the Tag property as you suggest.
Like:
private void ItemButtonClick(object sender, RoutedEventArgs e)
{
var theButton = (Button) sender;
var myItem = (TypeOfAnObjectInMyList) theButton.DataContext;
doSomethingWithMyItem(myItem);