Browsed by
Category: Uncategorized

Be careful with Path.GetExtension() and Path.GetFileName()

Be careful with Path.GetExtension() and Path.GetFileName()

Path.GetExtension as well as Path.GetFileName are very useful, but you will have some issues if you don’t read the document.

One important thing to know about them is that they are very restricted and related to NTFS file system. It’s safe to use with NTFS/FAT32 paths and not with URI, HFS+ (OsX) and almost all LINUX paths.

Capture

Invalid characters are: quote (“), less than (<), greater than (>), pipe (|), backspace (\b), null (\0), tab (\t). So if your string contains one of these characters (including the file path!!), Path.GetExtension or Path.GetFileName will return an exception. This restriction doesn’t make a lot of sense in my opinion, why verify if the file path contains an invalid character if the only thing we want is the file extension?

Some example of paths not compatible with Path.GetExtension:

  • test/Document <1>.txt
  • work|2010/meeting.docx
  • http://www.bing.com/test.txt
  • images/family <3/WP2016_01.png

Capture
So, if your app communicates with a Linux server for example, or if you want to retrieve the file extension of an URI or an other file system, you will have some issues with them.

In order to solve this issue, I’ve written a helper, compatible with URI as well as other filesystems, implementing Path.GetExtension, Path.GetFileName but also RemoveInvalidChars():


using System;
using System.IO;
using System.Linq;
using Windows.Networking.Sockets;

namespace Huyn
{
    public static class PathHelper
    {

        #region GetExtension

        static readonly char[] _filePathSeparators = { '/', '\\', ':' };
        static readonly char[] _uriSeparators = { '/' };

        /*
       * Equivalent of Path.GetExtension but doesn't throw an exception when the string contains an invalid character (" < > | etc...)
       */
        public static string GetExtension(this string path)
        {
            return GetExtensionInternal(path, _filePathSeparators);
        }

        public static string GetExtension(this Uri uri)
        {
            return GetExtensionInternal(uri.LocalPath, _uriSeparators);
        }

        public static string GetExtension(this string path, char[] separators)
        {
            if (separators != null && separators.Contains('.'))
            {
                throw new ArgumentException("separators can't contain '.'");
            }

            return GetExtensionInternal(path, separators);
        }


        private static string GetExtensionInternal(this string path, char[] separators)
        {
            if (path == null)
                return null;

            var length = path.Length;
            for (var i = length - 1; i >= 0; --i)
            {
                var ch = path[i];
                if (ch == '.')
                {
                    return i != length - 1 ? path.Substring(i, length - i) : string.Empty;
                }
                else if (separators != null && separators.Contains(ch))
                {
                    break;
                }
            }
            return string.Empty;
        }

        #endregion

        #region GetFileName

        public static string GetFileName(this string path)
        {
            return GetFileNameInternal(path, _filePathSeparators);
        }

        public static string GetFileName(this Uri uri)
        {
            return GetFileNameInternal(uri.LocalPath, _uriSeparators);
        }

        public static string GetFileName(this string path, char[] separators)
        {
            if (separators != null && separators.Contains('.'))
            {
                throw new ArgumentException("separators can't contain '.'");
            }

            return GetFileNameInternal(path, separators);
        }


        private static string GetFileNameInternal(string path, char[] separators)
        {
            if (path != null)
            {

                var length = path.Length;
                for (int i = length - 1; i >= 0; --i)
                {
                    var ch = path[i];
                    if (separators.Contains(ch))
                        return path.Substring(i + 1, length - i - 1);
                }
            }
            return path;
        }

        #endregion


        public static string RemoveInvalidChars(string filename, char? replacedLetter = null)
        {
            if (filename == null)
                return null;

            var invalidChars = Path.GetInvalidFileNameChars();

            if (replacedLetter == null)
            {
                return new string(filename
                    .Where(x => !invalidChars.Contains(x))
                    .ToArray());
            }
            else
            {
                return new string(filename
                    .Select(x => invalidChars.Contains(x) ? replacedLetter.Value : x)
                    .ToArray());
            }
        }
    }
}


Don’t hesitate to send me feedback on twitter: @rudyhuyn

More analytic data for your UWP apps

More analytic data for your UWP apps

If you go on the developer.microsoft.com/dashboard, click on your app then on analytics, you’re able to see some stats about your downloads:

capture

 

You can see stats for each OS, but what about if you want to see stats about type of devices: mobile, desktop, xbox, hololens?

Stats by device type

To have this kind of information, you can go on:

https://developer.microsoft.com/dashboard/analytics/reports/overview

then click on change filter

capture

Select your application and click on device type:

capture

You will be able to filter your stats by type of devices, including tablets!

Be careful, there is a little bug right now, Xbox acquisitions are listed in “Other” instead of “Console” for acquisitions.

Once done, for each type of devices, you will have access to some very useful information:

  • Acquisitions graph (30 days)
  • Total lifetime app acquisitions
  • Failure hits (72 hours)
  • Total user sessions (30 days)
  • Ratings (30 days)
  • Top add-ons (30 days)

OS + device type?

There is no easy way to have these stats, but you can do it yourself, go on:

 

https://developer.microsoft.com/dashboard/analytics/reports/export?reportFilter=acq&productId=

and download the .tsv file (csv file with tabs) and open it with Excel, you will be able to create your own charts depends of these criteria (if your application is popular, it can take some minutes to create them)

 

Merry Christmas!

AdaptivePanel: a smarter way to restrict the size of your controls

AdaptivePanel: a smarter way to restrict the size of your controls

One of the big challenge when you write an UWP application is to adapt your UI to 5″ screens (phones) as well as to 84″ screens (Surface Hub).

Let be honest: it’s not the easiest task, especially because the framework misses some controls made for your adaptive UI, for example MinWidth and MaxWidth (+ MinHeight/MaxHeight or course).

In order to simplify this article, we will only focus on the horizontal plan (MinWidth, Width, MaxWidth and HorizontalAlignment), of course, all the content can be also apply to the vertical plan (Height, MinHeight, MaxHeight and VerticalAlignment).

Here is some scenarios almost impossible to solve with the standard controls, we will then try to fix them with a new control:

Problem 1: Use the maximum of space, with alignment to the left/right and with a maxWidth

If you write something like:


<Grid Background="White" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" MaxWidth="600" MaxHeight="400">
</Grid>

Capture

The Grid will be aligned to the center, using the maximum of the space but limited to MaxWidth, so whatever the content of the Grid, the width will be only limited by the size of the parent items (the window for example).

Because we use MaxWidth instead of Width, even if the window is resized, the content of the Grid continue to fit well:

Capture

But what if we want to align the control to the left or right?


<Grid Background="White" HorizontalAlignment="Right"
VerticalAlignment="Stretch" MaxWidth="600" MaxHeight="400">
</Grid>

 

Here is the result:

Capture

It’s not really the behavior we expected… This time the width of the control depends first of the content (and not the MaxWidth). The only way to force the width of the Grid is to set Width, but our UI will break if we resize the window:

Capture2

Problem to solve: Have a way to say Stretch the content not only aligned to the center but also to the left and right.

Problem 2: Use MinWidth and window resizing:

Use MinWidth to force a control to not be too small whatever the content is a very common trick, especially with dynamic contents (GridView, etc…), but a drawback is that you can break your adaptive UI. Let cut a long story short with an example:


<Grid Background="White" HorizontalAlignment="Right"
MinWidth="600">
</Grid>

Whatever the content, the width of our Grid will always be 600px or higher. But what happens if we decrease the size of the window to 500px or if we display this UI on a mobile phone?

CaptureConclusion: every times you use MinWidth, there is a risk to break your adaptive-design.

Problem to solve: improve MinWidth to take into account the size available and not force the width when there’s no enough space available.

Solution: AdaptivePanel, a smart control for adaptive-design

In order to fix these 2 annoying issues, I created a new control named “AdaptivePanel”, it’s a standard Panel but with 2 extra properties: VerticalBehavior and HorizontalBehavior.

Using these two properties, you can override the behavior of MinWidth and MaxWidth to improve the adaptability of your UI.

Here is the 3 possible values :

  • Standard: the control will behave similarly to all other controls
  • StretchToMaxSizeIfPossible: the control will try to use the maximum space available taking into account the size of the parent and the MaxWidth property (or MaxHeight)
  • MinSizeOnlyIfAvailable: the control will use the minimum space available taking into account the size of the parent and the MinWidth property (or MinHeight)

 

So let try to solve our 2 problems:

Problem 1

(Use the maximum of space, with alignment to the left/right and with a maxWidth).

You can easily solve this issue using the behavior StretchToMaxSizeIfPossible:


<huyn:AdaptivePanel Background="White" HorizontalAlignment="Right"
MaxWidth="600" HorizontalBehavior="StretchToMaxSizeIfPossible">
</huyn:AdaptivePanel>

Your control will use the maximum of the size available and take into account the size of the parent.

Problem 2

(Use MinWidth and window resizing)

You can solve this issue using MinSizeOnlyIfAvailable with the following code:


<huyn:AdaptivePanel Background="White" HorizontalAlignment="Right"
MinWidth="600" HorizontalBehavior="MinSizeOnlyIfAvailable">
</huyn:AdaptivePanel>

Whatever the size of the window or the parent item, your control will always be fully visible.

Source code of AdaptivePanel

https://github.com/rudyhuyn/AdaptivePanel-UWP/blob/master/AdaptivePanel/Controls/AdaptivePanel.cs

Sample project

https://github.com/rudyhuyn/AdaptivePanel-UWP

 

Delay an action: Debounce and throttle

Delay an action: Debounce and throttle

It can be sometimes interesting to delay an action, for many reasons, but performance is the principal one. Let take an example: you have a searchbox and you need to make a request each time the content changes, if the user types very quickly, it will be totally useless to make a request at every KeyUp events.

Solution: Debouncing

What is deboucing?

The origin is electronic. Mechanical switches don’t make or break a connection cleanly due to microscopic conditions on the contact surface. This is referred to as “Switch Bounce” and can cause problems in digital circuits.

switch_debounce_04_lrg

In our case, we will do exactly the same thing but with actions: we will wait until the user stops typing during an interval.

Here is a simple class to do it:

using System;
using Windows.UI.Xaml;

namespace Huyn.Utils
{
public class Delayer
{
private DispatcherTimer _timer;
public Delayer(TimeSpan timeSpan)
{
  _timer = new DispatcherTimer() { Interval = timeSpan };
  _timer.Tick += Timer_Tick;
}

public event RoutedEventHandler Action;

private void Timer_Tick(object sender, object e)
{
  _timer.Stop();
  if (Action != null)
    Action(this, new RoutedEventArgs());
}

public void ResetAndTick()
{
  _timer.Stop();
  _timer.Start();
}

public void Stop()
{
  _timer.Stop();
}
}
}

And usage:

_searchWithDelay = new Delayer(TimeSpan.FromSeconds(2));
_searchWithDelay.Action += (sender, e) =>
{
  Search();
};

private void Page_KeyUp(CoreWindow coreWindow, KeyEventArgs e)
{
  searchWithDelay.ResetAndTick();
}

 

What about Throttle?

Throttle versus Debounce

Contrary to Debounce, Throttle will not wait the end of an action, but it will limit the number of call of this action. One example: with a delay of 4 seconds, if an user types a key every second, Throttle will call the action every 2 seconds whereas Debounce will wait that the user stops to type during 4 seconds.

How to implement it?

It’s very easy to implement, we only need to add the method:


public void ResetAndTick()
{
  _timer.Start();
}

and use it:

private void Page_KeyUp(CoreWindow coreWindow, KeyEventArgs e)
{
  searchWithDelay.ResetAndTick();
}

Conclusion

This code is very simple, but don’t hesitate to use it to improve performance of your app!

 

Optimize your UWP app without laziness

Optimize your UWP app without laziness

UWP apps are powerful, but due to adaptive design and the many form factors to support, your pages can be a lot more complex than with Windows phone for instance.

Let’s take an example, I recently release 6tin as a UWP app, it supports 5 different UI to adapt the user experience to many factors: device family, orientation, theme, user interaction (mouse/touch/gamepad) and of course screen size and density.

Here is an example of 2 different states:

00

In blue, controls specific to an UI.

01

Except the central part, all other controls aren’t used other UI, almost 50% of the visible view and 80% of the complexity is not used by some visual states

So imagine if I load ALL these controls on my phone, including controls specific to big screens, the app will takes a lot of time and RAM to load for nothing…

How I can fix it?

Visibility is not a solution

With XAML, we have 2 different states for visibility: Visible and Collapsed, but whatever the value, the item will be loaded, added to the visual tree and will use RAM. So how can I prevent for example the ad for big screens to load on phone?

2 solutions:

  • create these controls on code-behind
  • have a way to prevent the system to load some parts of our UI

The first solution is interesting, but not a good solution in production, your code will be hard to read.

So can I have a way to prevent the loading?

Yes, thanks to a new property brought by Windows 10:

x:DeferLoadStrategy=”Lazy

This property will prevent the system to parse a part of the xaml and load it if the visibility property equals Collapsed, for example:

<MediaElement x:Name=”Player” x:DeferLoadStrategy=”Lazy” Visibility=”Collapsed” />

The system will not load the MediaElement except if you change the visibility property, to summarize, the control will not exist, this.Player will be equal to null, no RAM will be used, etc…

Moreover, if you use DeferLoadStrategy on a Panel, the Panel as well as all its descendants will not be loaded/parsed/etc..

This is what I did on 6tin, the left panel is “Lazy” and Collapsed, so if I display this page on a phone, no CPU/GPU will be used to load/render all these useless controls, my page will use less memory and will be rendered faster.

So, how can I display them if they don’t exist?

doctor-who-spoilers-silence-431x300

I said the control doesn’t exist, but there is 2 ways to activate it.

Imagine we have a Grid named DesktopMenu:

<Grid x:Name=”DesktopMenu” x:DeferLoadStrategy=”Lazy” Visibility=”Collapsed”>

…. a lot of descendants

</Grid>

On code-behind this.DesktopMenu will be equals to null, so impossible to write: this.DesktopMenu.Visibility=Visibility.Visible

Visual States

The easiest solution is to use visual states, even if this.DesktopMenu == null, setters and storyboards will have access to the item.


<VisualState x:Name="WideState">
<VisualState.Setters>
<Setter Target="DesktopMenu.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>

Important fact: once the item visible, the property this.DesktopMenu will not be null anymore!

FindName

An other solution is to use FindName:


var myGrid=(Grid)myPage.FindName("DesktopMenu")

myGrid.Visibility=Visibility.Visible;

In fact, it’s exactly what the visual state do.

Best practices

Code-behind

Because of the Lazy system, an item can be null, so never write something like:


DesktopPanel.Width=200;

If the visibility has never be changed, this.DesktopPanel will be null and your application will crash, the best practice is to write:


(DesktopPanel??FindName("DesktopPanel")).Width=200;

Be careful will this usage, as soon as you use FindName, the object will be loaded as well as all its descendants. So for example, never writes something like:


if(DesktopPanel??FindName("DesktopPanel")).Visibility==Visibility.Collapsed)

If DesktopPanel equals null, FindName will be called and DesktopPanel (and its descendants) will be loaded for nothing. Instead write:


if(DesktopPanel==null || DesktopPanel.Visibility==Visibility.Collapsed)

Optimize all screens

Take advantage of the Lazy system and use it on ALL controls not used by ALL visual states. If a control/panel is collapsed on 1 or many visual states, set  x:DeferLoadStrategy=”Lazy” Visibility=”Collapsed” and change its visibility on visual states.

Never write something like:

<Grid>
<Grid x:Name="SmallMenu">
.... UI specific to small screens
</Grid>
<Grid x:Name="DesktopMenu" x:DeferLoadStrategy="Lazy" Visibility="Collapsed">
.... UI specific to wide screens
</Grid>
</Grid>

 

<VisualState x:Name="WideState">
<VisualState.Setters>
<Setter Target="DesktopMenu.Visibility" Value="Visible" />
<Setter Target="SmallMenu.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="NormalState"/>

This UI is optimized for small screens but not for wide screens, if you start your app on a wide screen, the SmallMenu will be loaded then collapsed, this step is totally useless and take ressources.

The default state must not be related to a specific UI, it must be the greatest common divisor of all your UI, only items used by ALL states must be visible, others must be collapsed and lazy.

If we change our previous example, DesktopMenu and SmallMenu must be collapsed and Lazy (cause not used on all our UI) and we will display them with visual states

<Grid>
<Grid x:Name="SmallMenu" x:DeferLoadStrategy="Lazy" Visibility="Collapsed">
.... UI specific to small screens
</Grid>
<Grid x:Name="DesktopMenu" x:DeferLoadStrategy="Lazy" Visibility="Collapsed">
.... UI specific to wide screens
</Grid>
</Grid>

 

<VisualState x:Name="WideState">
<VisualState.Setters>
<Setter Target="DesktopMenu.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="NormalState">
<VisualState.Setters>
<Setter Target="SmallMenu.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>

Conclusion

Don’t be lazy with lazy

Gérer la synchronisation de ses agents avec l’application

Gérer la synchronisation de ses agents avec l’application

Il existe de nombreux scénarios où un développeur ne souhaite pas lancer un background agent (périodique par exemple) lorsque son application est lancée, ceci pour différentes raisons:

  • l’application en foreground réalise un traitement équivalent à celui dans le background agent, voire + complet
  • résoudre des problématiques d’accès de fichiers
  • obiwan kenobi

C’est une question que je vois régulièrement sur les forums, malheureusement, aucune solution n’est indiquée si ce n’est quelques idées et propositions, mais rien de concret.

Donc corrigeons cela pour la nouvelle année !

La solution idéale ?

C’est un peu un rêve… étant donné que winrt gère les conditions sur les background agents:


builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));

Il ferait sens d’avoir un équivalent pour qu’un agent ne se lance que si l’application n’est pas déjà lancée, par exemple:


builder.AddCondition(new SystemCondition(SystemConditionType.ApplicationNotRunning));

Malheureusement ce genre de condition n’est toujours pas disponible ce qui est plutôt regrettable… Nous allons donc devoir bricoler une solution maison car soyons honnête, la solution se rapproche vraiment du bricolage, mais faute de mieux…

Le secret: les mutex nommés

Pour résoudre notre problématique, nous allons utiliser ce qu’on appelle les Mutex nommés, ces mutex (ou Semaphore(1) pour ceux qui sont plus familier avec cette notion) ont la particularité de porter un nom/identifiant et d’être multi-process.

Attention toutefois, les applications “Metro” étant sandboxées, ce mutex se limite au scope de l’application, aucun risque donc d’avoir des soucis de synchronisation si 2 applications utilisent le même nom/id.

mutex

Implémentation

Nous allons donc commencer par créer deux variables, un mutex et un booléen:


private static Mutex _agentMutex=new Mutex(false,"SyncAgentsMutex");
private static bool _applicationRunning;

Ce booléen est un peu redondant avec le Mutex, mais cela est fait exprès, il va permettre de stocker l’état du Mutex, la classe .Net ne nous permettant pas d’avoir accès à cette information directement.

Nous allons maintenant gérer la prise du Mutex afin d’indiquer que l’application est en cours d’exécution


private static void ApplicationIsLaunched()
{
if (_applicationRunning)
return;
lock (_agentMutex)
{
if (_applicationRunning)
return;
_agentMutex.WaitOne();
_applicationRunning = true;
}
}

l’implémentation est classique, un lock pour être sûr de ne pas appeler deux fois WaitOnce (ce qui pourrait bloquer l’application au second appel) et une double vérification du booléen à l’extérieur et à l’intérieur du lock, schéma plutôt classique.

Occupons nous maintenant de la fonction de libération du Mutex. Contrairement aux cas précédent, j’ai volontairement choisi de ne pas faire une vérification avant le lock, je veux être sûr et certain qu’un ApplicationIsLaunched n’est pas en cours, ce qui pourrait nous causer quelques problèmes de synchronisation.


private static void ApplicationIsSuspended()
{
lock (_agentMutex)
{
if (!_applicationRunning)
return;
_agentMutex.ReleaseMutex();
_applicationRunning = false;
}
}

Enfin, nous allons créer une fonction pour vérifier l’état de l’application


public static bool IsApplicationLaunched()
{
lock (_agentMutex)
{
if (_agentMutex.WaitOne(1))
{
_agentMutex.ReleaseMutex();
return false;
}
return true;
}
}

WaitOne(1) va nous permettre de tester l’état du Mutex, pour résumer, on va essayer d’acquérir le Mutex avec un timeout d’1 milliseconde (soit presque rien), si on réussi à le récupérer, cela signifie que l’application n’est pas en cours d’exécution, on demande alors à relâcher le mutex aussitôt.

Précision important, je n’ai pas utiliser le booléen _applicationRunning. En effet, mon application et mes agents sont dans des processus différent, autant le Mutex est synchronisé (car nommé), autant le booléen est propre à chaque processus. Comme évoqué précédemment, le booléen est là pour sécuriser le Mutex côté application, et uniquement de ce côté.

Dernière étape, deux fonctions pour enregistrer l’application et gérer automatiquement l’état de celle-ci

 

        public static void Init(Application app)
        {
            app.Resuming += App_Resuming;
            app.Suspending += App_Suspending;
            ApplicationIsLaunched();
        }

        private static void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
        {
            ApplicationIsSuspended();
        }

        private static void App_Resuming(object sender, object e)
        {
            ApplicationIsLaunched();
        }

Comment l’utiliser ?

Côté application

Dans le constructeur de votre application (App.xaml.cs), appelez


AgentSync.Init(this);

le plus tôt possible (première ligne si possible) et c’est tout.

Côté agent

Dès la première ligne de votre agent, dans la méthode Run(…), appelez la méthode suivante :


if(AgentSync.IsApplicationLaunched())
return;

Cela évitera tout traitement si votre application est en cours d’utilisation.

Code complet

using System.Threading;
using Windows.UI.Xaml;

namespace Sixtin.Services.Utils
{
    public class AgentSync
    {
        private static Mutex _agentMutex=new Mutex(false,"SyncAgentsMutex");
        private static bool _applicationRunning;

        public static void Init(Application app)
        {
            app.Resuming += App_Resuming;
            app.Suspending += App_Suspending;
            ApplicationIsLaunched();
        }

        private static void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
        {
            ApplicationIsSuspended();
        }

        private static void App_Resuming(object sender, object e)
        {
            ApplicationIsLaunched();
        }

            private static void ApplicationIsLaunched()
            {
                if (_applicationRunning)
                    return;
                lock (_agentMutex)
                {
                    if (_applicationRunning)
                        return;
                    _agentMutex.WaitOne();
                    _applicationRunning = true;
                }
            }

        private static void ApplicationIsSuspended()
        {
            lock (_agentMutex)
            {
                if (!_applicationRunning)
                    return;
                _agentMutex.ReleaseMutex();
                _applicationRunning = false;
            }
        }

        public static bool IsApplicationLaunched()
        {
            lock (_agentMutex)
            {
                if (_agentMutex.WaitOne(1))
                {
                    _agentMutex.ReleaseMutex();
                    return false;
                }
                return true;
            }
        }
    }
}

Display an AppBarButton on the left inside a CommandBar

Display an AppBarButton on the left inside a CommandBar

With Windows 8.1, it was easy to add a button to the left side or the right side of a CommandBar using PrimaryCommands and SecondaryCommands.

But Windows 10 uses a different template for CommandBar, similar to the one used by Windows Phone 8.1:

  • primary commands as a button (aligned to the right)
  • secondary commands as a menuitem

Sans titre

So, here is the question: How can I align a button to the left?

Have a button to the left can be very interesting for many scenarios, one of them is the hamburger menu, an other one is to highlight a specific feature, like I did on 6tag.

Sans titre

So, how can I do that?

In order to do that, you need to use the property Content of the CommandBar, this property will allow you to add one FrameworkElement to the left, like for example… an AppBarItem!

Note: contrary to a lot of other controls, the default property for children nodes is not Content but PrimaryCommand, this is IMO the reason why nobody finds it.

<Page.BottomAppBar>
<CommandBar>
<CommandBar.Content>
<AppBarButton Icon="Home"/>
</CommandBar.Content>
<AppBarButton Icon="Delete"/>
<AppBarButton Icon="Calendar"/>
</CommandBar>
</Page.BottomAppBar>

Sans titre

But if I need many buttons?

As I said, Content property needs a FrameworkElement, so you can easily use a StackPanel and include many AppBarItem on it:


<Page.BottomAppBar>
<CommandBar>
<CommandBar.Content>
<StackPanel>
<AppBarButton Icon="Home"/>
<AppBarButton Icon="Like"/>
</StackPanel>
</CommandBar.Content>
<AppBarButton Icon="Delete"/>
<AppBarButton Icon="Calendar"/>
</CommandBar>
</Page.BottomAppBar>

What about the label of the AppBarItem?

Sans titre

A bug is remaining, if you use a label, it will be visible even if the CommandBar is not opened, to fix that, we will bind the property AppBarItem.IsCompact to the CommandBar.IsOpen property. Of course, we need to invert the value of the boolean.


<AppBarItem IsCompact="{Binding IsOpen,ElementName=MyCommandBar,Converter={StaticResource InvertBooleanConverter}}"/>

What about globalization and right-to-left languages?

I’ve tested the same code with arabian language for example, and no problems, the Content is aligned to the right whereas PrimaryCommands are on the left

Sans titre

 

Conclusion

This property was not easy to find, due to a poor MSDN documentation

Capture

but whatever, don’t hesitate to use it in your apps, and for example think about it when you want to use an hamburger menu on your phone app, buttons on the top are not always easy to use with some phones (1520, 625, 950 XL)

Detect if your 8.1 app runs on Windows 10

Detect if your 8.1 app runs on Windows 10

Here is a little helper I use on my apps to detect if the OS is Windows 10 or higher.

For this purpose, I use the property “Status” of the class Package, why? Cause it’s a new property only available on Windows 10. So using reflection, I can test if this property is available or not and therefore, detect if the app runs on Windows 10.

Voici une petite astuce pour détecter si votre application 8.1 tourne sur Windows 10 ou sur une version plus ancienne de l’OS.

Pour cela, j’utilise la propriété “Status” de la classe “Package”, pourquoi ? Car elle a l’avantage d’être une propriété uniquement disponible sous Windows 10, en testant donc sa présence, on peut affirmer sans soucis que l’OS est Windows 10 ou pas.

using System.Reflection;
using Windows.ApplicationModel;

namespace Huyn.Utils
{
 public class Windows10Helper
 {
  private static bool? _isWindows10;
  public static bool IsWindows10()
  {
    if (!_isWindows10.HasValue)
    {
       _isWindows10 = Package.Current.GetType()
                         .GetRuntimeProperty("Status") != null;
    }
    return _isWindows10.Value;
  }
 }
}
How to use the Clipboard on Windows Phone 8.1 apps

How to use the Clipboard on Windows Phone 8.1 apps

Clipboard api are only available for Windows 8.1 and Windows 10, but not for Windows Phone 8.1 apps

Yes and no

Yes, Windows Phone 8.1 apps have no access to this cool feature, but only on Windows Phone 8.1 devices! If your app runs on Windows 10 mobile, you can use reflection to use it! Here is the code to use:

var clipboardType= typeof(DataPackage).GetTypeInfo().Assembly.GetType("Windows.ApplicationModel.DataTransfer.Clipboard");
if (clipboardType != null)
{
    var dataPackage = new DataPackage();
    dataPackage.SetText("Hello You!");
    clipboardType.GetRuntimeMethod("SetContent",new[] {typeof(DataPackage) }).Invoke(null,new object[] { dataPackage });
}

If you have not enough time to update your app to UWP, do not hesitate to use reflections to improve your 8.1 app with some 10 exclusive features!

[TrucASavoir] Lock screen, Background Agents et Windows 10

[TrucASavoir] Lock screen, Background Agents et Windows 10

Avec Windows 8 et Windows 8.1, les développeurs avaient le choix parmi plusieurs Background Agent afin de faire vivre leurs applications même si celles-ci n’étaient pas lancées.

Parmi ces agents, on trouve notamment 4 agents bien spécifiques :

  • Timer
  • Raw Push Notification
  • Location
  • Control Channel

Ces 4 agents avaient une contrainte très forte : pour pouvoir s’exécuter, votre application devait être épinglée à l’écran de verrouillage…

Soyons honnête, ceci est difficilement compréhensible, quel est le rapport entre le lock screen et un agent Raw Push Notification ? Rappelons que ce dernier est très utile afin de gérer au niveau local vos toasts/badges/tiles. Imaginons que l’on développe l’application Facebook Messenger, à quoi bon envoyer 3 notifications à chaque message reçu afin de mettre à jour la tuile principale, la tuile secondaire du contact et afin d’afficher un toast alors qu’une simple notification Raw suffit ?

RAW jefermar

Les choses changent!

Depuis Windows 10, il n’est plus nécessaire d’avoir son application sur le lock screen afin d’utiliser ces agents, une très bonne nouvelle pour les développeurs ! Les applications UWP n’auront évidemment plus cette contrainte, mais on est en droit de se poser une question : Est ce que ça concerne aussi nos vieilles apps W8(.1) tournant sur W10 ?

Après avoir effectué plusieurs tests : OUI !!!

Je ne peux que vous conseiller alors de cacher l’ensemble des références au lock-screen dans vos UI si le seul but était celui-ci.

Un petit code afin de savoir si votre application tourne sur Windows 10 (c’est sale mais il vaut ce qu’il vaut) :


private bool IsWin10()
{
return Windows.UI.ViewManagement.ApplicationView.GetForCurrentView()
.GetType()
.GetRuntimeProperty("TitleBar") != null;//SALE
}

Visual Studio peut vous tromper

Attention toutefois, pour vos apps 8.0/8.1, Visual Studio continuera à vous forcer à gérer le mode lock-screen, ce qui n’est pas totalement faux car c’est vraiment nécessaire pour Windows 8/8.1/RT, mais inutile pour Windows 10.

Sans titre-1