jeudi 22 mars 2018

UWP, C# - Retrieve the redirect url using HttpClient from the Headers of the Response - [HttpClient,C#]

I wanted to share this little piece of code because I did not quickly find any great documentation on this. 

My needs where as follows: I had a URL lets say http://bit.ly/2pwccfo and I did not have the direct access to the image of the URL.  So I ended up creating a small static class that holds a method GetRedirectedUrl that will get my redirect url.

Here is my code:

public static class CoreTools
{
       public static async Task<string> GetRedirectedUrl(string url)
        {
            //this allows you to set the settings so that we can get the redirect url
            var handler = new HttpClientHandler()
            {
                AllowAutoRedirect = false
            };
            string redirectedUrl = null;


            using (HttpClient client = new HttpClient(handler))
            using (HttpResponseMessage response = await client.GetAsync(url))
            using (HttpContent content = response.Content)
            {
                // ... Read the StatusCode in the response 
                // to see if we have the redirected url
                if (response.StatusCode == System.Net.HttpStatusCode.Found)
                {
                    HttpResponseHeaders headers = response.Headers;
                    // do we have headers 
                    //do we have something in location
if (headers != null && headers.Location != null) { redirectedUrl = headers.Location.AbsoluteUri; } } } return redirectedUrl; } }

You can find my gist here.
Happy Coding

jeudi 1 mars 2018

UWP Imbricking a ItemTemplateSelector in a ItemTemplateSelector to create a dynamic listview with horizontal and vertical items

In this tutorial we are going to see how to create a dynamic listview that can hold horizontal and vertical items, this tutorial will be broken into 3 parts :

  • First we will create ou Model and and Data
  • Next we we create the UserControl and DataTemplates to hold our listview and gridviews
  • Lastly we will look at how to use a ItemTemplateSelectoron our first listview.

Now here are 2 screen shots of what we would like to do, the first one is from the Window Store App and the second one from the dailymotion UWP app.


this one looks like one listview that is holding another listview with an ItemTemplateSelector, however these seem to have a fix number of items they are showing so in the end it could be the it is only one listview with an ItemTemplateSelector that select the correct template for when you have 4 items, 6 items or 3 items and so on.

I want to create something more generic like in the dailymotion app :




Let start coding!

Here is a quick diagram of how the idea of imbricking 2 DataTemplateSelector selector will work:



We are going to need 2 enums that will allow us to say when the segment is vertical or horizontal and the second one will allow us to know which template we will need to set in the second gridview.

This allows us to know what to sent in our first listview in its ItemTemplate: Horizontal/Vertical
public enum SegmentType
  {
        HorizontalSegment,
        VerticalSegment,
  }


This is for when we are in the Horizontal/Vertical Control so that we can set the ItemTemplate for the GridView:
 public enum ItemTemplateType
    {
        CircleUserItem,
        SquareUserItem,
    }

Next we are going to need to create two DataTemplateSelector so that depending on what kind of enum the EntityViewModel is passing we can assign the correct DataTemplate.

This is our DataTemplateSelector for our main listview, it will server either a horizontal gridview or a vertical Gridview:


 public class SegementTemplateSelector : DataTemplateSelector
    {
        public DataTemplate KeyHorizontalControl = Application.Current.Resources["KeyHorizontalControl"] as DataTemplate;
        public DataTemplate KeyVerticalControl = Application.Current.Resources["KeyVerticalControl"] as DataTemplate;

        protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
        {

            var element = item as BaseSegementEntityViewModel;

            if (element != null)
            {
                if (element.ItemType == SegmentType.HorizontalSegment)
                    return KeyHorizontalControl;
                else
                    return KeyVerticalControl;
            }
            return base.SelectTemplateCore(item, container);
        }
    }


This is our DataTemplateSelector for the Gridviews that are inside our Horizontal/Vertical Control it allows to know if we are serving a square or a circle DataTemplate:


 public class ItemTemplateSelector : DataTemplateSelector
    {
        public DataTemplate KeyCircleUserControl = Application.Current.Resources["KeyCircleUserControl"] as DataTemplate;
        public DataTemplate KeySquareUserControl = Application.Current.Resources["KeySquareUserControl"] as DataTemplate;
        
        protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
        {

            var element = item as BaseItemEntityViewModel;
            
            if (element != null)
            {
                switch (element.ItemTemplate)
                {
                    case ItemTemplateType.CircleUserItem:
                        return KeyCircleUserControl;


                    default:
                        return KeySquareUserControl;
                }

            }
            return base.SelectTemplateCore(item, container);
        }
    }

Bonus: this could also be used to serve BOTH a Circle DataTemplate and a square DataTemplate in the Gridview depending on the type of the item.

Next we will be looking at the XAML in our Horizontal/Vertical Controls.

HorizontalControl.xaml :


 <GridView
        x:Name="HorizontalGridView"
        Height="250"
        Margin="26,0,0,0"
        HorizontalAlignment="Stretch"
        ItemTemplateSelector="{StaticResource KeyItemTemplateSelector}"
        ItemsSource="{Binding MyItemSource}"
        ScrollViewer.HorizontalScrollBarVisibility="Hidden"
        ScrollViewer.HorizontalScrollMode="Enabled"
        ScrollViewer.VerticalScrollBarVisibility="Disabled">

        <GridView.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsStackPanel
                    Margin="5"
                    HorizontalAlignment="Left"
                    Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </GridView.ItemsPanel>
    </GridView>

In the HorizontalControl we need to change the orientation of the ItemsStackPanel of the ItemsPanelTemplate. In this GridViewI edit the ScrollViewer settings so that they match my neededs.

VerticalControl.xaml :


 <GridView
        x:Name="VerticalGridView"
        Width="300"
        Margin="26,0,0,0"
        HorizontalAlignment="Stretch"
        ItemTemplateSelector="{StaticResource KeyItemTemplateSelector}"
        ItemsSource="{Binding MyItemSource}"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
        ScrollViewer.HorizontalScrollMode="Disabled"
        ScrollViewer.IsHorizontalScrollChainingEnabled="False"
        ScrollViewer.IsVerticalScrollChainingEnabled="False"
        ScrollViewer.VerticalScrollBarVisibility="Disabled"
        ScrollViewer.VerticalScrollMode="Disabled">
    </GridView>

This is really just your normal GridView with an item template selector.

Now we will look at our main page which will be holding our Listview with an ItemTemplateSelector which will select if your items will be displayed Horizontally or Verticaly.

 <ListView
            Grid.Row="0"
            Margin="0,0,0,0"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            IsTabStop="False"
            ItemTemplateSelector="{StaticResource KeySegementTemplateSelector}"
            ItemsSource="{Binding MyData}"
            ScrollViewer.VerticalScrollBarVisibility="Auto"
            ScrollViewer.VerticalScrollMode="Enabled"
            SelectionMode="None"
            ShowsScrollingPlaceholders="False"
            VirtualizingStackPanel.VirtualizationMode="Recycling"
            Visibility="Visible" />


Now we need to create some data, first we will start by creating 2 lists. One that will hold our list of Circles and the second one that will hold our list of Squares:

 //circle list
 var CircleList = new List<SmallItemViewModel>();

//square list
var SquareList = new List<SmallItemViewModel>();

Next we will need to populate these 2 lists:

for (int i = 0; i < 10; i++)
{
     CircleList.Add(new SmallItemViewModel(string.Format("title circle {0}", i ), ItemTemplateType.CircleUserItem));
   
     SquareList.Add(new SmallItemViewModel(string.Format("title square {0}", i), ItemTemplateType.SquareUserItem));
}

Now we create another list that will hold the Segements and their data.

//horizontal // vertical  
MyData = new ObservableCollection<BaseSegementEntityViewModel>();

now we populate this list:
MyData.Add(NeonExploreTemplateEngine(SquareList, SegmentType.HorizontalSegment));        
MyData.Add(NeonExploreTemplateEngine(CircleList.Take(3).ToList(), SegmentType.VerticalSegment));
MyData.Add(NeonExploreTemplateEngine(CircleList, SegmentType.HorizontalSegment));
           
Here is the full code:


private async Task Init()
        {
            //horizontal // vertical  
            MyData = new ObservableCollection<BaseSegementEntityViewModel>();

            //circle list
            var CircleList = new List<SmallItemViewModel>();

            for (int i = 0; i < 10; i++)
            {
                CircleList.Add(new SmallItemViewModel(string.Format("title circle {0}", i ), ItemTemplateType.CircleUserItem));
            }

            //square list
            var SquareList = new List<SmallItemViewModel>();

            for (int i = 0; i < 10; i++)
            {
                SquareList.Add(new SmallItemViewModel(string.Format("title square {0}", i), ItemTemplateType.SquareUserItem));
            }


            //data
            MyData.Add(NeonExploreTemplateEngine(SquareList, SegmentType.HorizontalSegment));        
            MyData.Add(NeonExploreTemplateEngine(CircleList.Take(3).ToList(), SegmentType.VerticalSegment));
            MyData.Add(NeonExploreTemplateEngine(CircleList, SegmentType.HorizontalSegment));
            MyData.Add(NeonExploreTemplateEngine(SquareList.Take(3).ToList(), SegmentType.VerticalSegment));
            MyData.Add(NeonExploreTemplateEngine(SquareList, SegmentType.HorizontalSegment));
            MyData.Add(NeonExploreTemplateEngine(CircleList, SegmentType.HorizontalSegment));
            MyData.Add(NeonExploreTemplateEngine(CircleList.Take(3).ToList(), SegmentType.VerticalSegment));

        }

        public BaseSegementEntityViewModel NeonExploreTemplateEngine(List<SmallItemViewModel> item, SegmentType type)
        {
            if (type == SegmentType.HorizontalSegment)
            {
                return new HorizontalSegmentViewModel(item);
            }
            else
            {
                return (new VerticalSegmentViewModel(item));
            }          
        }


And here is what we would get:



In the same Listview you will have elements position Vertically and Horizontally at the same time, also you will be able to change your design on the fly depending on the data that you send to your application.  

We can quickly see how powerful it can be to design an application that relies on ItemTemplateSelector , for example this could mean that if you didn't want to use a certain template anymore you would not need to update the application but just the API that the application is using. This could be a great time saver! 

You could also do some form of A/B testing, if for example you had multiple Circles Template you could try different templates to see which templates your user will more likely click on!

I hope that this will help you build better apps!

Happy Coding!
You can find the full sample here.