dimanche 9 décembre 2018

Using WinAppDriver to do UI Test Automation on your Windows Application, using the My Radio UWP - Part I

Because test is caring you should always try to test as much as possible your application to make sure that is it running smoothly.

We are going to setup multiples UI tests paths that need to be passed before a new version of the application can be pushed to the store. This will allows us to make sure that our application passes all of the basic quality checks and that all classic user paths are still functional.

We are going to have a deep look into one of these test path so that you too can improve your app by using UI test automation. 

Setup

First I highly recommend to have a look at the github repository on how WinAppDriver can be used:

Installing and Running Windows Application Driver

  1. Download Windows Application Driver installer from https://github.com/Microsoft/WinAppDriver/releases
  2. Run the installer on a Windows 10 machine where your application under test is installed and will be tested
  3. Run WinAppDriver.exe from the installation directory (E.g. C:\Program Files (x86)\Windows Application Driver)
Windows Application Driver will then be running on the test machine listening to requests on the default IP address and port (127.0.0.1:4723). You should have something as follows:




Locating Elements

The latest Visual Studio version by default includes the Windows SDK with a great tool to inspect the application you are testing. This tool allows you to see every UI element/node that you can query using Windows Application Driver
This inspect.exe tool can be found under the Windows SDK folder which is typically C:\Program Files (x86)\Windows Kits\10\bin\

Once you have launched the tool you should have a window as follows:






Here we can see that I have already launch the My Radio application.

We are going to be looking at 3 users Paths:

  • Arrive on Home, check we are on Home, click on favorite check we are on favorite and then go back to home
  • Can we will click on a radio thumbnail and make sure that it is playing
  • Advanced test : In another post we will go over how to create a more complex test in which we will:  We click on discovery, click on a category, click on a radio add it to our favorites, go to favorites and check that it is there.

Can I click on a button

First to be able to have the inspect tool select and highlight your selected item you must have activated these two buttons which are called watch cursor and show highlighted rectangle.


Now we are going to select the item that we wish to click on.  In my application the XAML code for this home button is:


                <NavigationViewItem x:Name="MainView" Tag="MainView">
                    <StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
                        <SymbolIcon
                            Width="30"
                            Height="30"
                            Margin="-5,0,0,0"
                            Symbol="Home" />
                        <TextBlock
                            Margin="12,0,0,0"
                            VerticalAlignment="Center"
                            Text="Home" />
                    </StackPanel>
                </NavigationViewItem>

Here is what it looks like in the inspect tool:


We can see that the automation id is "MainView" and when we look on the favorite button we can see that its id is "FavoriteView" for our first test we are going to be using FindElementByName to check and see if we have a specific text in the visible view and FindElementByAccessibilityId to look for a UIElement that has this specific Id.

Here is the TestMethod:


 [TestMethod]
 public void IsSideNavigationWorking()
 {
            //We arrive on home            
            var homeText = session.FindElementByName("Explore Radios");

            //check to see if we see the explore radios text
            Assert.IsNotNull(homeText);

            //click on categorie
            session.FindElementByAccessibilityId("FavoriteView").Click();

            //check to see if we see the fav radios text
            var favText = session.FindElementByName("My Favorite Radios");
            Assert.IsNotNull(favText);
            
            //go back on home view
            session.FindElementByAccessibilityId("MainView").Click();
            var homeTextV2 = session.FindElementByName("Explore Radios");

            //check to see if we are on correct view
            Assert.IsNotNull(homeTextV2);
 }


Is Radio playing user path

Here is the user path we are going to check:

  • launch the application
  • click on home and check that home is selected
  • click on first radio thumbnail and check that the radio is playing.
    • to check that radio is playing we are going to look for the media element that plays the radio item
Here is the inspect tool on the first radio in our list.


And here is the full test:


Here you have it, we have gone over two easy example on how to use WinAppDriver to do our UI Test Automation on a UWP Application. In the next Article we will do a deepdive on how to use WinAppDriver.




lundi 27 août 2018

[UWP/XAML] How to customize your NavigationView and add NavigationViewItem next to the Settings button

In this tutorial we are going to see how to add NavigationViewItems next to your Settings button in your NavigationView view.

First we need to get the Template of the NavigationView, Document Outline => Your NavigationView => Edit Template => edit a Copy.

Here is mine:


Look for the style that has a target type = Navigation View, here is mine:


 <Style x:Key="NavigationViewStyle1" TargetType="NavigationView">

Next we will need to find the NavigationViewItem that holds our settings button:


<NavigationViewItem x:Name="SettingsNavPaneItem" Grid.Row="7">
               <NavigationViewItem.Icon>
                      <SymbolIcon Symbol="Setting" />
               </NavigationViewItem.Icon>
</NavigationViewItem>


Now we are going to add our 2 new buttons, first we are going to add s stackpanel and move the property Grid.Row=7 to it as follows:



<StackPanel Grid.Row="7">
   <NavigationViewItem x:Name="AddedNavItemOne"    Tag="MyViewOne">
  <NavigationViewItem.Icon>
   <SymbolIcon Symbol="World" />
  </NavigationViewItem.Icon>
</NavigationViewItem> <NavigationViewItem x:Name="AddedNavItemTwo" Tag="MyViewTwo">
   <NavigationViewItem.Icon>
   <SymbolIcon Symbol="Search" />
  </NavigationViewItem.Icon>
</NavigationViewItem> <NavigationViewItem x:Name="SettingsNavPaneItem"> <NavigationViewItem.Icon> <SymbolIcon Symbol="Setting" /> </NavigationViewItem.Icon> </NavigationViewItem> </StackPanel>

Now we need to be able to handle the Click actions of these 2 new NavigationViewItem.

We are going to create a class that inherits from NavigationView that will be called ExtendedNavigationView and we arer going to look for our 2 buttons and set handlers on the Tapped events as follows:

_navItemOne= GetTemplateChild("AddedNavItemOne") as NavigationViewItem;
            
//addign events
_navItemOne.Tapped += NavItem_Tapped;

The full class will look as follows:

 public class ExtendedNavigationView : NavigationView
    {
        private NavigationViewItem _navItemOne;
        private NavigationViewItem _navItemTwo;

        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            SetupExtraNavItems();
        }

        private void SetupExtraNavItems()
        {
            //check to see if not already set
            if (_navItemOne!= null)
            {
                //unload events
                _navItemOne.Tapped -= NavItem_Tapped;
               
            }

            if (_navItemTwo!= null)
            {
                _navItemTwo.Tapped -= NavItem_Tapped;
            }

            _navItemOne= GetTemplateChild("AddedNavItemOne") as NavigationViewItem;
            _navItemTwo = GetTemplateChild("AddedNavItemTwo") as NavigationViewItem;           

            if (_navItemOne== null || _navItemTwo== null)
            {
                return;
            }

            //addign events
            _navItemOne.Tapped += NavItem_Tapped;
            _navItemTwo.Tapped += NavItem_Tapped;            
        }
        

        private void NavItem_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
        {
            var NavView = (sender as NavigationViewItem).Tag;
            //navigate to item
        }


And there you have it you have customized your NavigationView with 2 new NavigationViewItems above the Settings button.

Happy Coding.




mercredi 1 août 2018

[UWP/XAML] using Microsoft.Toolkit for Implicit animations using ScalarAnimation, OpacityAnimation, TranslationAnimation

Do you hate having to use Compositor?


_compositor = new Compositor();
_root = ElementCompositionPreview.GetElementVisual(RootGrid);

Well Microsoft.Toolkit.Uwp.UI.Animations is here to save you (and me).  The toolkit has added implicit animation which will allow you to easily add animations to your elements.  You wont need to use Storyboard animation!

In this example we are going to see how to:

  •  Add a translation to an element
  • Show and hide an element using Opacity
  • Scale an element
The advantage of using an Implicit animation is that it can for example be execute when you change the visibility of a component using:
 <animations:Implicit.ShowAnimations>

Or
 <animations:Implicit.HideAnimations>

Animations

To move an element in your app using implicit animation you have 2 options either you use ScalarAnimation or TranslationAnimation.

TranslationAnimation

Has 3 main properties that you are going to want to set:
  • From
  • To
  • Duration
From and To are based on the x-y-z axis
Duration uses a h:m:s format

 <animations:TranslationAnimation
                    From="500,300,0"
                    To="0"
                    Duration="0:0:1" />

This means we are going to add 500 to the x position, 300 to the y postion and 0 to the z. The To=0 will take the element from its current position and the translation will take 1 second.


OpacityAnimation 

Same as the translation this also takes 3 properties, however the From and To only takes one value as the opacity property is only based on one value.



<animations:OpacityAnimation
                    From="0"
                    To="1"
                    Duration="0:0:2" />


ScalarAnimation

If you wish to have more control on your animation you can also use ScalarAnimation which will allow you to more precisely (I find) to set the values to the property you wish to change.



            <animations:ScalarAnimation
                    Target="Translation.Y"
                    From="100"
                    To="0"
                    Duration="0:0:1"/>


First you need to set your Target, which is the property you are going to want to change.  Then depending on your target like the other animations you will set the From, To, Duration


Combining Animations

Now we are going to combine 2 animations using the ShowAnimations and HideAnimations.  We will use ScalarAnimation and OpacityAnimation to move and change the opacity of our object.

Here is the full XAML:

 <Button Click="Button_Click" Content="Hit me" />
        <Rectangle
            x:Name="MyRec"
            Grid.Row="0"
            Height="45"
            Margin="0,0,0,0"
            VerticalAlignment="Bottom"
            Fill="Red"
            Visibility="Collapsed">
            <animations:Implicit.ShowAnimations>
                <!--<animations:TranslationAnimation
                    From="100,0,0"
                    To="0,0,0"
                    Duration="0:0:1" />-->
                <animations:ScalarAnimation
                    Target="Translation.Y"
                    From="100"
                    To="0"
                    Duration="0:0:1">                  
                </animations:ScalarAnimation>

                <animations:OpacityAnimation
                    From="0"
                    To="1"
                    Duration="0:0:2" />
            </animations:Implicit.ShowAnimations>

            <animations:Implicit.HideAnimations>
                <animations:OpacityAnimation
                    From="1"
                    To="0"
                    Duration="0:0:2" />

                <animations:ScalarAnimation
                    Target="Translation.Y"
                    From="0"
                    To="100"
                    Duration="0:0:1">                
                </animations:ScalarAnimation>

                <!--<animations:TranslationAnimation
                    From="0,0,0"
                    To="100,0,0"
                    Duration="0:0:1" />-->
            </animations:Implicit.HideAnimations>
        </Rectangle>

C# Code:


private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (MyRec != null)
            {
                MyRec.Visibility = MyRec.Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
            }
        }

Happy Coding.





lundi 25 juin 2018

UWP/XAML using PopupThemeTransition / PopOutThemeAnimation / PopInThemeAnimation to animate elements coming in and out of your app [Part 1]

In UWP we have this great control called Popup this control can allow you to quickly and cleanly have element pop in and pop out of your view. Also we will look at how to do more of less the same thing with only a Grid.

In this post I wont go into details about the popup control itselft however if you are interested in getting more information msdn is the best place to look.

First we are going to create our simple popup element, make sure that you set the IsOpen property to false (this will hide your popup),  IsLightDismissEnabled to true will mean that if the user clicks outside of the popup it will disappear.

Here is what the XAML can look like:

        <Popup
            x:Name="PopupFromBottom"
            Grid.Row="0"
            Width="459"
            Height="90"
            Margin="20,0,20,20"
            VerticalAlignment="Bottom"
            IsLightDismissEnabled="True"
            IsOpen="False"/>

To add transition to when the popup will be shown we will add ChildTransitions:

 <Popup.ChildTransitions>
      <TransitionCollection>
           <PopupThemeTransition FromVerticalOffset="100" />
      </TransitionCollection>
 </Popup.ChildTransitions>

This will add an animation, the popup will come from the bottom with a translation of 100px. If you have wanted a Horizontal translation then you could have used FromHorizontalOffset.

For this popup to be displayed all you need is to set the IsOpen to true :

 PopupFromBottom.IsOpen = true;

Now if we wanted the popup to hide it self after 3 seconds all we would need to add is a wait of 3 seconds and again change the IsOpen to false as follows:

 //show
PopupFromBottom.IsOpen = true;

//wait 3 sec
await Task.Delay(TimeSpan.FromSeconds(3));

//close 
PopupFromBottom.IsOpen = false;

In the previous example we used the default popup control which allowed us to use the lightdismiss and directly attach the PopupThemeTransition animations to the popup.  However we always have custome cases where we need custome code and sometimes the default controls just wont cut it.  NOw we are going to see how to use a Grid as a popup.

first we are going to create our Grid as follows:

<Grid
            x:Name="GridPopup"
            Grid.Row="0"
            Width="459"
            Height="90"
            Margin="20,0,20,20"
            VerticalAlignment="Bottom"
            Background="LightGray"
            Opacity="0" />

Next we are going to add storyboards and use PopInThemeAnimation and PopOutThemeAnimation
to pop in and pop out our grid.  Here is the Storyboard XAML code:

 <Storyboard x:Name="PopOutStoryboard">
            <PopOutThemeAnimation SpeedRatio="2" TargetName="GridPopup" />
 </Storyboard>
 <Storyboard x:Name="PopInStoryboard">
            <PopInThemeAnimation
                FromVerticalOffset="150"
                SpeedRatio="0.3"
                TargetName="GridPopup" />
            <DoubleAnimation
                d:IsOptimized="True"
                Storyboard.TargetName="GridPopup"
                Storyboard.TargetProperty="Opacity"
                To="1"
                Duration="0" />
 </Storyboard>

and now to easily call this code all we need to do:

        private void OpenStoryboard_Click(object sender, RoutedEventArgs e)
        {
            PopInStoryboard.Begin();
        }

        private void CloseStoryboard_Click(object sender, RoutedEventArgs e)
        {
            PopOutStoryboard.Begin();
        }

And there you are! You can find the full code here.

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.





mercredi 21 février 2018

WebView using InvokeScriptAsync and calling a method and passing the parameters using JSON.parse [WebView,C#]

Calling a method on your WebView is simple just call the WebView.InvokeScriptAsync( method , param) (more information on MSDN) and you are good to go.  However imagine if you need to send json in the param, how do you send it?


This is the issue that we are going to have a quick look in the post, here is an example in of a javascript call that needs to be called with JSON data.  Looking at the MSDN documentation  , we can see that we need to surrond ou json string with JSON.parse({0}), in the params value in the InvokeScriptAsync.

Here is an example of the method name 'eval' and the javascript parameter that I would like to send to the UWP WebView:

 loadUrl javascript:object.load('xsazphf',
JSON.parse('{  
   "props":{  
      "param":{  
         "action":{  
            "gesture":"click",
            "id":"63825b13-dff5-4f45-81e9-a9dbe2aca39b"
         },
         "screen":{ 
            "id":"662dfa95-6ad2-43c1-b403-4e9a4118be4c"
         },
         "section":{  
            "id":"2bcd2be7-9c10-49e9-b71e-b77dee17c7fb",
            "index":1
         }
      }
   }
}'))

All in all,the json string that we want to pass to our javascript object needs to be wrapped in JSON.parse('{0}')).  This will allow the javascript to understand that the data that we are passing is JSON.

When we are building our string to call with the eval function in javasciprt  we will need to have some like this : myJsObject.MyMethode(Paramater1, ,JSON.parse("MyJSONStringFormat")) , when using StringBuilder we just need follow the pattern above.

Here is the code that will help you pass the correct string to your webview:

        public async void CallPlayerMethod(string method, string param, string json)
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("myJSObject.");
            builder.Append(method);
            builder.Append(string.Format("('{0}'", param)) ;
            builder.Append(string.Format(",JSON.parse('{0}'))", json));  
// old
            //builder.Append('(');
            //builder.Append("'" + param + "'");
            //builder.Append(",JSON.parse('" + json + "')");
            //builder.Append(')');
//call webview CallEvalWebviewMethod(builder.ToString()); } private async void CallEvalWebviewMethod(string callMethod) {
            List<string> callingJsMethod = new List<string>();
            callingJsMethod.Add(callMethod);

            try
            {
                await MyWebView?.InvokeScriptAsync("eval", callingJsMethod);
            }
            catch (Exception e)
            {
                //silent fail
            }
        }

You can find my sample here
Happy coding





lundi 19 février 2018

Building a Placeholder Loading UI like Facebook using only XAML for your UWP application [XAML,C#,Composition]

Building beautiful native UI is very important, and keeping your users informed of what is going in the app is even more important.

We are going to look at creating a placeholder loading control that looks like what facebook is using in its application.

Here is a static version of the loading control:


Here is what we could do if we add some animation using composition.

It is super important to remember that the loading animation should not hide or distract the user from using y our application, the loading animation should just be there to help your user understand that they must wait a bit for the loading to end.

For this example we will be build 2 different UI loaders to show that data is being loaded, here is what the placeholders will have to show that they are loading:



Here is an example of what we want to do:


You have two options either we have a control with a shimmer over to simulate that something is loading or we just have a static rectangle (or grid) with a background color.   If you only want to use the backgroudn color you can look at the RectangleNoShimmerControl.xaml file, if you do want the shimmer keep on reading =).

First we are going to create a RectangleControl UserControl that will hold a ContentControl and a StackPanel with a FontIcon.

The ContentControl with its LinearGradientBrush is what will allow you have the gradient effect when we move the element, in the samlpe you can change: 
<GradientStop Offset="0.5" Color="#dcdcdc" />
by
<GradientStop Offset="0.5" Color="Red" />

which will give you this for example:

Next we will need to add the animation on the content control, we need something to move on the X axis, so using a DoubleAnimationUsingKeyFrames  and targeting  CompositeTransform.TranslateX  we will be able to do this.

<Storyboard>
    <DoubleAnimationUsingKeyFrames
            EnableDependentAnimation="True"
            RepeatBehavior="Forever"
Storyboard.TargetName="contentControl" Storyboard.TargetProperty="(Control.Foreground).(Brush.RelativeTransform).(CompositeTransform.TranslateX)"> <EasingDoubleKeyFrame KeyTime="0" Value="-1" /> <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1" /> </DoubleAnimationUsingKeyFrames> </Storyboard>

We are going to nest our Storyboard inside a <Grid.Triggers> =>        <EventTrigger RoutedEvent="Grid.Loaded"> => <EventTrigger.Actions> which will allow us to start the animation when the Grid is loaded.

Here is the full XAML code for the page:

    <UserControl.Resources>         
        <Color x:Key="EmptyStateLightgray">#ebebeb</Color>
        <Color x:Key="EmptyStatebackground">#f7f7f7</Color>
        <SolidColorBrush x:Key="EmptyStateColorBrush" Color="{StaticResource EmptyStateLightgray}" />
    </UserControl.Resources>

     <Grid>
        <Grid.Triggers>
            <EventTrigger RoutedEvent="Grid.Loaded">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimationUsingKeyFrames
                                EnableDependentAnimation="True"
                                RepeatBehavior="Forever"
                                Storyboard.TargetName="contentControl"
                                Storyboard.TargetProperty="(Control.Foreground).(Brush.RelativeTransform).(CompositeTransform.TranslateX)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="-1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </Grid.Triggers>
<ContentControl x:Name="contentControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch"> <ContentControl.Foreground> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.RelativeTransform> <CompositeTransform CenterX="0.5" CenterY="0.5" Rotation="95" /> </LinearGradientBrush.RelativeTransform> <GradientStop Offset="0" Color="{StaticResource EmptyStateLightgray}" /> <GradientStop Offset="0.5" Color="#dcdcdc" /> <GradientStop Offset="1" Color="{StaticResource EmptyStateLightgray}" /> </LinearGradientBrush> </ContentControl.Foreground> <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource EmptyStateColorBrush}"> <FontIcon FontSize="800" Glyph="&#xE009;" /> </StackPanel> </ContentControl> </Grid>


Now that we have our Facebook like shimmer animation we will create our loading states using that UserControl. Now we are going to create another UserControl to resemble something like this:
To create this in XAML I will use a Grid which will use multiple RowDefinitions , which gives us this:


<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="120" />
            <RowDefinition Height="15" />
            <RowDefinition Height="20" />
            <RowDefinition Height="15" />
        </Grid.RowDefinitions>

        <local:RectangleControl
            Grid.Row="0"
            Width="290"
            Margin="0"
            />
        <local:RectangleControl
            Grid.Row="1"
            Width="60"
            Margin="0,5,0,0"
            />
        <local:RectangleControl
            Grid.Row="2"
            Width="210"
            Margin="0,9,0,0"
            />
        <local:RectangleControl
            Grid.Row="3"
            Width="170"
            Margin="0,5,0,0"
            />
    </Grid>

You can find my code sample here.
Happy coding