Twitter integration in Windows 8
Integrating Twitter in your own Windows 8 store app requires a lot of knowledge of their API, this because of the strict requirement of authentication through OAuth ! We needed this twitter integration, because we want to show the twitter avatars of several users in our app!
If you are up to it, you can implement your own REST code… But if you are a bit like me and would think others are far more educated to get it right, you can go for a library that does all the hard work for you!
I choose to use Linq2Twitter ( made by @JoeMayo ) for our latest Windows 8 store application, because for me, the Linq format just seems the best way to deal with the API.
Now let me show you how you can add that twitter integration in your Windows 8 app!
First things first, when you want to do any twitter API calls from within your own app, you’ll need to register the app through their Developer site, be sure to also fill in a callback URL ! When this is done and your app was verified, you’ll get 2 keys that are needed in your .net code:
- Consumer key
- Consumer secret
When you have that in place, you can add the code needed to get your user to allow your app to talk with his twitter account. Within a Windows 8 store app, this is done with the WinRtAuthorizer from Linq2Twitter. Just take care of saving the passed through user credentials, so you can reuse them the next time your app starts!
public TwitterContext TwitterContext { get; private set; }
public async void InitializeTwitter()
{
WinRtAuthorizer authorizer = null;
if (ApplicationData.Current.LocalSettings.Values.ContainsKey("Credentials"))
{
string credentialsJson = ApplicationData.Current.LocalSettings.Values["Credentials"] as string;
LocalDataCredentials credentials = JsonConvert.DeserializeObject<LocalDataCredentials>(credentialsJson);
authorizer = new WinRtAuthorizer
{
Credentials = credentials,
UseCompression = true,
Callback = new Uri("YOUR APP CALLBACK")
};
}
else
authorizer = new WinRtAuthorizer
{
Credentials = new LocalDataCredentials
{
ConsumerKey = "YOUR APP CONSUMER KEY",
ConsumerSecret = "YOUR APP CONSUMER SECRET"
},
UseCompression = true,
Callback = new Uri("YOUR APP CALLBACK")
};
if (!authorizer.IsAuthorized)
{
await authorizer.AuthorizeAsync();
ApplicationData.Current.LocalSettings.Values["Credentials"] = JsonConvert.SerializeObject(authorizer.Credentials);
}
this.TwitterContext = new TwitterContext(authorizer);
}
When this is done, you normally should have an authorized twitter context that you can use inside your app. What I needed in our app was retrieving several twitter user handle images and also needed to be able to tweet a certain text.
For searching a lot of users at the same time you can use the following code:
var participantNames = (from participant in twunch.Participants
select participant.Name).ToList();
string nameQuery = string.Join(",", participantNames);
(from user in this.TwitterContext.User
where user.Type == UserType.Lookup && user.ScreenName == nameQuery
select user).MaterializedAsyncCallback(response =>
{
if (response.Status == TwitterErrorStatus.Success)
{
var twitterHandles = response.State;
// Perform needed operations on the twitter user info!
}
});
What we do here is perform a twitter lookup with a string concatenated list of twitter screen names. If the search went ok, we get back an Enumerable list of twitter Users that contain a lof of info about the user! In our case we needed the user.ProfileImageUrl…
So that’s it, nothing more to it, be sure to check out the codeplex site of Linq2Twitter for much more details on what you can do with this library.
EventBrite and AgFx – Windows Phone development
So you will already know that anything linked to online data should be retrieved using AgFx! Now if you have that, let me show you how you can use this for retrieving event data from EventBrite ( an online service to host an event calendar and maintain attendees data ).
EventBrite is a cool online service that also comes packed with a great API that will allow devs to integrate it in their own apps.
Before I continue I must point out that there is also a clean .net wrapper available, called EventBrite.Net but like I stated above, I’m more hooked on AgFx so I didn’t used the wrapper.
Before you can do anything with the EventBrite API you first should get an API key through their website here… After that you are set to go! With that API key you can than start using all API methods that are described on the EventBrite API documentation here…
For this demo we will try to download a list of events for a given organizer, the API method to get this info is organizer_list_events. But before we can do anything in our program we need to have .net objects representing the EventBrite response. Getting this information is not that difficult… First find an organizer and get it’s ID. You’ll only need to get the list of all events given by the organizer and EventBrite will show you his ID in the URL: I used this one http://www.eventbrite.com/org/2276332563 so the ID here is 2276332563
With this, you can use Fiddler to take a look at the returning JSON result. Open the composer tab in Fiddler and paste a legal EventBrite API method URL in the GET input box: https://www.eventbrite.com/json/organizer_list_events?app_key=[YOUR KEY HERE]&id=2276332563
When everything is ok, you’ll get a big JSON result back that contains all event information. This result will help you getting the needed .net objects! In Fiddler open the Raw tab in the Response pane and only copy the JSON string… after you have copied that, use a word processing tool to strip it of all unneeded line returns ( ex: Notepad++ with TextFX plugin – Unwrap text ). With your currently stripped JSON response go to the fantastic site called json2csharp! Paste your JSON code and press the Generate button, when all goes well you’ll get following result:
public class Organizer
{
public string url { get; set; }
public string description { get; set; }
public string long_description { get; set; }
public object id { get; set; }
public string name { get; set; }
}
public class Ticket2
{
public string description { get; set; }
public string end_date { get; set; }
public int min { get; set; }
public int max { get; set; }
public string price { get; set; }
public string visible { get; set; }
public string currency { get; set; }
public int type { get; set; }
public int id { get; set; }
public string name { get; set; }
}
public class Ticket
{
public Ticket2 ticket { get; set; }
}
public class Venue
{
public string city { get; set; }
public string name { get; set; }
public string country { get; set; }
public string region { get; set; }
public double longitude { get; set; }
public string postal_code { get; set; }
public string address_2 { get; set; }
public string address { get; set; }
public double latitude { get; set; }
public string country_code { get; set; }
public int id { get; set; }
public string __invalid_name__Lat-Long { get; set; }
}
public class Event2
{
public string box_header_text_color { get; set; }
public string link_color { get; set; }
public string box_background_color { get; set; }
public string timezone { get; set; }
public string box_border_color { get; set; }
public string logo { get; set; }
public Organizer organizer { get; set; }
public string background_color { get; set; }
public object id { get; set; }
public string category { get; set; }
public string box_header_background_color { get; set; }
public int capacity { get; set; }
public int num_attendee_rows { get; set; }
public string title { get; set; }
public string start_date { get; set; }
public string status { get; set; }
public string description { get; set; }
public string end_date { get; set; }
public string tags { get; set; }
public string timezone_offset { get; set; }
public string text_color { get; set; }
public string title_text_color { get; set; }
public List<Ticket> tickets { get; set; }
public string created { get; set; }
public string url { get; set; }
public string box_text_color { get; set; }
public string privacy { get; set; }
public string modified { get; set; }
public string logo_ssl { get; set; }
public string repeats { get; set; }
public Venue venue { get; set; }
public string repeat_schedule { get; set; }
}
public class Event
{
public Event2 @event { get; set; }
}
public class RootObject
{
public List<Event> events { get; set; }
}
Or you can just copy it from my blog post ![]()
DO NOTE that not it seems that not all JSON names are .net compatible resulting in _invalid_name_ prefixes! My suggestion is to change the .net class and ‘manipulate’ the JSON mapping in your app! ( I’m NOT covering this in the rest of the example )
So you added these classes in some Model part of your project, next up hooking up AgFx to EventBrite. To get this working you’ll need to create an AgFx ‘viewmodel’, here is how I did it:
/// <summary>
/// We cache the Event data for a half day - 12hours ( 43200 seconds )
/// </summary>
[CachePolicy(CachePolicy.CacheThenRefresh, 43200)]
public class agfxEventBriteEventsViewModel : ModelItemBase<EventBriteEventsLoadContext>
{
public const string EventBriteEventsPropertyName = "EventBriteEvents";
private ObservableCollection<EventBriteEvent> _events = new ObservableCollection<EventBriteEvent>();
public ObservableCollection<EventBriteEvent> Events
{
get
{
return _events;
}
set
{
if (!ReferenceEquals(_events, null))
{
_events.Clear();
if (_events != value)
{
foreach (var eventItem in value)
{
_events.Add(eventItem);
}
}
}
RaisePropertyChanged(EventBriteEventsPropertyName);
}
}
public agfxEventBriteEventsViewModel()
{
}
public agfxEventBriteEventsViewModel(EventBriteEventsKey eventId)
: base(new EventBriteEventsLoadContext(EventBriteEventsKey.All))
{
}
public class agfxEventBriteEventsViewModelLoader : IDataLoader<EventBriteEventsLoadContext>
{
public object Deserialize(EventBriteEventsLoadContext loadContext, Type objectType, Stream stream)
{
agfxEventBriteEventsViewModel viewModel = new agfxEventBriteEventsViewModel(loadContext.Id);
var sr = new StreamReader(stream);
var eventCollectionJSON = sr.ReadToEnd();
try
{
var rootObject = JsonConvert.DeserializeObject<RootObject>(eventCollectionJSON);
//Filter out events that have the EventBrite status field set to 'Completed'
rootObject.events.Where((x) => !x.@event.status.ToLower().Equals("completed")).ToList().ForEach((x) => viewModel.Events.Add(x.@event));
}
catch (Exception e)
{
}
return viewModel;
}
public LoadRequest GetLoadRequest(EventBriteEventsLoadContext loadContext, Type objectType)
{
return new WebLoadRequest(
loadContext,
new Uri(EventBriteAPI.GetInstance().GetComposedAPIMethod(APIMethod.OrganizerListEvents)));
}
}
}
A lot of code, but if you are used to working with AgFx, most of it will look familiar… The only ‘special’ thing is being done inside the GetLoadRequest method, here we need to give AgFx our API method URL. So I just wrapped all possible methods inside a static class EventBriteAPI. But in the end all it does is generate the same URL we used in Fiddler being: https://www.eventbrite.com/json/organizer_list_events?app_key=[YOUR KEY HERE]&id=2276332563
So that’s it, now you will have all events retrieved from EventBrite through AgFx! Happy coding!
#ComDayBe – Flyout control in XAML through Callisto for Windows 8
@BartLannoeye ( his blog ) and I did a session on Building the Windows 8 Community app, last week at Community Day 2012, this as members of the WiPhug user group.
Here we are with part 3 and this time we will be talking about using the appbar and implementing a Flyout Control. More detail on what it is and when to use it, is given here…
But why would we need to put a blog post up about it, well because if you go through all the examples on the MSDN site, you’ll notice that there is no XAML version available!! Yep with Windows 8 RP there is still no XAML flyout control out of the box!
But there is a good alternative for this, that is by using the Callisto lib created by Tim Heuer! If you want you can simply add it through the Callisto NuGet. This lib contains several neat features, but for me the FlyOut control for XAML is by far the most valueable.
So once refernced, how do you add a flyout control… this is done through code behind on your view page. In our case we want to trigger the visibility of the flyout when the user presses an appbar button.
So we add an appbar, ad a button and hook up the Click event to a method on our code behind. This looks like this in XAML:
<Page.BottomAppBar>
<AppBar x:Name="BottomAppBar" Padding="10,0,10,0" Visibility="Visible" IsSticky="True" IsOpen="True">
<Grid>
<StackPanel x:Name="RightPanel" Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Right">
<Button Style="{StaticResource FilterAppBarButtonStyle}" Tag="Filter" Click="ShowFlyoutMenu" />
</StackPanel>
</Grid>
</AppBar>
</Page.BottomAppBar>
On thing to notice here, is the fact that I’m using a static resource for the button! It’s called FilterAppBarButonStyle, I’ve added this myself in the Common\StandardStyles.xaml file. It’s a Filter Icon for the appbar, looking like this:
This icon is actually a font instead of an image, and you can get it for free, because the font itself is already installed on your machine!! The style definition is done in XAML and the code is:
<Style x:Key="FilterAppBarButtonStyle" TargetType="Button" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="FilterAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Filter"/>
<Setter Property="Content" Value=""/>
</Style>
You’ll notice that we set a Content property to some special value, well this value represents one char from the font Segoe UI… and you can get that value by opening the Character map in Windows 8. More on this technique can be found explained here… by Jerry Nixon
Now that our button is in place we only need to create the flyout control in the code behind method, this is don like this:
private void ShowFlyoutMenu(object sender, RoutedEventArgs e)
{
Flyout flyOut = new Flyout();
flyOut.PlacementTarget = sender as UIElement;
flyOut.Placement = PlacementMode.Top;
Menu menu = new Menu();
// Do set a width, because there is currently a small issue with Flyout control if you only use ToggleMenuItems
// https://github.com/timheuer/callisto/issues/24
menu.MinWidth = 110;
ToggleMenuItem tmiAll = new ToggleMenuItem();
tmiAll.Text = "All";
tmiAll.Tapped += (a, b) =>
{
_toggleMenuChecked["All"] = !_toggleMenuChecked["All"];
foreach (string key in _toggleMenuChecked.Keys.ToList())
{
if (!key.Equals("All"))
_toggleMenuChecked[key] = _toggleMenuChecked["All"];
}
};
ToggleMenuItem tmiFirst = new ToggleMenuItem();
tmiFirst.Text = "First";
ToggleMenuItem tmiSecond = new ToggleMenuItem();
tmiSecond.Text = "Second";
ToggleMenuItem tmiThird = new ToggleMenuItem();
tmiThird.Text = "Third";
menu.Items.Add(tmiAll);
menu.Items.Add(new MenuItemSeparator());
menu.Items.Add(tmiFirst);
menu.Items.Add(tmiSecond);
menu.Items.Add(tmiThird);
menu.Items.Select(item => {
if (item is ToggleMenuItem)
{
item.Tapped += (a, b) => _toggleMenuChecked[((ToggleMenuItem)item).Text] = !_toggleMenuChecked[((ToggleMenuItem)item).Text];
((ToggleMenuItem)item).IsChecked = _toggleMenuChecked[((ToggleMenuItem)item).Text];
}
return item;
}).ToList();
flyOut.Content = menu;
flyOut.IsOpen = true;
UpdateLayout();
}
The flyout control can contain any ui control, in our case we add a Menu control with ToggleMenuItems. If you look again, you’ll also see that at the beginning of the ShowFlyoutMenu method, we set the Placement of the Flyout above the sender, in this case the appbar button!
Well again nothing that difficult, but it gives your app a fancy UI element!
The demo can be found underneath…
#ComDayBe – Share contract through MVVM in Windows 8
@BartLannoeye ( his blog ) and I did a session on Building the Windows 8 Community app, last week at Community Day 2012, this as members of the WiPhug user group.
So this is the second post in reference to our session, this time on how you can use a share contract through MVVM.
So one of the new neat features of Windows 8 is the Share Contract, it allows you to ‘share data’ from within your own app with other apps. You can read in more detail here what it is all about and sure check this list of possible data types you can use to share data here…
In our app we currently only share simple text/html basically a title and a description that will contain an URL to an external webpage with more info. So in the Community Calendar app this will be enabled on the Event detail page, so that you can share the selected event through email for example. And on the User Group detail page, so you can share the selected user group.
Adding a share contract isn’t that difficult, but we faced one problem and that is that our data to shre resides inside our MVVM ViewModel and not on our View. So how do you go about this…
First thing you’ll need to do is create a ShareData class. This little class will represent our data that we will share to other apps. In our case a Title, Description and some Text. Depending on the target app, some of these data properties will be used to acquire data.
public class ShareData
{
public string Title { get; set; }
public string Description { get; set; }
public string Text { get; set; }
}
Secondly you need to create a ShareManager class, put it somewhere inside a folder in your project. It is a singleton class, because we will need a same instance to be accessible on different places.
public class ShareManager
{
public ShareData ShareData { get; set; }
#region Constructor
private static ShareManager _instance;
private static object _instanceSync = new Object();
protected ShareManager()
{
Initialize();
}
public static ShareManager GetInstance()
{
// This implementation of the singleton design pattern prevents unnecessary locks (using the double if-test)
if (_instance == null)
{
lock (_instanceSync)
{
if (_instance == null)
{
_instance = new ShareManager();
}
}
}
return _instance;
}
#endregion
#region Private methods
private void Initialize()
{
DataTransferManager dataTransferManager;
dataTransferManager = DataTransferManager.GetForCurrentView();
dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.DataRequested);
}
private void DataRequested(DataTransferManager sender, DataRequestedEventArgs e)
{
if (!ReferenceEquals(this.ShareData, null))
{
e.Request.Data.Properties.Title = ShareData.Title ?? string.Empty;
// Setting the Description to null of empty string will throw exception
if(string.IsNullOrEmpty(ShareData.Description))
e.Request.Data.Properties.Description = "Description";
else
e.Request.Data.Properties.Description = ShareData.Description;
e.Request.Data.SetText(ShareData.Text ?? string.Empty);
}
}
#endregion
}
The ShareManager holds your ShareData class and we will be filling that up from our ViewModels! In the Initialize method, you’ll need to hook up to the DataRequested event, because that event will be triggered when you click on the Share button on your Charms bar in Windows 8! The only thing to do inside the method you’ve hooked up, is presenting the needed ShareData to the requester.
Now if you want to enable the Sharing, you’ll need to let Windows 8 know which Views have the Share ability. You do this by instantiating the ShareManager in the code behind of the View.
private ShareManager _shareManager = ShareManager.GetInstance();
There is nothing more you need to do in the View itself… the body work is done in the ViewModel. So also in the ViewModel you’ll need to instantiate the ShareManager and when we want to share Event Data we add it when our SelectedEvent data get’s set in the ViewModel by passing the data to the ShareManager.
this._shareManager.ShareData = _selectedEvent.ShareData;
And that’s it, nothing more to it
I added a demo project so you can go through the code yourself!
#ComDayBe – Image manipulation in Windows 8 metro app
@BartLannoeye ( his blog ) and I did a session on Building the Windows 8 Community app, yesterday at Community Day 2012, this as members of the WiPhug user group.
During this session I presented some technical items on how to make your Windows 8 app more attractive… Because of the fact that we currently only shared the slide deck, I’ll be putting up some small blog posts with code references so you can use these inside your own app as well!
First post is about doing Image manipulation in Windows 8 metro.
Our Community app presents a list of events that contain sessions. Such a session can be given by one speaker or more speakers. What we wanted to achieve was to let the user of our app directly see the image(s) of the speaker(s) when he looks at the session list. But the trick here is that because of the use of XAML templates we only have one Image control inside our session list with a given Width and Height. So we need to transform the given images so that they will fit. The effect we are after is this, one speaker = one large image, more speakers = smaller images inside one large one:
Now how do you do this… well it’s very easy and this thanks to the WriteableBitmapEx lib available on codeplex or through NuGet. But let me suggest you get the source code because you’ll need to tweak one small thing to get a good result! ( more on this later ) This library gives you a whole range of different extensions on the WriteableBitmap class that is available in .Net namespace Windows.UI.Xaml.Media.Imaging. With these extensions you can manipulate your images.
So when you added a reference to the WriteableBitmapEx, you’ll only need a WriteableBitmap inside your code and you are set! What we do in our app, if the session is only given by one speaker we will just be downloading the image from the web ourselves and return it to the image control.
Downloading images from the web is done by using the FromStream extension.
var rass1 = RandomAccessStreamReference.CreateFromUri(new System.Uri(speaker, UriKind.Absolute));
IRandomAccessStream stream1 = await rass1.OpenReadAsync();
//We initialize the bitmap with height and width, but the actual size will be reset after the FromStream method!
WriteableBitmap speakerImage = new WriteableBitmap(150, 150);
speakerImage = await speakerImage.FromStream(stream1);
UPDATE: if you get version 1.0.2.0 of the WriteableBitmapEx, you don't need to change the source code, because as of that version the problem has been fixed!
But befor we continue, do note that the current code base of the FromStream method swaps Red en Blue colour channels! The effect you’ll get are speaker images that look a lot like Navi people from Avatar
. So therefore, open up the WinRT project of the WriteableBitmapEx solution and search for the FromStream method, it is inside the WriteableBitmapConvertExtensions.cs file! Just put in comment the for block that is underneath the pixelData.DetachPixelData();
// Swap R and B channels
var pixels = pixelData.DetachPixelData();
//for (var i = 0; i < pixels.Length; i += 4)
//{
// var r = pixels[i];
// var b = pixels[i + 2];
// pixels[i] = b;
// pixels[i + 2] = r;
//}
But what do we do when we have several speakers and only the same amount of image space inside our XAML? Well we just resize the original images and merge them inside one larger one at specific positions… we currently only support up to 4 speakers, so I didn’t use a sophisticated algorithm to do the transformations.
To do this kind of manipulation we use the Blit method. The Blit method is used on the Target image and you’ll have to give it a set of 4 parameters. The first one is a rectangle indicating where in the new Target image you will be placing the Original image. But the neat thing here is, that by setting this first rectangle parameter you can also in one go resize the Original image! The second parameter is the actual Original image itself. And the third parameter is a source rectangle, also here a neat thing, you can if you want only take out a part of the original image! But we use the complete image… The last parameter is a BlendMode and this is set to Additive.
Now how does this look in code:
public static async Task<WriteableBitmap> GetImage(string source)
{
WriteableBitmap image = new WriteableBitmap(250, 250);
try
{
if (source.Contains(";"))
{
var speakers = source.Split(';');
int count = 0;
foreach (string speaker in speakers)
{
var rass1 = RandomAccessStreamReference.CreateFromUri(new System.Uri(speaker, UriKind.Absolute));
IRandomAccessStream stream1 = await rass1.OpenReadAsync();
//We initialize the bitmap with height and width, but the actual size will be reset after the FromStream method!
WriteableBitmap speakerImage = new WriteableBitmap(150, 150);
speakerImage = await speakerImage.FromStream(stream1);
int xPosition = 0;
int yPosition = 0;
if (count == 1 || count == 3)
yPosition = yPosition + 125;
if (count == 2 || count == 3)
xPosition = xPosition + 125;
//We use Blit to do image resizing and image merging
image.Blit(new Rect() { Height = 125, Width = 125, X = xPosition, Y = yPosition }, speakerImage, new Rect() { Height = speakerImage.PixelHeight, Width = speakerImage.PixelWidth, X = 0, Y = 0 }, WriteableBitmapExtensions.BlendMode.Additive);
count++;
}
}
else
{
var rass1 = RandomAccessStreamReference.CreateFromUri(new System.Uri(source, UriKind.Absolute));
IRandomAccessStream stream1 = await rass1.OpenReadAsync();
//We initialize the bitmap with height and width, but the actual size will be reset after the FromStream method!
image = await image.FromStream(stream1);
}
return image;
}
catch (Exception exception)
{
return image;
}
}
So that’s it! Now you have everything in place to get the cool look we showed you in our app
I’ve added a demo solution - Visual Studio 2012 RC - with all the code, so you can try it out.
WriteableBitmapEx Demo solution
Community work
Just a quick note… I’ll be speaking @ Community Day 2012 !
Together with Bart Lannoeye we will present the Windows 8 metro app “Community Calendar”.
We’ll show you how we created the app using XAML with WinRT and touch different technical aspects.
Click the image below for the complete agenda… see you there.
Location: Utopolis Mechelen (Map)
Address: Spuibeekstraat 5 2800 Mechelen
Event date: Thursday June 21th
Semantic Zoom with MVVMLight – Windows 8 Metro
This week I've been trying out some Windows 8 Metro development and by doing so I came across something I think will help out other devs.
I wanted to try out one of the key new features that is presented in Windows 8, called Semantic Zoom ( read about/view it here ).
But because of the fact I’m mostly familiar with Windows Phone 7 development, I wanted to use Laurent Bugnion’s MVVMLight framework also on Windows 8. You can get a v4Beta1 version here…
So by doing so, I needed to tweak my code so it would fit nicely with the Semantic Zoom pattern. Let me show you how I did this.
First create a new MVVMLight Project
Than open the Model folder and add a class called Car.cs
public class Car
{
public string Model { get; set; }
public Manufacturer Group { get; set; }
}
And a class called Manufacturer.cs
public class Manufacturer
{
public string Name { get; set; }
private ObservableCollection<Car> _items = new ObservableCollection<Car>();
public ObservableCollection<Car> Items
{
get { return this._items; }
}
}
These 2 classes will define your Group and Items needed for the Semantic Zoom. So for the Zoomed In view we will be presenting all Cars that we have ( grouped by manufacturer ) and in the Zoomed Out view we will show a birds eye overview of all Manufacturers with some general details about the amount of Cars available.
Next thing on our list is adding an ObservableCollection of Manufacturers to the MainViewModel.
public const string ManufacturersPropertyName = "Manufacturers";
private ObservableCollection<Manufacturer> _manufacturers = null;
public ObservableCollection<Manufacturer> Manufacturers
{
get
{
return _manufacturers;
}
set
{
if (_manufacturers == value)
{
return;
}
_manufacturers = value;
RaisePropertyChanged(ManufacturersPropertyName);
}
}
Be sure to initiate your data, we will now for this demo do this in the MainViewModel constructor.
Manufacturer man1 = new Manufacturer() { Name = "Volkswagen" };
Manufacturer man2 = new Manufacturer() { Name = "Renault" };
Manufacturer man3 = new Manufacturer() { Name = "Opel" };
man1.Items.Add(new Car() { Model = "Polo" });
man1.Items.Add(new Car() { Model = "Golf" });
man1.Items.Add(new Car() { Model = "Passat" });
man2.Items.Add(new Car() { Model = "Megan" });
man2.Items.Add(new Car() { Model = "Laguna" });
man3.Items.Add(new Car() { Model = "Corsa" });
man3.Items.Add(new Car() { Model = "Astra" });
man3.Items.Add(new Car() { Model = "Zafira" });
man3.Items.Add(new Car() { Model = "Insignia" });
this.Manufacturers = new ObservableCollection<Manufacturer>();
this.Manufacturers.Add(man1);
this.Manufacturers.Add(man2);
this.Manufacturers.Add(man3);
Now open up your MainPage.xaml and get rid of the following TextBlock
<TextBlock FontSize="56"
Text="{Binding WelcomeTitle}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
FontFamily="Segoe UI Light"
Margin="120,49,0,0" />
Put following code into place ( where the textblock was previously set ) to get your title and backbutton
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
<TextBlock x:Name="pageTitle" Text="{Binding WelcomeTitle}" Grid.Column="1" Style="{StaticResource PageHeaderTextStyle}"/>
</Grid>
So now we need to add our Semantic Zoom control, do this directly underneath the backbutton and page title xaml
<SemanticZoom Grid.Row="1">
<SemanticZoom.ZoomedInView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
Before we continue we will be adding a CollectionViewSource, this is needed to fill the Item grid later on in the Semantic Zoom control! You place this inside the MainPage.xaml <ResourceDictionary> tag, it should be there already and contain <ResourceDictionary.MergedDictionaries> tag… Take note that it is actually here that we will be binding to our ObservableCollection froml our MainViewModel!
<CollectionViewSource x:Name="groupedItemsViewSource"
IsSourceGrouped="true"
Source="{Binding Manufacturers}"
ItemsPath="Items"
/>
So with this in place we can continue with our ZoomedIn view, all we will be doing is defining visual elements on how we want to structure our data. In our case we will be adding the Manifacturer name as Group header with Car models underneath it, to accomplish this add following xaml inside the <SemanticZoom.ZoomedInView></SemanticZoom.ZoomedInView> tags
<GridView x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Margin="116,0,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
>
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="145" Height="145">
<TextBlock TextWrapping="Wrap" Margin="5" FontSize="32" FontFamily="Segoe UI Light" Text="{Binding Model}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<TextBlock Text="{Binding Name}" Foreground="Gray" Margin="5" FontSize="24" FontFamily="Segoe UI Light" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
Now that the ZoomInView is ready, add following xaml for our ZoomOutView. Now we will again show the Manufacturer name but only with a total Car count underneath it so we can get our birds eye overview! Place the code inside the <SemanticZoom.ZoomedOutView></SemanticZoom.ZoomedOutView> tags
<GridView x:Name="groupGridView"
IsItemClickEnabled="True"
Margin="116,0,40,46"
>
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Group.Name}" FontSize="32" Foreground="Gray" />
<TextBlock Text="{Binding Group.Items.Count}" FontSize="24" Foreground="White" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
Before we are done we do actually need to add one line of extra code in the MainPage.xaml.cs file! To get the ZoomedOutView working we need to link the groupGridView with the groupedItemsViewSource
So inside the constructor of the MainPage.xaml.cs add following line underneath the this.InitializeComponent(); call
this.groupGridView.ItemsSource = groupedItemsViewSource.View.CollectionGroups;
And that’s it… if everything goes well you should have following result
The complete solution can be downloaded here… MvvmLight1
Tweetsharp and AgFx – Windows Phone 7
Being a big fan of AgFx for any online data retrieval, I was exited to see Bjorn Kuiper his blog post about using Tweetsharp together with AgFx in a WP7 app. You can read all about it here… it shows you how to display tweets from a specific users’ timeline.
But like many blog posts, most of the time you’ll have to tweak them to work in your own scenario. So also with this one, I needed to manipulate some code to get my case working. Best to start off, is downloading Bjorn’s example here.
What I wanted to achieve, was to show a list of tweets done by a Search, also through the use of Tweetsharp and AgFx of course. But what I soon found out was that the code of Bjorn uses the class TwitterStatus that contains a tweet, in my case the returning items are of type TwitterSearchStatus. They are actually not that different, but I ran into some serialization/deserializations issues.
So what was my problem… when you get your search results back from Tweetsharp you want to serialize them so that you can use the AgFx feature. In Bjorn’s example, he strips the Entities from the TwitterStatus before serializing. Found in the Execute method of the TwitterLoadRequest class.
// Have to clear this, unable to serialize.
foreach (var status in statuses)
{
status.Entities = null;
}
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(IEnumerable<TwitterStatus>));
ser.WriteObject(ms, statuses);
byte[] data = ms.ToArray();
ms.Close();
But like I stated before I’m not working with TwitterStatus but with TwitterSearchStatus and it seems that performing the same action has no effect and I was still getting problems with the Entities property when Deserializing.
So how did I solve this, well I used Json.net from James Newton-King, also available as a NuGet here. So, instead of using the .Net DataContractJsonSerializer like Bjorn, we use the JsonConvert.SerializeObject. This gives us following code that replaces the one show above:
this.twitterService = new TwitterService();
this.twitterService.Search(
this.twittersearch,
TwitterSearchResultType.Recent,
(statuses, response) =>
{
try
{
if (response.StatusCode == HttpStatusCode.OK)
{
// Remove Entities on status, unable to deserialize later
string output = JsonConvert.SerializeObject(statuses.Statuses, Formatting.None, new JsonSerializerSettings { ContractResolver = new DynamicContractResolver() });
MemoryStream mStream = new MemoryStream(Encoding.UTF8.GetBytes(output));
byte[] data = mStream.ToArray();
mStream.Close();
result(new LoadRequestResult(new MemoryStream(data)));
}
else
{
result(new LoadRequestResult(new MemoryStream()));
}
}
catch(Exception e)
{
//TODO: Perform needed error actions
result(new LoadRequestResult(new MemoryStream()));
}
});
What you will notice is that I’m doing a special kind of Serialization… using a DynamicContractResolver! This is needed to manipulate the Json serialization, because if we don’t do anything, we’ll still have an output Json string containing the Entities properties.
So to fix this, you’ll need to interact when Json.Net tries to convert your class to Json, how to do this is is by creating your own class that inherits from DefaultContractResolver.
public class DynamicContractResolver : DefaultContractResolver
{
public DynamicContractResolver()
{}
/// <summary>
/// Perform our own searialization of all properties
/// We will skip the property Entities, because on deserialization setting a value to it will fail ( it has only a Getter )
/// </summary>
/// <param name="type"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
properties = properties.Where(p => !p.PropertyName.StartsWith("Entities".ToString())).ToList();
return properties;
}
}
As you can see, Json.net finds properties to serialize, so he will pass the CreateProperties method, what we do here is use Linq to filter out the Entities, resulting in a total Json outpot string containing serialized TwitterSearchStatus objects without any Entities property.
The only thing left to do is Deserialize everything back in the Deserialize method of your AgFx DataLoader class.
public object Deserialize(TwitterLoadContext loadContext, Type objectType, System.IO.Stream stream)
{
agfxTwitterFeedViewModel feed = new agfxTwitterFeedViewModel(loadContext.TwitterName);
var sr = new StreamReader(stream);
var tweetCollectionJSON = sr.ReadToEnd();
var tweetCollection = JsonConvert.DeserializeObject<IEnumerable<TwitterSearchStatus>>(tweetCollectionJSON);
foreach (Tweet tweet in tweetCollection.Select(status => new Tweet(status)))
feed.TimeLine.Add(tweet);
return feed;
}
So now you’ll be getting TwitterSearchStatus objects back from AgFx that you can use in your WP7 Views! Happy coding…
Resize images to be used on Live Tile – Windows Phone 7
Recently I added a live tile feature to one of my WP7 applications.
The app tile would show an image of a person, retrieved from a RSS feed.
Now because of the fact that the image is of a person, it's not squared, but portrait. Meaning that the width of the image is shorter then the height. Also the image I need to display is larger than the windows live tile.
So when you just try to display this image without any modifications, you'll get a weird result.
The original image

To fix this, you'll need to resize the image.
First of all, download the image and map it to a BitmapImage object, you can just use an URI that points to the image on the web for this.
Uri imageUri = new Uri(imageUrl, UriKind.Absolute);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.CreateOptions = BitmapCreateOptions.None;
bitmapImage.UriSource = imageUri;
One note, before you can start manipulating the image, you'll need to be sure it has been downloaded completely, so register to the ImageOpened event.
bitmapImage.ImageOpened += (sender, e) =>
{
//All transformation code goes here
};
If needed you can also add code when there has ben a failure with downloading the image.
bitmapImage.ImageFailed += (sender, e) =>
{
//All code regarding image download failure goes here
};
So when you are sure the image has been downloaded you can start manipulating it. Now an extra note here, there is a great library available on Codeplex ( and NuGet ) that adds many extension methods to the WriteableBitmap that are handy when dealing with any image manipulation. It's called WriteableBitmapEx and can be found here ( http://writeablebitmapex.codeplex.com/ ).
We need to target our image to a tile of 173 by 173 pixels, thus we need to find the aspect - ratio. To do this, we first need to determine what is the largest side of our image.
var aspect = Math.Max(bitmapImage.PixelWidth, bitmapImage.PixelHeight);
After this we can determin the destination width and height...
var ratio = (decimal)aspect / (decimal)173;
var width = decimal.ToInt32(bitmapImage.PixelWidth / ratio);
var height = decimal.ToInt32(bitmapImage.PixelHeight / ratio);
All we need to do now is performing the actual resize.
WriteableBitmap writeableBitmap = new WriteableBitmap(bitmapImage);
var resized = writeableBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.Bilinear);
Now that you have 'constructed' your resized image, you'll have to store it on the phone's isolated storage before you can use it as a live tile image!
The live tile needs an URI to this saved image to work correctly. To get this done in one go you can use following method SaveTileBitmap, it will save the image and return you the correct URI.
private Uri SaveTileBitmap(WriteableBitmap bitmap, string fileName)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.DirectoryExists(@"Shared\ShellContent"))
store.CreateDirectory(@"Shared\ShellContent");
else if (store.FileExists("Shared/ShellContent/" + fileName))
store.DeleteFile("Shared/ShellContent/" + fileName);
using (
var stream = store.OpenFile(
@"Shared\ShellContent\" + fileName,
FileMode.OpenOrCreate))
{
bitmap.SaveJpeg(stream, 173, 173, 0, 100);
}
}
return new Uri(
"isostore:/Shared/ShellContent/" + fileName, UriKind.Absolute);
}
So the last stop is using this method to get the URI and constructing a live tile...
You'll need to give the image a name, but any name will do.
Uri localTileUri = this.SaveTileBitmap(resized, "LiveTileImage");
ShellTile firstTile = ShellTile.ActiveTiles.First();
if (firstTile != null)
{
var newTileData = new StandardTileData
{
BackgroundImage = localTileUri
};
firstTile.Update(newTileData);
}
BUT when you do this, you'll notice the image is still scaled to 173 px! This is something I didn't except, but it seems the tile will always try to stretch to 173 by 173, thus miss forming our image.
Resized image - wrong!

To compensate we need to paste our resized image on a dummy 173 by 173 before storing it in the isolated store... In other words best to store a correct sized square image with the resized image pasted inside it at the correct location.
Because we know the width was the smallest, we need to center the image on the tile! So we will be merging the resized image starting not at position 0.0 but compensating for the smaller width!
double rectX = (173 - width) / 2;
Rect rect = new Rect(0.0, 0.0, width, height);
WriteableBitmap bitmapDummy = new WriteableBitmap(173, 173);
bitmapDummy.Blit(new Rect(rectX, 0.0, width, height), resized, rect, WriteableBitmapExtensions.BlendMode.None);
Uri localTileUri = this.SaveTileBitmap(bitmapDummy, "LiveTileImage");
ShellTile firstTile = ShellTile.ActiveTiles.First();
if (firstTile != null)
{
var newTileData = new StandardTileData
{
BackgroundImage = localTileUri
};
firstTile.Update(newTileData);
}
Resized image - correct!

LongListSelector – Windows Phone 7 development
If you are using a Windows Phone 7, you'll have noticed that when you want to go through your People list they are all 'grouped' by one character. And when you press one of those characters, you'll get a nice overview of all available groups, again shown by a character.
Example 
Now if you are a wp7 developer and want to have the same kind of behaviour for any kind of 'lists' you want to present to the enduser, you have 2 options, 1. develop your own control or 2. use the LongListSelector available in the Sliverlight toolkit for WindowsPhone7!
Now to use this control, you better read up 2 posts available on windowsphonegeek
Now if you follow these posts, you'll notice you won't get the same look and feel as on the People list in the Windows Phone itself... now to overcome this, you'll better read up Benjii's post on how to use the LongListSelector!
Because when you follow his instructions, you'll get a better UI in reference to the People list on the Windows Phone.
But somehow when I used his solution, I was not able to get a correct Item Selection Change event to Command working. Somehow Benjii's group class won't let me cast back to one selected item in the ViewModel.
So again I needed a solution! And after some twitter chatter and a question on stackoverflow, I was directed to a custom implementation done by Claus Jørgensen!
The best way to look at his grouping class and LongListSelector usage, is to download his weather app from GitHub!
It contains a fine example on how to use the LongListSelector and also an extension that is needed to get Command binding to the SelectionChanged event!
His Grouping class also makes it possible to get the correct item from when the selection changed event get's fired!
I did however change one small detail of his Grouping class ( LongListCollection.cs ), because when you do nothing with Claus' class, you'll only see the item group keys of the available items. And Benjii's solution, first add's dummy key/list items so that you'll get the same overview as on the People list, when a group has no items the group key will be greyed out!!
To get this working with Claus' class, I added a DefaultHeaders list! Here is my implementation of the class:
namespace DepSoft.Mobile.WP7.iTunesControl.Model
{
public class LongListCollection<T, TKey> : ObservableCollection<LongListItem<T, TKey>>
where T : IComparable<T>
{
#region Constructor
public LongListCollection()
{
}
public LongListCollection(IEnumerable<T> items, Func<T, TKey> keySelector, List<TKey> defaultHeaders)
{
if (items == null)
throw new ArgumentException("items");
var groups = new Dictionary<TKey, LongListItem<T, TKey>>();
this.FillGroup(groups, defaultHeaders);
foreach (var item in items.OrderBy(x => x))
{
var key = keySelector(item);
if (groups.ContainsKey(key) == false)
groups.Add(key, new LongListItem<T, TKey>(key));
groups[key].Add(item);
}
foreach (var value in groups.Values)
this.Add(value);
}
#endregion
#region Private methods
private void FillGroup(Dictionary<TKey, LongListItem<T, TKey>> groups, List<TKey> defaultHeaders)
{
foreach (TKey key in defaultHeaders)
{
if (!groups.ContainsKey(key))
groups.Add(key, new LongListItem<T, TKey>(key));
}
}
#endregion
}
public class LongListItem<T, TKey> : ObservableCollection<T>
{
public LongListItem()
{
}
public LongListItem(TKey key)
{
this.Key = key;
}
public TKey Key
{
get;
set;
}
public bool HasItems
{
get
{
return Count > 0;
}
}
public Brush GroupBackgroundBrush
{
get
{
if (HasItems)
return (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
else
return (SolidColorBrush)Application.Current.Resources["PhoneChromeBrush"];
}
}
}
}
Now if you want to use this in your ViewModel, you only need to add the following 2 lines:
List<char> defaultHeaders = new List<char>("#abcdefghijklmnopqrstuvwxyz");
this.ListGroup = new LongListCollection<iBaseItem, char>(albumList.Cast<iBaseItem>(), c => c.GroupHeader[0], defaultHeaders);
I hope you guys have as much fun with the LongListSelector as I did




