1

I'm quite new to programming WinRT/C#/XAML, so please excuse if this is a simple question for you.

I would like to arrange an amount of images (which shall be clickable in the future) on some kind of background (Panel, Canvas, Grid, you name it). So I could use the predefined user controls like ListView etc., but in my case, I have one special criterium: The x- and y-Index of each Element should be derived from the ViewModel. (Background: I try to create a sort of "grading"-application, where students are arranged in the way they sit in front of me in the classroom. A click on the upper area of the students image increases the grade, a click in the lower area will decrease it.)

So, I was looking through many and more different tutorials and examples, but as far as I can see, in all of those ItemsControl/ItemsPanel-Examples, the arrangement of the Items is done by the parent-element, namely the ItemsControl. That way, I don't see how I can access the positioning through data binding.

If anyone would be so kind and post perhaps just an idea, how something like

public class data{
  public BitmapImage Image{get;set;}
  public String Name{get;set;}
  public int xIndex {get;set;}
  public int yIndex {get;set;}
}

, collected in an ObservableCollection, could be displayed (and in the future, handled) in the above described way?

Thank you in advance

Adrian

1 Answers1

1

I found a way how it could work, it's not the best but in that way you can override that the arrangement of the items is done by the parent-control.

your class:

public class Data
{
    public BitmapImage Image { get; set; }
    public string Name { get; set; }
    public double xIndex { get; set; }
    public double yIndex { get; set; }
}

ViewModel:

public class ViewModel


  {
        private readonly ObservableCollection<Data> images = new ObservableCollection<Data>();
        public ObservableCollection<Data> Images
        {
            get { return images; }
        }

        public ViewModel()
        {
            Images.Add(new Data
            {
                Image = new BitmapImage(new Uri("ms-appx:///../Assets/StoreLogo.scale-100.png")),
                Name = "Image1",
                xIndex = 50,
                yIndex = 50
            });

            Images.Add(new Data
            {
                Image = new BitmapImage(new Uri("ms-appx:///../Assets/SmallLogo.scale-100.png")),
                Name = "Image2",
                xIndex = 250,
                yIndex = 250
            });

            Images.Add(new Data
            {
                Image = new BitmapImage(new Uri("ms-appx:///../Assets/SplashScreen.scale-100.png")),
                Name = "Image3",
                xIndex = 500,
                yIndex = 500
            });
        }
    }

Page:

<Page
    x:Class="App8.MainPage"
    DataContext="{Binding ViewModel, RelativeSource={RelativeSource Self}}"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App8"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="0 100 0 0">

        <ItemsControl ItemsSource="{Binding Images}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentPresenter x:Name="Test" Canvas.Left="{Binding xIndex}" Canvas.Top="{Binding yIndex}">
                        <StackPanel>
                            <TextBlock Text="{Binding Name}"/>
                            <Image Height="50" Source="{Binding Image}"/>
                        </StackPanel>
                    </ContentPresenter>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <local:MyCanvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Page>

You need your own ItemsPanelTemplate to override the arrangement.

public class MyCanvas : Canvas
{
    protected override Size ArrangeOverride(Size finalSize)
    {
        var children = this.GetDescendantsOfType<ContentPresenter>();

        foreach (var item in children)
        {
            var data = item.Content as Data;
            if (data != null)
            {
                Canvas.SetLeft(item, data.xIndex);
                Canvas.SetTop(item, data.yIndex);
            }
        }

        return base.ArrangeOverride(finalSize);
    }
}

GetDescendantsOfType is part of the VisualTreeHelperExtensions from the WinRT XAML Toolkit.

public static class VisualTreeHelperExtensions
{
    /// <summary>
    /// Gets the descendants of the given type.
    /// </summary>
    /// <typeparam name="T">Type of descendants to return.</typeparam>
    /// <param name="start">The start.</param>
    /// <returns></returns>
    public static IEnumerable<T> GetDescendantsOfType<T>(this DependencyObject start) where T : DependencyObject
    {
        return start.GetDescendants().OfType<T>();
    }

    /// <summary>
    /// Gets the descendants.
    /// </summary>
    /// <param name="start">The start.</param>
    /// <returns></returns>
    public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject start)
    {
        if (start == null)
        {
            yield break;
        }

        var queue = new Queue<DependencyObject>();

        var popup = start as Popup;

        if (popup != null)
        {
            if (popup.Child != null)
            {
                queue.Enqueue(popup.Child);
                yield return popup.Child;
            }
        }
        else
        {
            var count = VisualTreeHelper.GetChildrenCount(start);

            for (int i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(start, i);
                queue.Enqueue(child);
                yield return child;
            }
        }

        while (queue.Count > 0)
        {
            var parent = queue.Dequeue();

            popup = parent as Popup;

            if (popup != null)
            {
                if (popup.Child != null)
                {
                    queue.Enqueue(popup.Child);
                    yield return popup.Child;
                }
            }
            else
            {
                var count = VisualTreeHelper.GetChildrenCount(parent);

                for (int i = 0; i < count; i++)
                {
                    var child = VisualTreeHelper.GetChild(parent, i);
                    yield return child;
                    queue.Enqueue(child);
                }
            }
        }
    }
}
Dani
  • 971
  • 12
  • 31
  • Hey, that really looks like a promising approach. I just tested it and it looks like it could fulfill my needs. Thank you for your quick and detailed response! – Adrian Lehrmann Dec 19 '14 at 18:53