How to bind DropDownButton's items to a ViewModel?
<DropDownButton Width="100" Grid.Column="2" ToolTipService.ToolTip="Language">
<TextBlock FontSize="14" Text="Select language"/>
<DropDownButton.Flyout>
<MenuFlyout Placement="BottomEdgeAlignedLeft">
<MenuFlyoutItem Text="English" Tag="enEn" Click="DropdownOptionLanguage_Select" />
</MenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
If we follow the logic here MenuFlyout
or DropDownButton.Flyout
is the container for items. But there is no ItemSource
property or something what I can bind to an enumerable.
In this case you can add items in code-behind:
MainPage.xaml
<DropDownButton Content="Select language">
<DropDownButton.Flyout>
<MenuFlyout x:Name="SelectLanguageMenuLayout" />
</DropDownButton.Flyout>
</DropDownButton>
public enum Languages
{
English,
Spanish,
French,
German,
Italian,
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
foreach (Languages language in Enum.GetValues<Languages>())
{
this.SelectLanguageMenuLayout.Items.Add(
new MenuFlyoutItem
{
Text = language.ToString(),
Command = ViewModel.ChangeLanguageCommand,
CommandParameter = language,
});
}
}
public MainPageViewModel ViewModel { get; } = new();
}
public partial class MainPageViewModel : ObservableObject
{
[RelayCommand]
private void ChangeLanguage(Languages language)
{
}
}
I'm using the CommunityToolkit.Mvvm NuGet package for the MVVM design.
UPDATE
This should be a closer approach to what you mentioned in the comments:
MenuFlyoutEx.cs
public class MenuFlyoutEx : MenuFlyout
{
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
nameof(ItemsSource),
typeof(IEnumerable),
typeof(MenuFlyoutEx),
new PropertyMetadata(default, OnItemsSourcePropertyChanged));
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(
nameof(Command),
typeof(ICommand),
typeof(MenuFlyoutEx),
new PropertyMetadata(default));
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
private static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not MenuFlyoutEx menuFlyoutEx ||
e.NewValue is not IEnumerable itemsSource)
{
return;
}
menuFlyoutEx.Items.Clear();
foreach (object item in itemsSource)
{
MenuFlyoutItem menuFlyoutItem = new()
{
Text = item.ToString(),
Command = menuFlyoutEx.Command,
CommandParameter = item,
};
menuFlyoutEx.Items.Add(menuFlyoutItem);
}
}
}
MainPageViewModel.cs
// This class needs to be "partial" for the CommunityToolkit.Mvvm.
public partial class MainPageViewModel : ObservableObject
{
// The CommunityToolkit.Mvvm will generate a "ChangeLanguageCommand" for you.
[RelayCommand]
private void ChangeLanguage(Languages language)
{
// Do your logic here...
}
// The CommunityToolkit.Mvvm will generate a "LanguageOptions" property for you.
[ObservableProperty]
private Languages[] _languageOptions = Enum.GetValues<Languages>();
}
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public MainPageViewModel ViewModel { get; } = new();
}
and use it like this:
<DropDownButton Content="Select language">
<DropDownButton.Flyout>
<local:MenuFlyoutEx
Command="{x:Bind ViewModel.ChangeLanguageCommand}"
ItemsSource="{x:Bind ViewModel.LanguageOptions}" />
</DropDownButton.Flyout>
</DropDownButton>