توسعه برنامه‌های Cross Platform با Xamarin Forms & Bit Framework - قسمت شانزدهم
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: هفت دقیقه

در این قسمت می‌خواهیم به بحث Style دهی و Font‌ها در Xamarin Forms بپردازیم. در XF به دو روش می‌توان Style اعمال کرد؛ یکی با CSS و دیگری با Xaml. از هر روشی که استفاده کنیم، Styleها درون Resource‌ها قرار می‌گیرند. Resource، یک Dictionary است که درون آن هر چیزی می‌توان قرار داد؛ یک string یا Style یا عدد و ...
فایل App.xaml و همچنین تک تک صفحات، دارای Resources هستند که اگر چیزی درون App.xaml Resources قرار بگیرد، در کل برنامه می‌توان از آن استفاده کرد. برای مثال در صورت قرار دادن یک Style در App Resources، آن Style بر روی کل برنامه قابلیت اعمال پیدا می‌کند. همچنین اگر چیزی درون Resources یک صفحه قرار بگیرد، فقط برای کنترل‌های درون آن صفحه در دسترس است و ...
در روش Style دهی به سبک Xaml، می‌توانید از تمامی امکاناتی که تا اینجا یاد گرفته‌اید استفاده کنید؛ برای مثال Triggers و Binding و ... . هر Style یک Target Type دارد، اگر Style ای Target Type اش Label باشد، برای تمامی Property‌های Label می‌تواند تعیین تکلیف کند:
<Style TargetType="Label">
    <Setter Property="FontFamily" Value="Some font..." />
</Style>
هر Label مقدار FontFamily یا سایر Property‌های خودش را به شکل زیر حساب می‌کند:
1- اگر بر روی یک Label، صراحتا FontFamily مقدار دهی شود، آن مقدار معتبر در نظر گرفته می‌شود.
2- اگر در Resource‌های آن صفحه ای که Label درون آن قرار دارد، Style برای Label تعریف شود و در آن Style به FontFamily مقداری تخصیص داده شده باشد، آن مقدار ملاک قرار می‌گیرد.
3- اگر نه بر روی خود Label و نه در Resourceهای صفحه ای که Label در آن قرار دارد، مقدار FontFamily مشخص نشود، آنگاه بررسی می‌شود که آیا Style ای در App.xaml برای Label وجود دارد که به FontFamily مقداری داده باشد یا نه، که اگر وجود داشته باشد، آن عدد ملاک است.
4- در نهایت اگر هیچ جایی از FontFamily صحبتی نشده باشد، مقدار پیش فرض ارائه شده توسط تیم Xamarin Forms ملاک قرار می‌گیرد.

البته این آموزش همه جزئیات را شامل نمی‌شود تا از پیچیده شدن بحث جلوگیری کند و با همین مقدار از یادگیری نیز می‌توانید به خوبی کار خود را پیش ببرید.
حال اگر بخواهیم تمامی صفحات برنامه، Background آبی داشته باشند، می‌توان در App.xaml برای ContentPageها یک Style نوشت که BackgroundColor را آبی تعیین کرده باشد:
<Style TargetType="ContentPage" ApplyToDerivedTypes="True" >
    <Setter Property="BackgroundColor" Value="Blue" />
</Style>  
اگر دقت کنید، ApplyToDerivedTypes نیز مقدار True گرفته است. علت این است که صفحه لاگین که برای مثال قصد آبی کردن آن را به همراه تمامی صفحات دیگر داریم، از جنس ContentType نیست، بلکه از آن ارث بری کرده‌است. همچنین PopupPage‌ها نیز از ContentPage ارث بری کرده‌اند. با ApplyToDerivedTypes ما این Style را نه تنها بر روی ContentPage که بر روی کلاس‌هایی که از آن ارث بری کرده‌اند نیز اعمال می‌کنیم. صد البته که در پروژه ما خود ContentPage اصلا جایی مستقیما استفاده نشده‌است و تمامی صفحات ما، در واقع ارث بری ای از ContentPage محسوب می‌شوند. البته در مثال Label، ارث بری ای در کار نیست و همیشه مستقیما از Label استفاده می‌کنیم، نه اینکه از آن ارث بری کنیم.
حال ممکن است بخواهیم Style ای تعریف کنیم به نام Danger Button که نه کل دکمه‌های برنامه که چندتایی در صفحات مختلف می‌خواهند از آن استفاده کنند. اگر برای Button یک Style نوشته شود، تمامی Buttonها آن را خودکار می‌گیرند، پس راه حل چیست؟ راه حل این است که به Style ای که تعریف کرده‌ایم، یک x:Key دهیم. اگر Style ای Key داشته باشد، باید موقع استفاده در هر Button، نام آن Key ذکر شود.
<Style x:Key="DangerButton" TargetType="Button">
    <Setter Property="BackgroundColor" Value="Red" />
    <Setter Property="FontAttributes" Value="Bold" />
</Style>  
سپس موقع استفاده داریم:
<Button Style="{StaticResource DangerButton}" />
وقتی از StaticResource استفاده می‌کنیم، در Resourceهای صفحه‌ای که Button در آن قرار دارد و یا در App.xaml دنبال یک Style با x:Key برابر با DangerButton می‌گردد.
برای استفاده از Binding/Trigger در Styleها مثالی را داریم که متن تمامی Entryها به صورت Bold می‌شود، وقتی که IsFocused آنها True است!
<Style TargetType="Entry">
    <Style.Triggers>
        <Trigger TargetType="Entry" Property="IsFocused" Value="True">
            <Setter Property="FontAttributes" Value="Bold" />
        </Trigger>
    </Style.Triggers>
</Style>
برای Style دهی به شکل CSS نیز مثال زیر را داریم که متن تمامی buttonها را bold می‌کند:
<StyleSheet>
<![CDATA[
    button {
        font-style: bold;
    }
]]>
</StyleSheet>
می‌توان cssها را در فایل‌هایی با پسوند css نیز نوشت و به پروژه اضافه کرد. همچنین هر کنترل، دو ویژگی StyleId و class را دارد که بتوان به آن class / id داد تا در css به صورت . یا # استایل داد. مواردی چون stackpanel label به معنی label هایی که درون stack panel هستند و ... نیز پشتیبانی می‌شوند.
در رابطه با فونت نیز شما باید ابتدا فونت یا فونت‌های مربوطه را به هر سه پروژه Android/iOS/Windows اضافه کنید و سپس در App.xaml برای Font Family کنترل‌های مختلفی چون Label و ... از آن فونت استفاده کنید.
برای این مهم، ابتدا فایل‌های فونتی را انتخاب کرده (مثلا Open Sans) را به پروژه Windows/Android/iOS اضافه کنید: (سه فایل ما OpenSansItalic.ttf/OpenSansBold.ttf /OpenSansRegular.ttf هستند) 
در ویندوز آنها را در فولدر Assets/Fonts و در Android در فولدر Assets/Fonts و در iOS در فولدر Resources/Fonts کپی کنید. در iOS علاوه بر این کار، این‌ها را نیز به Key Value‌های فایل info.plist نیز اضافه کنید:
<key>UIAppFonts</key>
<array>
    <string>Fonts/OpenSansItalic.ttf</string>
    <string>Fonts/OpenSansRegular.ttf</string>
    <string>Fonts/OpenSansBold.ttf</string>
</array>  

سپس کدهای زیر را استفاده کنید:

<bitView:OnPlatform
    x:Key="OpenSansRegular"
    x:TypeArguments="x:String"
    Value="{OnPlatform Android='Fonts/OpenSansRegular.ttf#Open Sans',
                        iOS='OpenSans-Regular',
                        UWP='Assets/Fonts/OpenSansRegular.ttf#Open Sans'}" />

<!--  Italic مشابه کد بالا برای -->
<!--  Bold مشابه کد بالا برای -->

چون آدرس و نحوه نام دهی FontFamily در سه پلتفرم متفاوت است، با استفاده از OnPlatform، یک String می‌سازیم با x:Key برابر با OpenSansRegular که در هر پلتفرم مقدار خود را دارد. سپس از این نام برای مقدار دهی FontFamily در کنترل‌های Label/Entry/Button و ... در حالت‌های None/Italic/Bold استفاده می‌کنیم. برای مثال:

<Style TargetType="Label">
    <Style.Triggers>
        <Trigger TargetType="Label" Property="FontAttributes" Value="Bold">
            <Setter Property="FontFamily" Value="{StaticResource OpenSansBold}" />
        </Trigger>
        <Trigger TargetType="Label" Property="FontAttributes" Value="Italic">
            <Setter Property="FontFamily" Value="{StaticResource OpenSansItalic}" />
        </Trigger>
        <Trigger TargetType="Label" Property="FontAttributes" Value="None">
            <Setter Property="FontFamily" Value="{StaticResource OpenSansRegular}" />
        </Trigger>
    </Style.Triggers>
</Style>

این کد می‌گوید زمانیکه FontAttributes یک Label برابر با Bold است، از OpenSansBold برای FontFamily اش استفاده شود و همینطور برای Italic و None (یا همان Regular)

در قسمتی‌که داشتیم برای اندروید و ویندوز، مسیر فایل فونت را مشخص می‌کردیم، از مقدار OpenSansRegular.ttf#Open Sans استفاده کردیم که OpenSansRegular.ttf نام فیزیکی فایل و Open Sans نام خود فایل است که با دو بار کلیک کردن روی فایل آن در ویندوز از طریق برنامه Font ویندوز قابل مشاهده است:

همچنین برای اینکه این سه فایل، سه بار برای سه پلتفرم در سورس کنترلر کپی نشوند، از روش Add as link در Visual Studio بهره گرفته‌ایم و فایل فیزیکی فونت‌ها فقط در پروژه UWP وجود دارند. البته این به معنای این نیست که در Apk نهایی Android و ipa نهایی iOS این فایلها وجود نخواهند داشت؛ بلکه به خاطر ماهیت Add as link، انگار که این فایل‌ها در هر سه پروژه هستند و پشت صحنه کپی می‌شوند.