Search code examples
uwpresourcedictionaryxbind

UWP : How to use x:bind in ResourceDictionary?


I want to use ResourceDictionary in UWP like I used in WPF In WPF, I do this in ResourceDictionary file(*Style.xaml)

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
    xmlns:models="using:NWP.Models">

<Style x:key="MenuContent" TargetType="ContentControl">
    <Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="ContentControl">
            <controls:DockPanel>
                <ItemsControl ItemsSource="{x:Bind How-Can-I-Bind-Collection-Here?}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate x:DataType="models:MenuItemModel">
                            <RadioButton GroupName="MenuItems" IsChecked="{x:Binding IsChecked, Mode=TwoWay}" MinHeight="0" MinWidth="0" Command="{Binding Command}" CommandParameter="{Binding}"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>

                <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentTransitions="{TemplateBinding ContentTransitions}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </controls:DockPanel>
        </ControlTemplate>
    </Setter.Value>
</Style>

</ResourceDictionary>

Then I can use this style in my Page:

<ContentControl Style="{StaticResource MenuContent}">
    <StackPanel Orientation="Vertical">
        <TextBox/>
        <PasswordBox/>
        <Button Content="Login"/>
    </StackPanel>
</ContentControl>

But now, I struggle on how to provide the source for ItemsSource in ItemsControl in ResourceDictionary with x:bind:

<ItemsControl ItemsSource="{x:Bind How-Can-I-Bind-Collection-Here?}">

My question is, how to solve this problem?


Solution

  • Add to @Bite's suggestion, I need to explain more information for you. In that document, it says:

    if you use {x:Bind} in a resource dictionary then the resource dictionary needs to have a code-behind class.

    1. From the docs page, it sounds like it could work, using the backing class as the binding source like any other x:Bind, but the compiler explicitly rejects any x:Bind in a Style. It’s simply not supported. So, you can’t use x:Bind in a Style, not even for a DataTemplate defined within a Style.
    2. In order to use x:Bind from a DataTemplate defined in a ResourceDictionary, you need to create a code behind class for it. This template must still be outside of any Style, because of point 1, but you need the backing class for x:Bind to work from the ResourceDictionary.
    3. To get the item source binding to work, using a ResourceDictionary-based approach, you need to use legacy {Binding} instead of {x:Bind}.
    4. Since we must use legacy {Binding} to make it work, the item source must be customized by setting and/or adjusting the DataContext of the ContentControl.

    Then, I've made a simple code sample for your reference:

    <ResourceDictionary
    x:Class="AppStyle.MenuDictionary"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AppStyle"
    xmlns:System="using:System">
    
    <Style x:Key="MenuContent" TargetType="ContentControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ContentControl">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <ItemsControl ItemsSource="{Binding MenuItems}" ItemTemplate="{StaticResource MenuItemDataTemplate}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                        </ItemsControl>
                        <ContentPresenter Grid.Row="1" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentTransitions="{TemplateBinding ContentTransitions}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <DataTemplate x:Key="MenuItemDataTemplate" x:DataType="System:String">
        <RadioButton GroupName="MenuItems" MinHeight="0" MinWidth="0" Content="{x:Bind}" />
    </DataTemplate>
    

    public sealed partial class MenuDictionary : ResourceDictionary
    {
        public MenuDictionary()
        {
            this.InitializeComponent();
        }
    }
    
    <Page
    x:Class="AppStyle.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AppStyle"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <local:MenuDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ContentControl Style="{StaticResource MenuContent}">
            <StackPanel>
                <TextBlock>Test</TextBlock>
                <TextBox />
            </StackPanel>
        </ContentControl>
    </Grid>
    

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.DataContext = this;
        }
        public IEnumerable<string> MenuItems => new string[] { "Page Item 1", "Page Item 2", };
    }