This is quite simple task but I am running short on ideas how to implement it. I have added FontAwesome to my app and would like to create a picker control that would display two different labels with different fonts side by side, but it looks like Picker control applies font on control level and it is not possible to display two different fonts for each item side by side. I need this so I could display icon from FontsAwesome and text with my default font. Are there any solutions to this?
Here is what I have tried so far:
public class DualLabelPicker : Picker
{
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(DualLabelPicker), null);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty IconFontFamilyProperty =
BindableProperty.Create(nameof(IconFontFamily), typeof(string), typeof(DualLabelPicker), default(string));
public string IconFontFamily
{
get { return (string)GetValue(IconFontFamilyProperty); }
set { SetValue(IconFontFamilyProperty, value); }
}
public static readonly BindableProperty TextFontFamilyProperty =
BindableProperty.Create(nameof(TextFontFamily), typeof(string), typeof(DualLabelPicker), default(string));
public string TextFontFamily
{
get { return (string)GetValue(TextFontFamilyProperty); }
set { SetValue(TextFontFamilyProperty, value); }
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == ItemsSourceProperty.PropertyName)
{
RefreshPickerItems();
}
}
private void RefreshPickerItems()
{
Items.Clear();
if (ItemsSource != null)
{
foreach (IconTextPickerItem item in ItemsSource)
{
FormattedString formattedString = new FormattedString();
Span iconSpan = new Span { Text = item.Icon, FontFamily = IconFontFamily, FontAttributes = FontAttributes.Italic };
formattedString.Spans.Add(iconSpan);
Span textSpan = new Span { Text = " " + item.Text, FontFamily = TextFontFamily };
formattedString.Spans.Add(textSpan);
// Convert the FormattedString to a string and add it to the Items collection
Items.Add(FormattedStringToString(formattedString));
}
}
}
private string FormattedStringToString(FormattedString formattedString)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (Span span in formattedString.Spans)
{
stringBuilder.Append(span.Text);
}
return stringBuilder.ToString();
}
}
Here is model
public class IconTextPickerItem
{
public string Icon { get; set; }
public string Text { get; set; }
}
EDIT 1:
So I have created now this class to Platforms > Windows
using Microsoft.Maui.Handlers;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using UWPDataTemplate = Microsoft.UI.Xaml.DataTemplate;
namespace Application.Windows
{
public partial class PickerRowExHandler : PickerHandler
{
protected override ComboBox CreatePlatformView()
{
return new PickerComboBox();
}
protected override void ConnectHandler(ComboBox platformView)
{
base.ConnectHandler(platformView);
}
protected override void DisconnectHandler(ComboBox platformView)
{
base.DisconnectHandler(platformView);
}
}
public class PickerComboBox : ComboBox
{
public PickerComboBox()
{
// Define the XAML markup for the DataTemplate
string xaml = @"
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:local='using:CalendarApplication.Models'>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='{Binding Icon}' FontFamily='FontAwesomeSolid' FontSize='20' VerticalAlignment='Center' Margin='5'/>
<TextBlock Text='{Binding Text}' VerticalAlignment='Center' Margin='5'/>
</StackPanel>
</DataTemplate>";
// Load the XAML markup as the ItemTemplate
ItemTemplate = (UWPDataTemplate)XamlReader.Load(xaml);
}
}
}
Then I have added this to generic Models folder
public class PickerItem
{
public string Text { get; set; }
public string Icon { get; set; } // Path to the icon or a resource identifier
}
Then this to ViewModel
public MainViewModel()
{
Items = new ObservableCollection<PickerItem>
{
new PickerItem { Text = "User", Icon = "\uf007" },
new PickerItem { Text = "Cog", Icon = "\uf013" },
new PickerItem { Text = "Home", Icon = "\uf015" },
};
}
public ObservableCollection<PickerItem> Items { get; set; }
Then this to xaml
<controls:PickerRowEx x:Name="MyPicker" Padding="0,0,0,0"
BackgroundColor="Transparent" BorderColor="DarkBlue" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" MinimumWidthRequest="200"
SelectedIndexChanged="MyPicker_SelectedIndexChanged" TextColor="Blue"
ItemsSource="{Binding Items}">
<controls:PickerRowEx.ItemTemplate>
<DataTemplate x:DataType="models:PickerItem">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Icon}" FontFamily="FontAwesomeSolid" FontSize="20" VerticalOptions="Center" Margin="5"/>
<Label Text="{Binding Text}" VerticalOptions="Center" Margin="5"/>
</StackLayout>
</DataTemplate>
</controls:PickerRowEx.ItemTemplate>
</controls:PickerRowEx>
Currently problem is that I see there is correct amount of items in my combobox in UI, but I do not see any text. So I assume the problem is with bindings and that below can not assign correct x:DataType="models:PickerItem", but I do not know how to set it properly. Tried several options without success
public class PickerComboBox : ComboBox
{
public PickerComboBox()
{
// Define the XAML markup for the DataTemplate
string xaml = @"
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:local='using:CalendarApplication.Models'>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='{Binding Icon}' FontFamily='FontAwesomeSolid' FontSize='20' VerticalAlignment='Center' Margin='5'/>
<TextBlock Text='{Binding Text}' VerticalAlignment='Center' Margin='5'/>
</StackPanel>
</DataTemplate>";
// Load the XAML markup as the ItemTemplate
ItemTemplate = (UWPDataTemplate)XamlReader.Load(xaml);
}
}