Xamarin Forms FontSize by Platform - xaml

There is a known issue with UWP Xamarin Apps in that the size that fonts render in for Windows Mobile (not sure about Desktop Windows 10) are MUCH larger than they render on the other two platforms iOS and Android. The fix I have found a for this is to put a numeric font size with a decimal point in it (for example 24.1 for Large font size) in UWP apps. This works great. What I would like not is to define a static resource in my App.xaml that will work for all platforms. What I tried, that obviously does not work, is the following:
<OnPlatform x:Key="CustLarge" x:TypeArguments="x:Double">
<On Platform="iOS|Android">Large</On>
<On Platform="UWP">24.1</On>
</OnPlatform>
The theory is that in my XAML I would simply code all my large font sizes as "CustLarge" like this:
FontSize = "{StaticResource CustLarge}" />
Any ideas how I can accomplish this without doing on OnPlatform for every FontSize in my app? Any help would be appreciated.
UPDATE: Below are screen shots of what I am talking about. The iOS emulator was for the iPhone 6 4.7" wide. The Windows 10 emulator was a the 10.0.15254.9 5" phone.
You can see the Map All text is way bigger than it should be. (I am doing a relative comparison to the text in the segment control to the right of the stand alone buttons.) In both cases the fontsize is set to MICRO.
So back to my question - does anyone know how to do this?

I was able to find a workaround by creating a custom ResourceDictionnary, and adding FontSizes in the constructor.
public class SResourceDictionnary : ResourceDictionary
{
public SResourceDictionnary()
{
Add("Micro", new OnPlatform<double>
{
Default = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
Platforms = { new On
{
Value = 12d,
Platform = new List<string>{"UWP"}
}
}
});
Add("Small", new OnPlatform<double>
{
Default = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
Platforms = { new On
{
Value = 14d,
Platform = new List<string>{"UWP"}
}
}
});
Add("Medium", new OnPlatform<double>
{
Default = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
Platforms = { new On
{
Value = 18d,
Platform = new List<string>{"UWP"}
}
}
});
Add("Large", new OnPlatform<double>
{
Default = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
Platforms = { new On
{
Value = 20d,
Platform = new List<string>{"UWP"}
}
}
});
}
Then I merged the dictionary in my App.xaml like this
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<helpers:SResourceDictionnary></helpers:SResourceDictionnary>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Label">
<Setter Property="FontSize" Value="{StaticResource Small}" ></Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
It allowed me to use FontSize as StaticResource.
The only disadvantage is that you lose intellisense in Xaml

Related

How do I customize the Window Titlebar in a Windows App SDK (WIN UI/Project Reunion) Project?

I'm trying to build an app using the new Widows App SDK. I used the Windows Community Toolkit to create the application.
After consulting the documentation, I tried this:
On the first page that my app displays, I created a Textblock:
<TextBlock Text="Hello" x:Name="CustomTitleBar" />
In this page's code behind, I added the following code:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
App.MainWindow.ExtendsContentIntoTitleBar = true;
App.MainWindow.SetTitleBar(CustomTitleBar);
App.MainWindow.Activate();
}
On the App XAML page I followed the doumentation's directions to override these values:
<SolidColorBrush x:Key="WindowCaptionBackground">Green</SolidColorBrush>
<SolidColorBrush x:Key="WindowCaptionBackgroundDisabled">LightGreen</SolidColorBrush>
<SolidColorBrush x:Key="WindowCaptionForeground">Red</SolidColorBrush>
<SolidColorBrush x:Key="WindowCaptionForegroundDisabled">Pink</SolidColorBrush>
This above does make the default titlebar go away. However, I am left with just the word "Hello" with no background or buttons:
Is there something I'm missing?
You should probably be using the AppWindow and presenter (in my case I used the OverlapedPresenter) classes. Here's some code I used on my app.
public MainWindow()
{
InitializeComponent();
AppWindow appWindow = GetAppWindowForCurrentWindow();
OverlappedPresenter overlappedPresenter = appWindow.Presenter as OverlappedPresenter;
overlappedPresenter.IsResizable = false;
appWindow.TitleBar.ExtendsContentIntoTitleBar = false;
}
Then, right under the constructor I use the method Microsoft provides in this sample:
private AppWindow GetAppWindowForCurrentWindow()
{
IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WindowId myWndId = Win32Interop.GetWindowIdFromWindow(hWnd);
return AppWindow.GetFromWindowId(myWndId);
}
I use the OverlapsedPresenter class to change some things about the window, like if it's resizable or not.

Problems with global styles on Android, iOS working fine (Xamarin)

I'm facing some strange behaviour in my Xamarin App.
In the shared project I separated all control based styles in single xaml files.
All styles are combined in the DefaultTheme.xaml file.
Then, the DefaultTheme.xaml will be used by the LightTheme.xaml and DarkTheme.xaml.
That's how my styles are managed. Then, depending on "Darkomde on/off" I either load the LightTheme.xaml or the DarkTheme.xaml in the iOS and Android project.
On Android I set the themes like this in the MainActivity.cs (I'm using a SplashActivity.cs as well, should it be set there already?)
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
SetAppTheme();
}
void SetAppTheme()
{
if (Resources.Configuration.UiMode.HasFlag(UiMode.NightYes))
SetTheme(RemoteControlRepetierServer.Theme.Dark);
else
SetTheme(RemoteControlRepetierServer.Theme.Light);
}
public void SetTheme(Theme theme)
{
switch (theme)
{
case RemoteControlRepetierServer.Theme.Light:
// Night mode is not active, we're using the light theme
if (App.AppTheme == "light")
return;
App.Current.Resources = new LightTheme();
App.AppTheme = "light";
break;
case RemoteControlRepetierServer.Theme.Dark:
// Night mode is active, we're using dark theme
if (App.AppTheme == "dark")
return;
//Add a Check for App Theme since this is called even when not changed really
App.Current.Resources = new DarkTheme();
App.AppTheme = "dark";
break;
}
App.CurrentAppTheme = theme;
}
So far so good. Now, if I start my Android app, it seems that not all styles are loaded. For instance the Gauge. While the Gauge on the first tab looks like it should, the Gauge on the second tab doesn't.
Both are using the same XAML for the styling.
<gauge:Scale.Pointers>
<gauge:NeedlePointer Value="{Binding SelectedExtruder.TempRead}" Color="{DynamicResource Gray-Black}"
KnobColor="{DynamicResource Gray-Black}" KnobStrokeColor="{DynamicResource Gray-Black}"/>
<gauge:RangePointer Value="{Binding SelectedExtruder.TempRead}" Color="{DynamicResource PrimaryColor}"
RangeCap="Both"
/>
<gauge:MarkerPointer Value="{Binding SetExtruderTemp}" Color="{DynamicResource Red}"/>
</gauge:Scale.Pointers>
Also the Style of the NumericUpDown control is not set on the second tab. This only happens on Android, iOS looks fine all tabs.
I'm not sure what's causing this issue. Any help is appreciated.
If you need further information or code snippets, just let me know.
Just figured out that using the SyncfusionTheme was causing this behaviour. Once I removed it from the MergedDictionaries, it worked.

Runtime change Resource Dictionary for a Windows 8.1 XAML Store App

I am trying to dynamically change my resource dictionary definitions for my application during run time.
I am doing this because I would like to have different font sizes, scaling etc for my application based on the view port size.
I currently have the following code which gets executed every time the view port size changes:
string stylesPath;
if (args.Type == "small")
{
stylesPath = "ms-appx:///Styles/small.xaml";
}
else
{
stylesPath = "ms-appx:///Styles/standard.xaml";
}
var resourceDictionary = new ResourceDictionary
{
Source = new Uri(stylesPath, UriKind.RelativeOrAbsolute)
};
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
This works for the initial load. When I change my view port this code does get hit again, but after the resources dictionaries are cleared and reloaded my application's fonts/styles/etc don't change at all.
It appears that the application styles will only be loaded once during application start up.
Does anyone know of a way I can force my application to redraw itself based on the new resource dictionary values?
This can be accomplished using Themes. There is a handy nuget package, called Theme Manager, that makes it very easy to switch themes.
Instead of loading your themes on app load, put them in your app ThemeDictionaries
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Themes/small.xaml"/>
<ResourceDictionary Source="/Themes/standard.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
Then you can change the theme like such:
var url = new Uri(string.Format("ms-appx:///Themes/{0}.xaml", (args.Type == "small") ? "small" : "standard"));
ThemeManager.ChangeTheme(url);
You can read more about ThemeManager here.

Sharing styles between several GridView controls

I need to style several GridView throughout my application with the same visual styles. This style includes customizing the ItemsPanel property as well as the GroupStyle property.
My problem is that the GroupStyle property of GridView is not a dependency property. So the code I would have liked to write (see below) does not work.
Do you know a clean way to share a style (including GroupStyle) between several GridViews?
The only thing I can think of is using a GroupStyleSelector but it's kind of stupid since there is no selection to make: it's always the same GroupStyle that's being used. Moreover, I suspect it wouldn't be reflected at design time in VS & Blend.
The code I would love to use:
<GridView
ItemsSource="..."
ItemTemplate="..."
Style="{StaticResource MainMenuStyle}"/>
<Style TargetType="GridView" x:Key="MainMenuStyle">
<Setter Property="ItemsPanel">
<Setter.Value>
...
</Setter.Value>
</Setter>
<Setter Property="GroupStyle">
<Setter.Value>
<GroupStyle>
...
</GroupStyle>
</Setter.Value>
</Setter>
</Style>
I've got a magical happy solution.
You can create a custom Attached Property that you set in the Style, and upon setting it internally sets the GroupStyle property on the GridView.
Attached Property:
// Workaround for lack of generics in XAML
public class GroupStyleCollection : Collection<GroupStyle>
{
}
public class GroupStyleHelper
{
public static ICollection<GroupStyle> GetGroupStyle(ItemsControl obj)
{
return (ICollection<GroupStyle>)obj.GetValue(GroupStyleProperty);
}
public static void SetGroupStyle(ItemsControl obj, ICollection<GroupStyle> value)
{
obj.SetValue(GroupStyleProperty, value);
}
public static readonly DependencyProperty GroupStyleProperty =
DependencyProperty.RegisterAttached(
"GroupStyle",
typeof(ICollection<GroupStyle>),
typeof(GroupStyleHelper),
new PropertyMetadata(null, OnGroupStyleChanged));
private static void OnGroupStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ItemsControl itemsControl = d as ItemsControl;
if (itemsControl == null)
return;
RefreshGroupStyle(itemsControl, GetGroupStyle(itemsControl));
}
private static void RefreshGroupStyle(ItemsControl itemsControl, IEnumerable<GroupStyle> groupStyle)
{
itemsControl.GroupStyle.Clear();
if (groupStyle == null)
return;
foreach (var item in groupStyle)
{
itemsControl.GroupStyle.Add(item);
}
}
}
XAML Style:
<Style TargetType="ItemsControl">
<Setter Property="GroupStyleTest:GroupStyleHelper.GroupStyle">
<Setter.Value>
<GroupStyleTest:GroupStyleCollection>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="15" Text="{Binding Path=Name}" Foreground="HotPink"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GroupStyleTest:GroupStyleCollection>
</Setter.Value>
</Setter>
</Style>
Disclaimer: I'm testing this in WPF rather than WinRT but it should work the same, as far as I can tell. That's also why I'm using an ItemsControl rather than GridView, but the property is ItemsControl.GroupStyle anyway.
I've a solution and that will definitely work as per your question, but though you should decide whether to use that in your case or not.
If you have to make same style of a control in all over the project, then you should make one common folder and in that folder
create one "Custom User Control" and apply all of your style and
customize it however you want.
After that when you need to apply that same kind of style on same control (any grid control) then simply add that customized user
control instead of predefined control
By doing this you'll also achieve MVC architecture and modularity.
I'm developing Windows 8 Metro app in C# with XAML, and in that whenever i wanted this approach then i always use this solution and it always works...
to create custom user control, you should use visual studio & in that right click on project -> add -> new item -> User Control
(Sorry if you couldn't find your solution here, but i think this might help...)

Handle font size for multiple screen size windows 8 metro apps

I have a text of font size 14.
On smaller screens it's visible but on bigger screens it becomes smaller.
How do I handle this?
On android we have SP which adjust the font size according to the screens.
Is there anything similar to this in windows 8 ?
This will do exactly what you want.
<Viewbox>
<TextBlock>Hello World</TextBlock>
</Viewbox>
I was doing a research on this.
I came to know 2 different things.
One is View Box and the other is logical DPI.
I assume you are using XAML?
So, you should start with something like this:
<Page.Resources>
<x:Double x:Key="MyFontSize" />
<Style TargetType="TextBlock" x:Name="StandardText">
<Setter Property="FontSize" Value="{StaticResource MyFontSize}" />
</Style>
</Page.Resources>
<TextBlock Style="{StaticResource StandardText}">Hello World</TextBlock>
Then in your code behind have something like this:
Double _FontSize;
if (Windows.UI.ViewManagement.ApplicationView.Value
== Windows.UI.ViewManagement.ApplicationViewState.FullScreenPortrait)
{
// based on portrait
if (this.RenderSize.Height > 2000)
_FontSize = 30;
if (this.RenderSize.Height > 1000)
_FontSize = 20;
else
_FontSize = 10;
}
else
{
// based on landscape
if (this.RenderSize.Height > 1500)
_FontSize = 30;
if (this.RenderSize.Height > 1000)
_FontSize = 20;
else
_FontSize = 10;
}
this.Resources["MyFontSize"] = _FontSize;
Whatever you detect when your app loads will always remain unless the user changes monitors on you!