Search
Recent Tweets

Entries in WP7 (11)

Wednesday
Jun292011

Upgrading to Windows Phone Mango Beta 2 - Is that All?

<Rant>
The development experience may be pleasant for Windows Phone but the setup and upgrade process could sure use some smoothing out. On iOS I hate having to download and install a new version of XCode for each new SDK release (even minor updates) but at least it is pretty straightforward. I am sure that I am leaving some things out below with the 6+ hour process of upgrading to Mango beta 2 tonight.
  1. Downloaded beta 2 developer tools and started install.
  2. Beta 2 Developer tools won't install with Beta 1 installed. Uninstalling beta 1 for me was too much trouble I guess.
  3. Accidentally uninstalled non-beta (non Mango) SDK. Granted this was my fault but when you have several phone installs in Programs and Features it is easy to do and beta 2 install could have saved me that mistake. Had to reinstall 3 non-Mango installs (dev tools, jan update, phone developer tools fix). Two hours later...
  4. Accepted Connect invite email, completed survey, completed registration and email confirmation.
  5. Installed mango beta 2 dev tools.
  6. Spent a few minutes figuring out where to download the retail updates on Connect.
  7. Downloaded the 3 installers and setup instructions in the phone update and unblocked files.
  8. Made more free space for the phone backup. Uninstalled several apps, deleted and moved files, ran disk cleanup...
  9. Uninstalled Zune.
  10. Installed new Mango compatible Zune.
  11. Connected phone, closed Zune.
  12. Ran UpdateWP.
  13. UpdateWP failed (UpdateWP has stopped working). See http://forums.create.msdn.com/forums/p/85763/516753.aspx#516753. Deleted UpdateWP in Zune folder and reran UpdateWP.
  14. Reran DevRetailUpdate.
  15. Unlocked screen and reran dev retail update. Took a long break while waiting on backup.
  16. Copied 8GB backup to a dual layer DVD. Near the end, sorry, not quite enough room. Switched to 7-zip of directory. Split zip and copied off of hard disk.
  17. Updated windows phone to 7.0.7401.0 "pre-Mango?" with Zune. Why the need for 2 phone updates here?
  18. Updated windows phone to the actual Mango release with Zune (7661).

Now I can look forward to undoing all this in a few months since there is no upgrade path from beta to RTM. Okay, sorry, I'm done venting. At least now I have Mango installed but I'm too tired to play with it much. I am noticing some odd behavior already but am excited to see the vast improvements coming to life!
</Rant>
Tuesday
May102011

CodeStock 2011 App for Windows Phone 7

The most excellent CodeStock conference is just around the corner on June 3-4th and I'm really looking forward to it. Michael Neel and company have done a great job with this Knoxville-based developer / IT / entrepreneurs conference and it has come a long way in a short time since its inception in 2008. It just keeps getting better every year.
This year on the spur of the moment I decided to write a WP7 app to manage the schedule on my shiny new Samsung Focus. Developing for Windows phone is such a pleasant experience and the nice CodeStock API made it even easier. This was my first mobile app on any platform that I have submitted to a marketplace; I had a lot of fun coding it and certainly learned a good deal.

If you are going to CodeStock and you have a WP7 device you should check it out (both of you ). Even if not, feel free to play with the source, fork it, report issues etc. I'd love to hear feedback either way, good or bad. I'm always looking for better ways to do things and I know there is a lot of room for improvement.

Overview Video

The below video gives an overview of most of the features minus twitter sharing and a few miscellaneous ones. Note that the video codec used in recording washed the colors out and otherwise degraded the images and fonts a bit; it does look a lot better live but this gets the idea across. There are also some screenshots in my CodeStock Flickr album.


This page can also be accessed via http://www.geoffhudik.com/codestock-wp7.
Saturday
Feb122011

WP7 In-app Searching, Filtering Part II

In my first post on WP7 in-app searching / filtering I presented a quick, first pass solution to this task. Justin Angel left some good suggestions in his comments and I wanted to enhance this feature a bit now that I've finally had a little bit of time to return to Windows Phone development. This second pass includes enhancements such as:
  • Replacing ugly search form buttons with a contextual application bar
  • Animating search textbox visibility (fade-in / out)
  • Small moves towards "framework-ifying" search in separate class library
  • Customization of search application bar along with hide keyboard button
  • Smoother search switching from keypress to timer search trigger
  • Demo solution for download and hopefully reader contributions

The video to the right shows the search operation in action. The search works fine in landscape orientation as well as on my Samsung Focus phone, the video recording is just easier with the emulator.

I am only going to touch on changes made to the 1st pass of this functionality, so refer to the original post for any other background information.

Contextual App Bar

Removing the buttons beside the search textbox freed up space but more importantly it provides a much more polished look and is more consisent with Windows Phone usage patterns.

In the search control there is now support for specifying what application bar icons / features to use when the search control is active (focused). I originally did this because I had issues getting app bar icons working from within a class library and this allowed it to work with any images. In addition to the built-in commands of deleting, hiding the keyboard and closing search, a custom option allows executing an arbitrary command (not shown here).

<fwCtl:SearchControl x:Name="uxSearchControl" SearchCommand="{Binding SearchCommand}" Margin="0,0,0,0" Visibility="Collapsed">
	<fwCtl:SearchControl.AppBarItems>
		<fwCtl:SearchAppBarItem IconUri="/Images/appbar.delete.rest.png" Action="Delete" />
		<fwCtl:SearchAppBarItem IconUri="/Images/MB_0036_keyboard.png" Action="Keyboard" />
		<fwCtl:SearchAppBarItem IconUri="/Images/appbar.close.rest.png" Action="Close" />
	</fwCtl:SearchControl.AppBarItems>
</fwCtl:SearchControl>
When the search control loads up, it sets up the actions and resolves the page the search control is on. This is used later to backup the original application bar that is temporarily replaced when the search control is active.
private void Init()
	{
		this.Loaded += SearchControl_Loaded;
		this.Searcher = new Searcher();
		_actions = new Dictionary<SearchActions, Action<SearchAppBarItem>>
					   {
						   {SearchActions.Delete, (i) => Clear()},
						   {SearchActions.Close, (i) => Close()},
						   {SearchActions.Keyboard, (i) => ToggleKeyboard()},
						   {SearchActions.Custom, (i) =>
								{
									if (null == i.Command || !i.Command.CanExecute(null)) return;
									i.Command.Execute(null);
								}}
					   };

		uxSearchTextBlock.GotFocus += (s, e) => {SetContextAppBar(); HasSearchFocus = true; };
		uxSearchTextBlock.LostFocus += (s, e) => { RestoreAppBar(); HasSearchFocus = false; };
	}

	private PhoneApplicationPage Page { get; set; }

	private void SearchControl_Loaded(object sender, RoutedEventArgs e)
	{
		if (DesignerProperties.IsInDesignTool) return;
		LayoutRoot.Background = new SolidColorBrush(Colors.Transparent);
		
		this.Page = this.FindParent<PhoneApplicationPage>();

		this.Opacity = 0;
		this.Visibility = Visibility.Collapsed;
	}

	private Dictionary<SearchActions, Action<SearchAppBarItem>> _actions;
The context app bar is setup once and the page application bar simply gets swapped out as focus enters and leaves the search textbox.
private void SetContextAppBar()
{
	if (null != this.ContextAppBar)
	{
		this.Page.ApplicationBar = this.ContextAppBar;
		return;
	}
	
	this.OriginalAppBar = this.Page.ApplicationBar;

	// might want a setting for menu support, auto-create menu items for buttons
	var bar = new ApplicationBar {IsVisible = true, IsMenuEnabled = false, Opacity = 1};

	this.AppBarItems.ToList().ForEach(i =>
	{
		var btnText = (i.Action != SearchActions.Custom ? i.Action.ToString() : i.Text);
		var btn = new ApplicationBarIconButton { IconUri = i.IconUri, Text = btnText, IsEnabled = true };
		btn.Click += (s, e) => _actions[i.Action](i);
		bar.Buttons.Add(btn);
	});

	this.ContextAppBar = bar;
	this.Page.ApplicationBar = this.ContextAppBar;
}

private IApplicationBar OriginalAppBar { get; set; }
private IApplicationBar ContextAppBar { get; set; }

I am not sure what the best way is to go about swapping out application bars. Performance might be slightly better just using one application bar and removing and adding buttons. I did not notice a real performance problem though I did notice a slightly longer transition with the initial switch then on subsequent show/hide search operations.

Animating visibility transition

I started off using VisualStateManager in the search control's XAML but in the end it was not buying me much. I also switched from a sliding to a fade in/out animation and I found the later to be rather common so I went with a UIElement extension as opposed to repeating in XAML.
public static void FadeInAndShow(this UIElement target, double seconds, Action afterComplete)
{
	target.Opacity = 0;
	target.Visibility = Visibility.Visible;
	var da = new DoubleAnimation
	{
		From = 0.0,
		To = 1.0,
		Duration = TimeSpan.FromSeconds(seconds),
		AutoReverse = false
	};

	Storyboard.SetTargetProperty(da, new PropertyPath("Opacity"));
	Storyboard.SetTarget(da, target);

	var sb = new Storyboard();
	sb.Children.Add(da);

	if (null != afterComplete)
	{
		EventHandler eh = null;
		eh = (s, args) =>
				 {
					 //sb.Stop();
					 sb.Completed -= eh;
					 afterComplete();
				 };

		sb.Completed += eh;
	}

	sb.Begin();
}
On the fade-in an action is specified to automatically set focus into the search textbox to start typing once the animation has completed.
public void ShowHide()
{
	if (this.Visibility == Visibility.Collapsed)
		Show();
	else
		Close();
}

private void Show()
{
	this.FadeInAndShow(.8, () => uxSearchTextBlock.Focus());
}

private void Close()
{
	RestoreOriginalUI();
}

private void RestoreOriginalUI()
{
	button.Focus(); // how else to hide keyboard?
	RestoreAppBar();
	Clear();
	this.FadeOutAndCollapse(.5);
}

private void RestoreAppBar()
{
	this.Page.ApplicationBar = this.OriginalAppBar;
}

private void Clear()
{
	this.Searcher.Clear();
}

Timer-based Search

Searching immediately upon a keypress / property changed event was a bit overkill and introduced some lag on larger lists. It now simply toggles an invalidated property and a timer periodically inspects that and searches when needed. The interval might be a good target for a configurable property.
public class Searcher : NotifyPropertyChangedBase
{
	private DispatcherTimer _timer;

	private void EnsureTimerIsRunning()
	{
		if (null == _timer)
		{
			_timer = new DispatcherTimer() {Interval = TimeSpan.FromSeconds(.7)};
			_timer.Tick += (s, e) =>
							   {
								   if (Invalidated) ExecuteSearch();
							   };
		}

		if (!_timer.IsEnabled)
			_timer.Start();
	}

	private void ExecuteSearch()
	{
		if (null != this.SearchCommand && this.SearchCommand.CanExecute(null))
		{
			this.SearchCommand.Execute(this.SearchText);
			Invalidated = false;
		}
	}

	private string _searchText;
	public string SearchText
	{
		get { return _searchText; }
		set
		{
			if (_searchText != value)
			{
				EnsureTimerIsRunning();
				_searchText = value;
				Invalidated = true;
				OnPropertyChanged("SearchText");
			}
		}
	}

	private ICommand _searchCommand;
	public ICommand SearchCommand
	{
		get { return _searchCommand; }
		set
		{
			if (_searchCommand != value)
			{
				_searchCommand = value;
				OnPropertyChanged("SearchCommand");
			}
		}
	}

	private bool _invalidated;
	public bool Invalidated
	{
		get { return _invalidated; }
		set
		{
			if (_invalidated != value)
			{
				_invalidated = value;
				OnPropertyChanged("Invalidated");
			}
		}
	}

	// or Reset()
	public void Clear()
	{
		this.SearchText = string.Empty;

		// i.e. value cleared, full list
		if (this.Invalidated)
			ExecuteSearch();

		this.Invalidated = false;

		if (null != _timer && _timer.IsEnabled)
			_timer.Stop();
	}        
}

Demo Solution and Final Thoughts

Feel free to download and improve upon the demo solution. It's from a stripped down, earlier version of my app and there is plenty of room for improvement. I have only begun to cleanup the 1st pass and I haven't exercised the searching / filtering functionality outside of the single use case in my current app. The search feature is the only functionality hooked up in the demo.

Some things to consider for enhancing this:
  • Styling enhancements such as moving the search icon "inside the textbox"; this originally presented some oddities so I pulled it.
  • Additional dependency properties to allow changing more behavior characteristics
  • Watermark textbox; ignored here since I autofocus to search textbox
  • More generalization / "frameworkification" of searching / filtering
  • Looking at AutoCompleteBox perhaps; my current usage is more for filtering down a list then just for picking an item
  • Performance improvements and general code cleanup