Jak vynutit otevření CommandBaru směrem dolů

Ovládací prvek CommandBar  je důležitou komponentou pro design UWP aplikací. Jde o evoluci konceptu   AppBar , který je dostupný již od dob Windows Phone 7, ale v případě UWP má výrazně více funkcí. Stále však chybí možnost určit směr, kterým se command bar bude otevírat.

Problém

Výchozí chování CommandBaru je otevření směrem nahoru vždy, kdy není na samotném horním okraji okna. To je problém, protože toto platí i v případě, že definujeme vlastní title bar aplikace na Desktopu, kdy se   CommandBar  otevře pod tlačítka minimalizace, maximalizace a zavření okna a to rozhodně nevypadá dobře.

Výchozí šablona

Výchozí šablona prvku CommandBar  definuje kolekce VisualState  a  VisualStateTransition , které určují, jak bude prvek vypadat. Můžeme si všimnout že je vždy separátně definován stav pro otevření směrem dolů a nahoru.

Uvnitř těchto stavů můžeme vidět, že systém jen používá různé hodnoty pro některé z vlastností jako například CommandBarTemplateSettings.ContentHeight  resp. CommandBarTemplateSettings.NegativeOverflowContentHeight  pro vlastnost  Y  u transformace OverflowContentRootTransform .

Řešení

Nemůžeme snadno modifikovat vnitřní logiku ovladacího prvku, ale můžeme zajistit, že bude vypadat pro stav otevření nahoru identicky jako pro otevření dolů. Toho můžeme dosáhnout čistě překopírováním Storyboardů  ze stavů a přechodů se obsahujících ...OpenDown do jim odpovídajících  ...OpenUp  protějšků. Bohužel manuální překopírování je jediná možnost. Bylo by sice také možné jednotlivé Storyboardy  převést na samostatné resource, ale bohužel je pro VisualState  a VisualStateTransition nelze referencovat pomocí syntaxe {StaticResource}.

Pro získání kompletní kopie výchozího stylu olvadacího prvku můžeme buď použít Visual Studio XAML Designer (klepněte na prvek pravým tlačítkem myši, vyberte Edit Template a Edit a Copy…), nebo ji lze vyhledat na v souboru   C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP{version}\Generic\generic.xaml

Abych vám ušetřil klávesy  Ctrl , C  a V , připravil jsem kompletní upravený styl, který můžete přímo použít ve vaší aplikaci. Styl je ke stažení na mém GitHubu spolu s ukázkovým projektem k tomuto článku. Upozorňuji, že můj definovaný styl je pro verzi Anniversary Update. Pro budoucí verze doporučuji změny provést manuálně na aktuální šabloně.

Shrnutí

CommandBar  je skvělý ovladací prvek, který většinou funguje přesně tak, jak potřebujeme. Když však narazíme na problém, můžeme jeho šablonu poměrně snadno modifikovat.

Forcing the CommandBar to open down

The CommandBar  control is a vital component of UWP app design. It is an evolution of the AppBar  concept, which was available ever since Windows Phone 7, but with UWP is much more feature complete. One thing that is still missing however is the option to choose the direction in which the command bar opens.

Problem

The default behavior of the CommandBar  is to open in the up direction whenever the control is not at the very top of the window. This is an issue, because this holds true even when we define a custom title bar on Desktop, in which case the CommandBar  opens below the window’s minimize, maximize and close buttons which doesn’t look good at all.

The default template

The default template of the CommandBar  control defines the states of the control as a collection of VisualStates  and VisualStateTransitions . It turns out that there is always a separate visual state for down and up direction.

Inside these states you can see that the system just uses different values for some of the properties like CommandBarTemplateSettings.ContentHeight  vs CommandBarTemplateSettings.NegativeOverflowContentHeight  for the Y  property of OverflowContentRootTransform .

Solution

We cannot easily change the inner logic of the control itself, but we can make the control in up-open state look identical as it does for down-open state. This can be achieved purely by copy-pasting the Storyboards  from ...OpenDown visual states and visual state transitions to the respective ...OpenUp  counterparts. Unfortunately the manual copy-pasting is the only option, because extracting the Storyboards  into separate resources and referencing them with {StaticResource} isn’t supported.

To get a full copy of the default control style you can use either the XAML Designer (right-click the control, select Edit Template and Edit a Copy…), or search for it in C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP{version}\Generic\generic.xaml

To spare your Ctrl , C  and V  keys, I have prepared the full modified style which you can take and use in your app. You can get this style here on my GitHub along with the full sample project for this article. Beware that this style targets the Anniversary Update. I recommend doing the changes manually if you target a different version.

Summary

The CommandBar  is a great control that works as we expect most of the time. When we hit an issue however, its template is quite easy to modify.

Zarovnání obsahu UWP CommandBaru po Anniversary Update

Ovladací prvek CommandBar získal s Anniversary Updatem novou funkci dynamic overflow, která automaticky upravuje počet zobrazených tlačítek tak, aby se vešly na obrazovku a zbylá tlačítka nechá “přetéct” do sekundárního menu. Tato novinka bohužel přinesla neúmyslné problémy některým vývojářům, kteří používali vlastnost Content pro zobrazení dalšího obsahu na panelu – zarovnání obsahu nyní nefunguje dle očekávání.

Problém

Situaci demonstrujeme na jednoduchém příkladě:

Očekáváme, že obsah panelu bude zarovnaný uprostřed volného místa, které zbývá po umístění tlačítek. A tak tomu také před Anniversary Update bylo.

Od verze 14393 SDK ale vlastnost  HorizontalContentAlignment  ve výchozím stavu není respektována a obsah je zarovnán vlevo.

CommandBar ve výchozím stavu nerespektuje vlastnost HorizontalContentAlignment

Příčina problému

Výchozí šablony ovladacích prvků jsou definovány v souboru XAML resource dictionary na následující cestě: C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.14393.0\Generic\generic.xaml . Pokud v tomto souboru vyhledáte šablonu pro CommandBar  , zjistíte, že obsahuje novou skupinu  VisualStateGroup :

Jak je z kódu vidět, při povoleném dynamic overflow se mění nastavení velikosti sloupců v rozložení CommandBaru .

Ve výchozím visual state ( DynamicOverflowDisabled ) je   ContentControlColumnDefinition.Width  nastavena na  *  (hvězda) a PrimaryItemsControlColumnDefinition.Width  nastavena na  Auto . To znamená, že tlačítka napravo zabírají určitou šířku a zbylý prostor je dedikován obsahu Content .

Když je dynamic overflow povolen, velikosti sloupců se prohodí. Ovladací prvek nyní nechá sloupec s tlačítky roztáhnout (aby dostupné místo mohl potenciálně využít pro více tlačítek a mohl také spočítat prostor pro ně za běhu aplikace) a sloupec s obsahem dostane už pouze takový prostor, který skutečně potřebuje. To znamená, že s povoleným dynamic overflow bude obsah vždy vypadat jako zarovnaný vlevo, protože jeho sloupec mu nenabízí žádné místo navíc.

Od Anniversary Update je dynamic overflow u command baru ve výchozím stavu povolen.

Řešení

Dynamic overflow můžete vypnout pomocí vlastnosti IsDynamicOverflowEnabled. Ačkoliv se poté musíte ujistit, že se tlačítka na panelu zobrazují správně pro všechna rozlišení obrazovky, můžete díky tomu zarovnávat obsah dle potřeby.

Zakázání dynamic overflow opraví problém se zarovnáním obsahu

Pokud chcete zachovat dynamic overflow, můžete namísto jeho vypnutí okolo vašeho obsahu přidat odsazení ( Margin ) tak, aby vypadal zarovnaný. Pravděpodobně bude ale nutné použít Adaptive Triggery aby odsazení vypadalo dobře na každém zařízení.

Aligning UWP CommandBar content after Anniversary Update

The Universal Windows Platform CommandBar control has a new feature called dynamic overflow since the Anniversary Update. This automatically adjusts the number of presented app bar buttons so that they fit and puts the additional commands in the secondary (overflow) menu. This addition has however inadvertently caused some headaches for developers who use Content property of the  CommandBar  to display additional content – it turns out, that alignment of the content now doesn’t work properly.

The problem

Let’s demonstrate the issue with a simple example.

We would expect, that the content of the CommandBar is aligned to the center of its available area, which indeed was the case before the Anniversary Update.

Since Anniversary 14393 SDK, the HorizontalContentAlignment  property is by default not respected.

The CommandBar does not respect the HorizontalContentAlignment property by default

Cause of the problem

The default control templates are stored in a XAML resource dictionary file on the following path: C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.14393.0\Generic\generic.xaml . If you search for the CommandBar  template inside this file, you will find out that it contains a new VisualStateGroup :

As you can see, when dynamic overflow is enabled, sizing of the columns in the main layout Grid of the control changes.

In the default visual state ( DynamicOverflowDisabled ) is the ContentControlColumnDefinition.Width  set to *  (star) and the PrimaryItemsControlColumnDefinition.Width  set to Auto . This means that the app bar buttons on the right take up a certain width and the remaining space is dedicated to the content.

With dynamic overflow enabled, sizing of the columns is flipped. The control lets the primary items column take up as much space as it can (so that the available space can be used for the buttons and the number of displayable buttons can be calculated at runtime) and the content column now gets only the width it actually needs. Whichever alignment you set to the content doesn’t matter. The content will always seem left aligned, because its column is sized just to fit.

Since the Anniversary Update, dynamic overflow of the command bar is enabled by default.

Solution

You can disable dynamic overflow using the IsDynamicOverflowEnabledproperty. Although you have to make sure that the app bar buttons display well on all display sizes, you can also align the content as you please.

Disabling the dynamic overflow feature fixes the content alignment problem

If you want to preserve the dynamic overflow feature, you may just want to put some margin around your content to make sure it looks aligned. Of course, to support multiple different display sizes, you should adjust the margins using Adaptive Triggers.