Nico's digital footprint

I grew up in the nineties, that makes me awesome by default

WinRT: GroupedGridView first item other template

by Nico

I’m currently building a comic application for Windows 8, yesterday I was building the page where users can see the details about a certain comic issue. The idea I had for this page was that on the left was the cover of the issue, followed by a list of characters appearing in the issue. A grouped GridView would be perfect for this scenario but it only accepts one template, so all groups usually look the same. Here’s the way that I’ve fixed it, I’m not 100% happy with it but it works. If you have a better way, please tell me, I’ll update this post accordingly and give credit where credit is due off course.

Let’s start off with the class. I have a class called Issue that obviously contains all the information about an issue.

public class Issue
{
    public int Id { get; set; }
    public bool IsImage { get; set; }
    public string Name { get; set; }
    public string Image { get; set; }
    public List<Character> Characters { get; set; }
}

Notice the IsImage boolean? That one’s the key to the whole thing. The image itself is just a string to an image on the web. Next up is the Character class.

public class Character : INotifyPropertyChanged
{
    public string Name { get; set; }
    public bool IsImage { get; set; }

    private string _image;

    public string Image
    {
        get { return _image; }
        set
        {
            if (_image == value)
                return;

            _image = value;

            RaisePropertyChanged("Image");
        }
    }

    public Character()
    {
        IsImage = false;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

}

Again in this class a boolean called IsImage. This time however the boolean has an initial value of False set in the constructor.

A third class is a generic class that will contain all collections to be shown in every group in the GridView

public class GroupInfoList<T> : List<object>
{
    public object Key { get; set; }

    public new IEnumerator<object> GetEnumerator()
    {
        return base.GetEnumerator();
    }
}

This is a pretty basic implementation that you will find on many blogs and samples about grouped GridView.

The next step is in the viewmodel, where I’ll load in the data and prepare everything to be shown in the Grouped GridView.

private async void LoadData()
{
    IssueGroups = new ObservableCollection<GroupInfoList<object>>();
    var issueDetails = await _service.GetIssueDetail(SelectedIssue.Url);

    var cover = new GroupInfoList<object> { Key = "Cover" };
    SelectedIssue.IsImage = true;
    cover.Add(SelectedIssue);
    IssueGroups.Add(cover);

    var info = new GroupInfoList<object> { Key = "Characters" };
    info.AddRange(issueDetails.Characters);
    IssueGroups.Add(info);
}

First I create an ObservableCollection of type GroupInfoList, the class I’ve created in the previous step. Then I’ll ask my service to get me all the details of the issue. Once I have all the info I start dividing it into the groups that I want it shown on the page. I wanted to start off with the cover in the first group but I can’t just put the image url in the group because that way I can’t pass anything to my converter later on. So instead I just put the entire issue object in the first group and I set the IsImage property to True.

The second group will contain all the characters, remember that Character sets IsImage to false in the constructor, so no need to do that here explicitly. I now have everything set to show two groups in my GridView. Onto the XAML we go.

So now we reached the actual problem. We can set one ItemTemplate but I need two. Here’s what my template looks like.

<GridView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
            <Border BorderBrush="Black" BorderThickness="2" Width="300" HorizontalAlignment="Left"
Height="120" Visibility="{Binding IsImage,
Converter={StaticResource KeyToVisibilityConverterReverse}}"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,0"> <Image Source="{Binding Image}" Height="100" /> <StackPanel> <TextBlock Text="{Binding Name}" Width="180" Foreground="Black"
Margin="12,0,0,0" TextWrapping="Wrap" /> <TextBlock Text="{Binding Role}" Width="180" Foreground="Black"
Margin="12,0,0,0" TextWrapping="Wrap" /> </StackPanel> </StackPanel> </Border> <Image Source="{Binding Image}" Height="500" Visibility="{Binding IsImage,
Converter={StaticResource KeyToVisibilityConverter}}" /> </StackPanel> </DataTemplate> </GridView.ItemTemplate>

I've build a stackpanel that contains both my templates, one is just a simple image bound to the cover image. The second is a border containing a smaller image and a few textboxes. And as you can see, the Visibility of both templates are bound to IsImage. Now I have two converters to bind too, one has to return “Visible” when IsImage is True while the other one has to return “Collapsed”. Here’s what the converters look like.

public class KeyToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return (bool)value ? "Visible" : "Collapsed";
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

public class KeyToVisibilityConverterReverse : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return (bool)value ? "Collapsed" : "Visible";

    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

And that did the trick for me, this is what my result looks like.

 

I hope I helped some people out with this. Like I’ve said at the beginning of this post, this feels more like some sort of dirty hack than a decent solution but it works. Please let me know if you have another way of doing this.


Tags:

.Net | MVVM Light | Metro | Windows 8 | XAML

Weekly update on my XNA project

by Nico

My first week back on the job is finished, that means I’ve had about 5 hours of time to work on my lunch game. Progress started of pretty small because of some enemy placement issues that showed up after I changed the complete control scheme. After loosing about an hour of time trying to fix it, I’ve decided to revert back to the virtual thumbsticks. About half an hour and some Mercurial commands later I was back to the original controls, the enemy movement behavior also reverted back to normal.

Day 2 I decided it was about time to make the enemy move, the character itself was moving up and down but without any animation, he was just sliding up and down. My animations are done using a spritesheet, the eight walking directions each have a line in the spritesheet. In the code itself I have a rectangle that has the width and height of one texture on the sheet, using the Update method I move the rectangle after x amount of time. This is the piece of code that creates the animation

protected Rectangle SourceRectRunning;
protected readonly TimeSpan RunRate = TimeSpan.FromSeconds(0.1);
protected TimeSpan RunTimer;

public override void Update(GameTime gameTime)
{
    RunTimer -= gameTime.ElapsedGameTime;

    if (RunTimer <= TimeSpan.Zero)
    {
        if (CharState == State.Running)
        {
            if (SourceRectRunning.X >= TextureWidth * 7)
            {
                SourceRectRunning.X = 0;
                return;
            }

            SourceRectRunning.X = SourceRectRunning.X + (int)TextureWidth;
        }

        RunTimer = RunRate;
    }
}

 

The CharState is just en enum that I’ve build. The enum looks like this

public enum State
{
    Running,
    Standing,
    Spotted,
    Walking
}

This makes it very easy for me to call the right code when the player starts moving or when he’s spotted by the enemy.

I was very happy of how the animation looked like, so I figured it was about time to do something about the collision detection. This is something I’ve implemented quite some time ago but it never really worked how it should. The way collision detection is working in my app is a pretty nifty one. I found it in the demo code of the tile engine that I’m using.

A map build with Tiled consist of several layers, I have a layer that contains only the floor textures and a second layer that contains stuff like trees, walls and other non-interactable objects. The demo project contained with the WP7 Tiled engine has a method that can get the color of a certain tile on a certain layer. I take the player’s position, add a few pixels to it and determine the color on that location of the Objects layer, if the color is NULL then there’s no object in the way. Let me show you the code

Color? collColor = _level.Map.GetColorAt("Objects", _player.WorldPosition + 
    (VirtualThumbsticks.LeftThumbstick * 5));

if (collColor == null)
{
    //player can move
}

The GetColorAt method looks like this

public Color? GetColorAt(string layerName, Vector2 position)
{
    var l = GetLayer(layerName);

    TileLayer tileLayer = l as TileLayer;

    position.X = (int)position.X;
    position.Y = (int)position.Y;

    Vector2 tilePosition = new Vector2((int)(position.X / TileWidth), (int)(position.Y/TileHeight));

    Tile collisionTile = tileLayer.Tiles[(int)tilePosition.X, (int)tilePosition.Y];

    if (collisionTile == null)
        return null;

    if (collisionTile.CollisionData != null)
    {
        int positionOnTileX = ((int)position.X - (((int)position.X / TileWidth) * TileWidth));
        int positionOnTileY = ((int)position.Y - (((int)position.Y / TileHeight) * TileHeight));
        positionOnTileX = (int)MathHelper.Clamp(positionOnTileX, 0, TileWidth);
        positionOnTileY = (int)MathHelper.Clamp(positionOnTileY, 0, TileHeight);

        int pixelCheckX = (collisionTile.Source.X) + positionOnTileX;
        int pixelCheckY = (collisionTile.Source.Y) + positionOnTileY;

        return collisionTile.CollisionData[(pixelCheckY * collisionTile.Texture.Width) + pixelCheckX];
    }
    else
    {
        return null;
    }
}

As I’ve said, this piece of code comes from the Tiled engine I’ve found on this site.

The problem I was having before was that I was passing the player’s coordinate on the screen to the method instead of the player’s position on the map. It does work much better now but currently I’m passing the coordinate of the upper left corner of the texture (XNA default) so it does need some fine-tuning.

I’m thinking next week to do some more work on the level editor or maybe start working on interactable objects. I’ll write another post next week, so stay tuned or follow me on Twitter to get daily updates.


Tags:

.Net | Game development | WP7 | XNA

  Log in

About the author

Hi,

My name is Nico, I’m an MVP Windows Platform Development living in Belgium.
I’m currently employed as a .NET consultant at RealDolmen, one of Belgium’s leading IT single source providers.

I'm also founding member and board member of the Belgian Metro App Developer Network, a user group focussed on Windows 8 and Windows Phone development. If you're in Belgium feel free to drop by if we're doing an event. http://www.madn.be

Since June 2012 I'm a proud member of Microsoft's Extended Experts Team Belgium. And in February 2013 I became a member of DZone's Most Valuable Bloggers family.

In 2013 I became a book author and wrote "Windows 8 app projects, XAML & C# edition".

In 2014 I received the MVP award for the very first time.

I hope to get feedback from my readers either through comments, mail (nico_vermeir@hotmail.com), twitter, facebook, …

 

mvp

 

mvp

 

 

MeetLogo

 

MVBLogo

mybook

 

Month List