Cool to see a code sample I wrote for bolser and Nokia getting the spotlight!
http://blogs.windows.com/windows_phone/b/wpdev/archive/2013/10/17/inside-windows-phone-64-rate-my-app.aspx
Geoff Webber-Cross - .Net, Windows 8, Silverlight and WP developer. Software problem solver. My Website Obelisk - WP7 MVVM Tombstone Library
Showing posts with label WP7. Show all posts
Showing posts with label WP7. Show all posts
Friday, 18 October 2013
Thursday, 18 July 2013
WP7 & WP8 XAP Windows Phone App
I've recently been having problems with an app that has a WP7 and WP8 package.
At first the package had the same version when submitted and WP8 devices correctly got the WP8 XAP and WP7 devices got the WP7 XAP.
Suddenly with no change, WP8 devices started getting the WP7 XAP.
We put in a ticket with the store team and the mentioned the versions had to be different, so I updated the WP8 package higher than the WP7 one and submitted it, in accordance with this guideline:
http://blogs.windows.com/windows_phone/b/wpdev/archive/2013/01/15/app-submission-tips-for-windows-phone-7-and-windows-phone-8.aspx
This still did not help.
We asked support again and they examined the XAPs and started going on about one targeting 8.0 and one 7.1 AND 8.0. Well of course the 7 one will target both, because it's supposed to, but surely the store should be able to work it out?
We resubmitted a few times and nothing worked and we kept getting responses about the 7.1 and 8 thing.
Eventually I decided to up-version both XAPs with a minor fix together and that finally seemed to work!
Here's my check list for future reference:
I've got another minor update to push out this week so will see if it works again!
At first the package had the same version when submitted and WP8 devices correctly got the WP8 XAP and WP7 devices got the WP7 XAP.
Suddenly with no change, WP8 devices started getting the WP7 XAP.
We put in a ticket with the store team and the mentioned the versions had to be different, so I updated the WP8 package higher than the WP7 one and submitted it, in accordance with this guideline:
http://blogs.windows.com/windows_phone/b/wpdev/archive/2013/01/15/app-submission-tips-for-windows-phone-7-and-windows-phone-8.aspx
This still did not help.
We asked support again and they examined the XAPs and started going on about one targeting 8.0 and one 7.1 AND 8.0. Well of course the 7 one will target both, because it's supposed to, but surely the store should be able to work it out?
We resubmitted a few times and nothing worked and we kept getting responses about the 7.1 and 8 thing.
Eventually I decided to up-version both XAPs with a minor fix together and that finally seemed to work!
Here's my check list for future reference:
- Make sure the WP8 package has a higher version than the WP7 one in the WMAppManifest
- Make sure the WP8 package has a higher version than the WP7 one on the store
- Submit 2 XAPs at the same time
I've got another minor update to push out this week so will see if it works again!
Friday, 17 August 2012
Obelisk Windows 8 & WP7 MVVM Persistence Library V2.2 Release
I've just finished the Obelisk V2.2 release. It's now got support for Windows 8 persistence as well as WP7 persistence. There is a basic MVVM pattern example and an MVVM Light example for WP7 and Windows 8 The MSI only contains source for WP7 because I can't create an MSI in VS2012 yet, so you need to download the source to get the samples:
http://obelisk.codeplex.com/
http://obelisk.codeplex.com/
Monday, 16 April 2012
ScrollViewer AutoScroll Behavior
Overview
This behavior causes a ScrollViewer to automatically scroll to the bottom when it's contents change size. It is useful for TextBox controls inside ScrollViewers so that the cursor doesn't disappear whilst typing.
The Behavior
This behavior causes a ScrollViewer to automatically scroll to the bottom when it's contents change size. It is useful for TextBox controls inside ScrollViewers so that the cursor doesn't disappear whilst typing.
The Behavior
public class AutoScrollBehavior
: Behavior<ScrollViewer>
{
private ScrollViewer
_scrollViewer = null;
private double
_height = 0.0d;
protected override void OnAttached()
{
base.OnAttached();
this._scrollViewer = base.AssociatedObject;
this._scrollViewer.LayoutUpdated += new EventHandler(_scrollViewer_LayoutUpdated);
}
private void
_scrollViewer_LayoutUpdated(object sender, EventArgs e)
{
if (this._scrollViewer.ExtentHeight
!= _height)
{
this._scrollViewer.ScrollToVerticalOffset(this._scrollViewer.ExtentHeight);
this._height = this._scrollViewer.ExtentHeight;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
if (this._scrollViewer
!= null)
this._scrollViewer.LayoutUpdated -= new EventHandler(_scrollViewer_LayoutUpdated);
}
}
Implementation in XAML
<ScrollViewer Height="200">
<i:Interaction.Behaviors>
<cmd:AutoScrollBehavior />
</i:Interaction.Behaviors>
<TextBox IsEnabled="{Binding Path=
IsCommentEnabled}" Text="{Binding Path=Comment, Mode=TwoWay}" MinHeight="200"
AcceptsReturn="True" InputScope="Text" TextWrapping="Wrap">
</TextBox>
/ScrollViewer>
Thursday, 12 April 2012
MVVM Light - Passing Params to Target ViewModel Before Navigating - Part II
After a lot more thinking about this, I've come up with a really neat solution, needs more testing, but here it is in current form. There's a MessageSender and MessageReceiver class which between them handle sending InitMessageBase type objects and allow buffering and requesting of a message if target is not constructed.
Example code here
Part 1 here
MessageSender
InitMessageBase
An Implementation of InitMessageBase
Sending a Message
Simply create a global instance of the MessageSender and send a message or value:
Example code here
Part 1 here
MessageSender
public class MessageSender<T> where
T : InitMessageBase
{
private T _message = default(T);
public MessageSender()
{
// Send back item when a constructor asks for it
Messenger.Default.Register<T>(this, message =>
{
if (this._message
!= null && this._message
!= message && !this._message.Received)
{
// Send back original message
Messenger.Default.Send(this._message);
}
});
}
public void
SendMessage(T message)
{
// Store value
this._message = message;
// Try and send message
Messenger.Default.Send(message);
}
}
MessageReceiver
public class MessageReceiver<T> where T : InitMessageBase
{
private Action<T>
_handler = null;
private T _message = null;
public MessageReceiver(Action<T>
handler)
{
this._handler = handler;
// Wait for messages
Messenger.Default.Register<T>(this, message =>
{
if (this._message
!= null && this._message
!= message && !message.Received)
{
message.Received = true;
this.OnMessageReceived(message);
}
});
}
/// <summary>
/// Send empty message on
instantiation
/// </summary>
/// <param name="message"></param>
public MessageReceiver(Action<T>
handler, bool sendInitMessage)
:
this(handler)
{
if (sendInitMessage)
{
this._message
= Activator.CreateInstance(typeof(T))
as T;
// Send empty message
Messenger.Default.Send<T>(this._message);
}
}
private void
OnMessageReceived(T message)
{
if (this._handler != null)
this._handler(
message );
}
}
public abstract class InitMessageBase : MessageBase
{
// Used for a send
public bool Received
{ get; set; }
public InitMessageBase()
{
}
}
public class ArticleInitMessage : InitMessageBase
{
public Article Value { get; set; }
public ExampleInitMessage()
{
}
public ExampleInitMessage(
Article value)
{
this.Value = value;
}
}
if (this._articleSender == null)
this._articleSender = new MessageSender<ArticleInitMessage>();
this._articleSender.SendMessage(new
ArticleInitMessage(article));
this._navService.NavigateTo(ViewModelLocator.ArticleUri);
Receiving a Message
Simply create a global instance of MessageReceiver at VM construct time and pass it an action to do:
this._articleReceiver = new MessageReceiver<ArticleInitMessage>((message) =>
{
this.SelectedItem = message.Value;
}, true);
Thursday, 5 April 2012
MVVM Light - Passing Params to Target ViewModel Before Navigating
Overview
I use MVVM Light for my WP7 apps, it's great! I implement the following navigation service for controlling navigation from the view model layer:
http://blog.galasoft.ch/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx
Part 2 Here
The main problem I've faced is that I often want to pass parameters to the target view's view model to prepare it for what it needs to do. It is possible to access properties in the target view model through the ViewModelLocator as each view model has a static property used for the view binding via the non-static properties, however this means that the view models are tightly coupled through the VML and it seems like a bit of a hack.
The example code is from an article page which requires navigation to a links page.
Messaging
It's possible to fire a message out before navigating to pass parameters to the target view model, however, if the target view has never been hit, the associated vm will not not have instantiated so will not receive the message! This can be worked around by calling the VML CreateXXX method however this is still not the elegant solution I was hoping for.
2-Way Messaging
To solve this problem, I figured that when a VM instantiates, ask if any other VM has any parameters for it then register a message pipe for parameters when it is instantiated.
The Message
I created a message which would take a parameter to pass from the source to the target or an action to request a parameter from the source, by the target:
Source VM
First off messages need registering (at constructor stage) so that the target VM may message back one it constructs:
Conclusion
That's it, seems like a nice loosely coupled way of initialising a view model on navigation.
I use MVVM Light for my WP7 apps, it's great! I implement the following navigation service for controlling navigation from the view model layer:
http://blog.galasoft.ch/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx
Part 2 Here
The main problem I've faced is that I often want to pass parameters to the target view's view model to prepare it for what it needs to do. It is possible to access properties in the target view model through the ViewModelLocator as each view model has a static property used for the view binding via the non-static properties, however this means that the view models are tightly coupled through the VML and it seems like a bit of a hack.
The example code is from an article page which requires navigation to a links page.
Messaging
It's possible to fire a message out before navigating to pass parameters to the target view model, however, if the target view has never been hit, the associated vm will not not have instantiated so will not receive the message! This can be worked around by calling the VML CreateXXX method however this is still not the elegant solution I was hoping for.
2-Way Messaging
To solve this problem, I figured that when a VM instantiates, ask if any other VM has any parameters for it then register a message pipe for parameters when it is instantiated.
The Message
I created a message which would take a parameter to pass from the source to the target or an action to request a parameter from the source, by the target:
public class LinksInitMessage : MessageBase
{
public Article
Payload { get; set;
}
public Action<Article> Callback { get;
set; }
public LinksInitMessage(Article
payload)
{
this.Payload = payload;
}
public LinksInitMessage(Action<Article> callback)
{
this.Callback = callback;
}
}
Source VM
First off messages need registering (at constructor stage) so that the target VM may message back one it constructs:
private void
RegisterMessenger()
{
//Register any messages
Messenger.Default.Register<LinksInitMessage>(this,
msg =>
{
if (this._linksInitItem
!= null)
{
msg.Callback(this._linksInitItem);
this._linksInitItem = null;
}
});
}
Then when the source VM is ready to navigate , it can send out a message and keep a parameter in case the VM calls back, then navigate:
private void Link()
{
// Send message to Links (it will get it if it's there
otherwise, the VM will request when open)
Messenger.Default.Send<LinksInitMessage>(new LinksInitMessage(this._selectedItem));
// Store article incase Links requests it
this._linksInitItem = this._selectedItem;
// Nav to links
_navService.NavigateTo(ViewModelLocator.LinksUri);
}
Target VM
First off when the VM constructs, it sends out a message to ask if any other VM has any a message for it, if it gets a response it can initialise itself. Second it registers a message type so once it is already instantiated, it can receive params again and re-initialise itself:
private void
RegisterMessenger()
{
//Send out a message to see if another VM has params we
need
Messenger.Default.Send<LinksInitMessage>(new LinksInitMessage(article
=>
{
if (article != null)
{
this.SelectedItem = article;
}
}));
//Register any message pipes
Messenger.Default.Register<LinksInitMessage>(this,
msg =>
{
this.ClearViewModel();
this.SelectedItem = msg.Payload;
});
}
Thursday, 19 January 2012
WP7 HTTP Using Sockets & Problem With HttpWebRequest
Why Would You Want To Do This?
The first question you will have unless you found this article because you know what the problem is already is: why would you want to use sockets when there is a perfectly good way of sending and receiving data using WebClient and HttpWebRequest objects? Well the problem explains it.
The Problem
I recently had to integrate a WP7 Mango app with the Disqus API. I set out thinking it would be fairly straight forward, but as soon as I started work on it I ran into a big problem. I was using HttpWebRequests to do a simple HTTP GET, here's the code:
private void button1_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri("http://disqus.com/api/3.0/threads/listPosts.json?forum=abcd&thread:ident=12345&api_key=abcdef0123456789"));
req.BeginGetResponse(EndGetResponse, req);
}
private void EndGetResponse(IAsyncResult a)
{
try
{
HttpWebRequest req = a.AsyncState as HttpWebRequest;
HttpWebResponse res = req.EndGetResponse(a) as HttpWebResponse;
Stream s = res.GetResponseStream();
StreamReader str = new StreamReader(s);
string data = str.ReadToEnd();
}
catch (Exception ex)
{
string s = ex.ToString();
}
}
Normally this should work fine, but an exception was being thrown at EndGetResponse:
System.Net.WebException: The remote server returned an error: NotFound. --->
Now this exception is not very helpful so you need to use a protocol analyser (I use WireShark, I gave up with Fiddler because I never got it working with the phone!) to find out what the problem is. It turns out that it's 400 Bad Response code:
Now if you profile a browser making the same request you notice some differences:
For some reason (I don't know what) WP7 (.Net doesn't do this) adds a "Referer" header with a reference to some location on the device. The API server doen't like this and rejects the request. This is the problem.
Possible Solutions
One obvious solution would be to remove the offending header, but this is not possible. Another solution would be to change the "Referer" header value, this is possible but doesn't work. After some thought I realised that since HTTP is only a layer on top of TCP and since MS have kindly given us Sockets to use in the Mango update that Sockets are the solution.
Sockets
First job is to get a new client to handle asynchronous TCP requests, there's a nice MS example for a Tic-Tac-Toe game (Noughts and Crosses if you're from the UK!). This is the example download link: http://go.microsoft.com/fwlink/?LinkId=219075
The AsynchronousSocketClient is the bit we're interested in, but it needs some adjustment because we need the socket to keep receiving data until there is no more. I changed the ProcessReceive method to this:
// Called when a ReceiveAsync operation completes
private void ProcessReceive(SocketAsyncEventArgs e)
{
Socket sock = e.UserToken as Socket;
if (e.SocketError == SocketError.Success)
{
// Received data from server
dataFromServer += Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
// More data to receive
if (e.BytesTransferred > 0)
{
//Read data sent from the server
sock.ReceiveAsync(e);
}
else
{
sock.Shutdown(SocketShutdown.Both);
sock.Close();
sock.Dispose();
ResponseReceivedEventArgs args = new ResponseReceivedEventArgs();
args.response = dataFromServer;
OnResponseReceived(args);
}
}
else
{
if (retries < MAX_RETRIES)
{
retries++;
try
{
dataFromServer = string.Empty;
sock.ConnectAsync(socketEventArg);
}
catch (SocketException ex)
{
throw new SocketException(ex.ErrorCode);
}
}
else
{
sock.Close();
sock.Dispose();
ResponseReceivedEventArgs args = new ResponseReceivedEventArgs();
args.isError = true;
OnResponseReceived(args);
}
}
}
The method calls ReceiveAsync again if BytesTransferred is not 0. I also put in a retry loop in because I found on the emulator, the connection seemed to get reset a lot which was annoying (it works nicely on the phone).
HTTP Over Sockets
Now we have a way of sending and receiving data over TCP, we need to work out how to do an HTTP GET. This is where
WireShark comes in again to look at the structure of the message.
The implemented code looks like this:
private void button2_Click(object sender, RoutedEventArgs e)
{
// GET string
string getString = "GET /api/3.0/threads/listPosts.json?forum=abcd&thread:ident=12345&api_key=abcdef0123456789 HTTP/1.0\r\n";
// Headers
getString += "Host: disqus.com\r\n";
getString += "Connection: keep-alive\r\n";
// This carriage return is important to separate the content
getString += "\r\n";
// If this was a POST, content goes here
AsyncSocketClient client = new AsyncSocketClient("disqus.com", 80);
client.ResponseReceived += new ResponseReceivedEventHandler(GetCommentsRequestComplete);
client.SendData(getString);
}
private void GetCommentsRequestComplete(object sender, ResponseReceivedEventArgs e)
{
// Client
var client = sender as AsyncSocketClient;
client.ResponseReceived -= this.GetCommentsRequestComplete;
client = null;
if (!e.isError)
{
// Strip off http preamble
int start = e.response.IndexOf('{');
string data = e.response.Substring(start);
}
}
I started with a full set of headers from the browser analysis, then removed the ones that aren't needed one by one to keep the code more manageable. The "Connection" header is really important, if you use value as "close" the socket will shut down before all data is received, so "keep-alive" must be used.
Conclusion
I've actually used this solution twice already now and think it will come in useful again and again. I hope other people having this same problem find this as it works nicely!
Saturday, 24 December 2011
Obelisk - WP7 MVVM Tombstone Library
Just published first release of "Obelisk" a WP7 library designed to take the pain out of MVVM tombstone persistence.
This library offers an easy way to implement tombstone persistence in WP7 applications. Once the TombstoneHelper is attched to the application, View Models can be registered and all properties with [TombstonePersist] attribute set will be automatically persisted.
Monday, 19 December 2011
WP7 Microsoft.Phone.BackgroundAudio.AudioTrack.Tag String Length Limit
This is a bit of an odd one, I found out that the AudioTrack.Tag string property has a limit of 2047, any more than this and an exception is thrown when the AudioTrack object is assigned to BackgroundAudioPlayer.Instance.Track:
HRESULT = 0x8007007A
Friday, 16 December 2011
WP7 MVVM Panorama SelectedIndex Binding
The Panorama control doesn't support SelectedItem or SelectedIndex binding which is important for maintaining state on re-activation after tombstoning and may be required for navigation. To solve this problem, I've created a Panorama Behavior called TrackablePonaramaBehavior which can be attached to a panorama and offers a dependency property for binding to the selected index. The System.Windows.Interactivity library needs referencing to use it:
using System;
using Microsoft.Phone.Controls;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace Demo.Behaviors
{
public class TrackablePanoramaBehavior : Behavior<Panorama>
{
private Panorama _panarama = null;
private bool _updatedFromUI = false;
// DP for binding index
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(TrackablePanoramaBehavior),
new PropertyMetadata(0, new PropertyChangedCallback(SelectedIndexPropertyChanged)));
// Index changed by view model
private static void SelectedIndexPropertyChanged(DependencyObject dpObj, DependencyPropertyChangedEventArgs change)
{
if(change.NewValue.GetType() != typeof(int) || dpObj.GetType() != typeof(TrackablePanoramaBehavior))
return;
TrackablePanoramaBehavior track = (TrackablePanoramaBehavior)dpObj;
// If this flag is not checked, the panorama smooth transition is overridden
if (!track._updatedFromUI)
{
Panorama pan = track._panarama;
int index = (int)change.NewValue;
if (pan.Items.Count > index)
{
pan.DefaultItem = pan.Items[(int)change.NewValue];
}
}
track._updatedFromUI = false;
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this._panarama = base.AssociatedObject as Panorama;
this._panarama.SelectionChanged += _panarama_SelectionChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
if(this._panarama != null)
this._panarama.SelectionChanged += _panarama_SelectionChanged;
}
// Index changed by UI
private void _panarama_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_updatedFromUI = true;
SelectedIndex = _panarama.SelectedIndex;
}
}
}
To implement this in the View, the following XAML is used (I chopped most xmlns out to make it more concise):
<phone:PhoneApplicationPage
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<!--LayoutRoot contains the root grid where all other page content is placed-->
<Grid x:Name="LayoutRoot">
<controls:Panorama>
<i:Interaction.Behaviors>
<track:TrackablePanoramaBehavior SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}" />
</i:Interaction.Behaviors>
And simply bind to the view model:
public int SelectedIndex
{
get { return this._selectedIndex; }
set
{
if (this._selectedIndex != value)
{
this._selectedIndex = value;
base.RaisePropertyChanged("SelectedIndex");
}
}
}
using System;
using Microsoft.Phone.Controls;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace Demo.Behaviors
{
public class TrackablePanoramaBehavior : Behavior<Panorama>
{
private Panorama _panarama = null;
private bool _updatedFromUI = false;
// DP for binding index
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(TrackablePanoramaBehavior),
new PropertyMetadata(0, new PropertyChangedCallback(SelectedIndexPropertyChanged)));
// Index changed by view model
private static void SelectedIndexPropertyChanged(DependencyObject dpObj, DependencyPropertyChangedEventArgs change)
{
if(change.NewValue.GetType() != typeof(int) || dpObj.GetType() != typeof(TrackablePanoramaBehavior))
return;
TrackablePanoramaBehavior track = (TrackablePanoramaBehavior)dpObj;
// If this flag is not checked, the panorama smooth transition is overridden
if (!track._updatedFromUI)
{
Panorama pan = track._panarama;
int index = (int)change.NewValue;
if (pan.Items.Count > index)
{
pan.DefaultItem = pan.Items[(int)change.NewValue];
}
}
track._updatedFromUI = false;
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this._panarama = base.AssociatedObject as Panorama;
this._panarama.SelectionChanged += _panarama_SelectionChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
if(this._panarama != null)
this._panarama.SelectionChanged += _panarama_SelectionChanged;
}
// Index changed by UI
private void _panarama_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_updatedFromUI = true;
SelectedIndex = _panarama.SelectedIndex;
}
}
}
To implement this in the View, the following XAML is used (I chopped most xmlns out to make it more concise):
<phone:PhoneApplicationPage
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<!--LayoutRoot contains the root grid where all other page content is placed-->
<Grid x:Name="LayoutRoot">
<controls:Panorama>
<i:Interaction.Behaviors>
<track:TrackablePanoramaBehavior SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}" />
</i:Interaction.Behaviors>
And simply bind to the view model:
public int SelectedIndex
{
get { return this._selectedIndex; }
set
{
if (this._selectedIndex != value)
{
this._selectedIndex = value;
base.RaisePropertyChanged("SelectedIndex");
}
}
}
Labels:
MVVM,
Silverlight,
WP7
Subscribe to:
Posts (Atom)