Panorama to Hub

If you decided to use a Panorama control in your Windows Phone application, then you will be as disappointed as I am to discover Microsoft remove it from the Universal Windows Platform.

With a quick search on the web, you see it should be easy to replace with a Hub control.

But, surprise, the convenient ItemsSource property has been removed. In a panorama it was easy to bind a data source to generate one panorama section per item. You can’t natively do that with a Hub.windows-phone-7-panoramaDear Microsoft, bringing some cool stuff from Linux is great, but please consider keeping convenience for what is already awesome in your development environment: DataBinding. I would even say it’s one of the killer feature of C#/XAML applications.

Anyway, I will describe a DynamicHub implementation, to get the ability to generate one hub section per item in your data source.

First re-create your own DependencyObject. Then when the interesting dependency changes, iterate on your data and create one hub section per item.

    public class DynamicHub : DependencyObject
    {
        public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.RegisterAttached(
            "HeaderTemplate",
            typeof(DataTemplate),
            typeof(DynamicHub), new PropertyMetadata(null, HeaderTemplateChanged)
            );

        private static void HeaderTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var hub = d as Hub;
            if (hub == null) return;
            var template = e.NewValue as DataTemplate;
            if (template == null) return;
            foreach (var hubSection in hub.Sections)
            {
                hubSection.HeaderTemplate = template;
            }
        }
        public static void SetHeaderTemplate(UIElement element, DataTemplate value)
        {
            element.SetValue(HeaderTemplateProperty, value);
        }

        public static DataTemplate GetHeaderTemplate(UIElement element)
        {
            return element.GetValue(HeaderTemplateProperty) as DataTemplate;
        }

        public static readonly DependencyProperty SectionTemplateProperty = DependencyProperty.RegisterAttached(
            "SectionTemplate",
            typeof(DataTemplate),
            typeof(DynamicHub), new PropertyMetadata(null, SectionTemplateChanged)
            );

        private static void SectionTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var hub = d as Hub;
            if (hub == null) return;
            var template = e.NewValue as DataTemplate;
            if (template == null) return;
            foreach (var hubSection in hub.Sections)
            {
                hubSection.ContentTemplate = template;
            }
        }
        public static void SetSectionTemplate(UIElement element, DataTemplate value)
        {
            element.SetValue(SectionTemplateProperty, value);
        }

        public static DataTemplate GetSectionTemplate(UIElement element)
        {
            return element.GetValue(SectionTemplateProperty) as DataTemplate;
        }

        public static readonly DependencyProperty DataSourceProperty = DependencyProperty.RegisterAttached(
            "DataSource",
            typeof(object),
            typeof(DynamicHub),
            new PropertyMetadata(null, DataSourceChanged)
            );

        private static void DataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var data = e.NewValue as IEnumerable;
            var hub = d as Hub;
            var template = GetSectionTemplate(hub);
            var header = GetHeaderTemplate(hub);
            if (data == null || hub == null) return;

            hub.Sections.Clear();
            foreach (var section in data)
            {
                var sect = new HubSection { DataContext = section, ContentTemplate = template, HeaderTemplate = header };
                var hubData = section as IHubData;
                if (hubData != null)
                {
                    sect.Header = hubData.Header;
                }

                hub.Sections.Add(sect);
            }
        }

        public static void SetDataSource(UIElement element, object value)
        {
            element.SetValue(DataSourceProperty, value);
        }

        public static object GetDataSource(UIElement element)
        {
            return element.GetValue(DataSourceProperty);
        }
        private interface IHubData
        {
            object Header { get; }
        }
    }

Here is a XAML example to use it:

        <Hub local:DynamicHub.DataSource="{Binding HubSections}"
                local:DynamicHub.SectionTemplate="{StaticResource HubSectionTemplate}"
                local:DynamicHub.HeaderTemplate="{StaticResource HubHeaderTemplate}">
        </Hub>

And a data template to display it:

            <DataTemplate x:Key="HubHeaderTemplate">
                <StackPanel>
                    <TextBlock Text="HubHeader" Style="{StaticResource TitleTextBlockStyle}"/>
                    <TextBlock Text="{Binding SectionHeader}" Style="{StaticResource TitleTextBlockStyle}"/>
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="HubSectionTemplate">
                <StackPanel>
                    <TextBlock Text="HubSection" Style="{StaticResource BodyTextBlockStyle}"/>
                    <TextBlock Text="{Binding SectionNumber}" Style="{StaticResource BodyTextBlockStyle}"/>
                </StackPanel>
            </DataTemplate>

Hope it helps, full demo code is on GitHub.

 

Ouarzy

 

Leave a Reply

Your email address will not be published. Required fields are marked *

IP Blocking Protection is enabled by IP Address Blocker from LionScripts.com.