Xamarin.Forms : Using styles on custom UserControls

To use styles for your custom usercontrol, you have to define the styles TargetType property with an x:Type in your resource dictionary.

<ResourceDictionary
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:buttons="clr-namespace:AnAwesomeApp.Controls.Buttons"
    x:Class="AnAwesomeApp.Resources.Styles">

Declare the clr-namespace where your custom usercontrol lives.

<Style
        x:Key="BlueButtonStyle"
        TargetType="{x:Type buttons:ColoredButton}">
        <Setter
            Property="TextColor"
            Value="White" />
        <Setter
            Property="Color"
            Value="{AppThemeBinding Dark=#2325A6, Light=#61BAFF}" />
        <Setter
            Property="FontFamily"
            Value="Nunito" />
    </Style>

Set the target type with x:Type.

And boom, done!

Xamarin Forms: Ismerkedés a CustomUserControllokkal

Aki a Xamarin fejlesztésben kipróbálta magát, az könnyen találkozhatott azzal a problémával hogy valamit nem tud megvalósítani, mert nem ad rá a rendszer kész usercontrolt.

Prológus:


Nem rég azzal a problémával szembesültem, hogy a Xamarin Formsban elérhető ListView-nak a ContextActions menüjének láthatósága nem szabályozható. A ContextActions menu egy ListView elem hosszan nyomására jelenik meg. Ez tulajdonképpen egy lista MenuItem objektumokból. Ennek segítségével indíthatunk Commandokat a kiválasztott ListView elemét továbbadva. Azonban felmerülhet olyan igény, hogy szabályozhatóvá tehessük a hosszan nyomásra előtűnő menünk láthatóságát. Erre sajnos nincs lehetőségünk, csak különféle workaroundokkal. Rengeteg utánakeresés után arra jutottam, hogy a DataTemplate gyerekének, a ViewCell-nek az IsEnabled tulajdonsága tulajdonképpen letiltja a hosszan nyomást. Ez működött, adatköthettem a ViewModel-ben szereplő UI mód tulajdonságra, így a ContextMenü már csak akkor jelent meg, ha a UI mód szerkesztés módra volt állítva, megtekintés módon természetesen nem voltak elérhetőek a ContextMenüben szereplő adatmanipuláló gombok. És ekkor jött az igény. Mégpedig az, hogy szerkesztés módban jöjjenek elő az adatmanipuláló gombok, emellett megtekintés módban az elemre kattintva hajtódjon végre egy másik parancs. Erre gondoltam azt, hogy a SelectedItem tulajdonságnak Setter ága tökéletes lessz számomra. Mivel a ViewCell IsEnabledje megtekintés módban false volt, ezért a SelectedItem sem állítódott rajta. Az igény viszont az, hogy megtekintés módban csak a ContextMenü ne legyen elérhető, a lista elemre való kattintás viszont igen. Csak időközben felmerült még egy probléma: Ameddig a lista tartalmát nem frissítettem, addig a ContextMenü egy gombjára kattintva a Commandnak átadott paraméter null volt, és nem a ListView hosszan nyomott eleme. Ezek együttese igencsak nagy problémának tűnt számomra. Aztán rászántam magam a megoldására: A turbósított usercontrol fejlesztésére.

Megoldás:
Először azt szerettem volna megoldani, hogy ne a SelectedItem setter ágában kelljen végrehajtanom azt a logikát, amit akkor kell futtatnom, amikor a felhasználó rányom egy elemre. Továbbá a SelectedItem nem változik meg, ha a felhasználó ugyanarra az elemre nyom a listában. Leszármaztattam egy “ExtendedListView-t” a ListView osztályból. Új controlom konstruktorában feliratkoztam az ősosztály ItemTapped eseményére. Létrehoztam egy Bindolható Propertyt, ami várja azt a parancsot, amelyet végre kell majd hajtania. Az ItemTapped eseménykezelőjében pedig a szükséges vizsgálatok után megfuttatom a Commandot. Ezzel a felhasználó kétszer is rányomhat ugyanarra az elemre.

A másik nehezebb dolog a ContextActions menü eltüntetése volt. Tudtam, ha a ContextActions listája üres, hosszan nyomására nem jelenik meg semmi. Ha legalább egy elem van benne, akkor már megjelenik. Leszármaztam egy ViewCell objektumból. Létrehoztam egy ContextActionsEnabled bindolható tulajdonságot. Ennek a tulajdonságnak változására iratkoztam fel egy eseménykezelővel. Létrehoztam egy privát field-et, ami MenuItem-eket tárol. Sajnos nem találtam a Xamarinban a WPF-hez hasonló Initalized eseményt, így nem tudtam arra feliratkozni, hogy a Control beállítódott. Ez azért volt probléma, mert a tervem az volt, hogy XAML-ben definiálom hogy milyen MenuItemek vannak a ContextActionsben, majd amint a Control befejezte a beállítódását, eltárolom a MenuItemeket, a privát fieldemben. Így csak a ContextActionsEnabled változására kellett volna egy eseménykezelést írnom. Amennyiben a ContextActionsEnabled true értéket kap, abban az esetben beleteszem az ősosztály ContextActions tulajdonságába a fieldemben tárolt MenuItemeket, amennyiben false értéket kap, abban az esetben pedig eltávolítom az összeset ami benne van (az ősosztály ContextActions tulajdonságában). Mivel nem volt Initalized esemény, kénytelen voltam a ContextActionsEnabled megváltozásának eseménykezelőjébe beletenni azt, hogy első alkalommal tárolja el a field-be az eredeti XAML-ben definiált értékeket. Ez azt eredméynezi, hogy ha előbb definiáljuk XAML-ben a ContextActionsEnabled Propertyt, mint magát a ContextActions-t, nem fog működni. Erre még jó lenne egy jobb megoldást hallani. Ha van rá ötleted, kérlek jelezd azt kommentben!


Nem jártam utána, de miután a ViewCell IsEnabledjéről lekerült a Bindolás, a ContextMenü commandjai már soha többé nem kaptak null értéket.