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

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

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

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

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

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

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

 

Utiliser C# 6 avec Visual Studio 2013

C# 6 apporte énormément de nouveautés et il est très difficile de s’en passer une fois essayé. Toutefois ce dernier est restreint à Visual Studio 2015.

Même si Visual Studio 2015 est sorti depuis quelques semaines, vous n’avez peut-être pas les moyens d’acheter la nouvelle version ou comme dans mon cas, certains de vos projets ont des soucis avec la version 2015 (principalement sur les projets Silverlight 8 et 8.1 ainsi que Blend dans mon cas).

Alors comment puis-je utiliser C# 6 avec Visual Studio 2013?

Il y a encore quelques mois, il était possible d’avoir accès à la version preview de C# 6 avec Visual Studio 2013, cet accès est néanmoins clos depuis quelques jours et ne concernait pas la version finale de C# 6. Alors y’a t’il une autre solution ?

Oui ! Le compilateur .Net est maintenant disponible sous la forme de package Nuget et il est possible de demander à Visual Studio d’utiliser ce compilateur en lien et place du compilateur associé à votre version de Visual Studio.

Pour cela, tapez la commande suivante dans la fenêtre “Package Manager Console”

PM > Install-Package Microsoft.Net.Compilers

et faites cela pour chacun de vos projets.

Une fois cette commande exécutée, votre projet sera compilé par nouveau compilateur et vous aurez ainsi à l’ensemble des nouveautés de ce dernier, incluant C# 6 !

Bon code !

 

Why Silverlight 8.1 doesn’t display splashscreens on 1080p devices

It was a mystery for me, some of my 6tag and 6tin users report me a problem with splashscreens never displayed, problem: I broke my Lumia 1520 (RIP) so no way to test it.

So I tried to find a solution: “try to close the app”, “restart your phone”, but their answers were always the same “nothing changes…”. So I went to a Microsoft Store to test it myself and yes, 6tag and 6tin splashscreens were not displayed, but I was not alone, Facebook Messenger, Skype, etc… ALL Silverlight 8.1 apps had this issue…

What about the documentation?

Let check the MSDN documentation:

How to create a splash screen for Windows Phone
https://msdn.microsoft.com/en-us/library/windows/apps/ff769511(v=vs.105).aspx

Nothing on it

Multi-resolution apps for Windows Phone 8
https://msdn.microsoft.com/en-us/library/windows/apps/jj206974(v=vs.105).aspx

“Windows Phone 8 Update 3 will default to using the 720p splash screen file on a 1080p phone”… Cool, but it doesn’t work on 8.1 devices…

I searched during 10 minutes, no documentation, no feedbacks on stackoverflow, MSDN forums, reddit, etc…

So what is the solution?

I tried a lot of solution, add a splashscreen in the Package.appxmanifest, etc… and after a long time, I succeed.

Contrary to 8.0 devices, you need to add a specific file for 1080p splashcreens, this file must be named “SplashScreenImage.screen-1080p.jpg” and must be at the root folder of your project. The image size is 1920×1080 pixels.

Capture

Once done, your splashscreen will be well-displayed on 1080p devices!

Bonus

Sans titre-1

No comment…

ListView: how to prevent visual glitches during scrolling

Prelude

My friend Samuel Blanchard just wrote an article (in french) including some tips about ListView this morning, do not hesitate to read it also: http://blog.naviso.fr/wordpress/?p=2078

To complete this article, I will share one of the control I use to improve the ListView experience.

How to prevent shifts in ListView

If you’re familiar with the ListView control, perhaps you know there is a little problem when you scroll items, a small shift of the control to the right (or left) can happen and is really boring.

So here is a tip to prevent this behavior: If the ItemsPanel’s width is manually set, the glitch disappears!

Here is a very small class to manage it automatically even if the control is resized (switch from portrait to landcape for example):


namespace Huyn.Controls
{
public class PerfectScrollListView : ListView
{

public PerfectScrollListView()
{
this.SizeChanged += PerfectScrollListView_SizeChanged;
}

private void PerfectScrollListView_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{

if(ItemsPanelRoot!=null)
ItemsPanelRoot.Width = e.NewSize.Width;
}
}