SuperJMN
SuperJMN

Reputation: 14002

x:Bind and UserControls

I'm trying to use compiled bindings in UWP with a simple use case.

In order to make my XAML more readable easy to manage, I've extracted the XAML of a DataTemplate to a UserControl. So I transformed this

MainPage.xaml

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <ListView ItemsSource="{x:Bind ViewModel.Items}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:ProjectItem">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{x:Bind Name, Mode=OneWay}" />
                    <TextBlock Text="{x:Bind Description, Mode=OneWay}" />
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Page>

Into this

MainPage.xaml

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <ListView ItemsSource="{x:Bind ViewModel.Items}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:ProjectItem">
                <local:MyUserControl1 />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Page>

MyUserControl1.xaml

<UserControl
    x:Class="App1.MyUserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{x:Bind Name}" />
        <TextBlock Text="{x:Bind Description}" />
    </StackPanel>
</UserControl>

The problem is that it doesn't even compile because x:Bind doesn't know the context.

How does x:Bind cover this use case?

Upvotes: 2

Views: 1004

Answers (3)

cflorenciav
cflorenciav

Reputation: 439

This could be faster as the DataContext is casted only once when the Control is Loaded.

public ProjectItem ProjectItem { get; private set; }

public MyUserControl1()
{
    InitializeComponent();
    Loaded += (s, e) => 
    {
        ProjectItem = (ProjectItem)DataContext;
        Bindings.Update();
    };
}

Add this if DataContext change in MainPage:

    DataContextChanged += (s, e) => 
    {
        ProjectItem = (ProjectItem)DataContext;
        Bindings.Update();
    };

Based on the answer given by @mm8.

Upvotes: 2

Mac
Mac

Reputation: 957

I'd suggest create Dependency Property for ProjectItem on your MyUserControl1.xaml.cs

  public static readonly DependencyProperty ProjectItemProperty =
        DependencyProperty.Register(
            nameof(ProjectItem),
            typeof(ProjectItem),
            typeof(MyUserControl1),
            null);

    public ProjectItem ProjectItem
    {
        get => (ProjectItem)GetValue(ProjectItemProperty);
        set => SetValue(ProjectItemProperty, value);
    }

Then on your XAML, bind the properties of your ProjectItem Dependency Property:

<StackPanel Orientation="Horizontal">
    <TextBlock Text="{x:Bind ProjectItem.Name, Mode=OneWay}" />
    <TextBlock Text="{x:Bind ProjectItem.Description, Mode=OneWay}" />
</StackPanel>

Then on your MainPage.xaml, pass on the 'ProjectItem' collection item.

<DataTemplate x:DataType="local:ProjectItem">
            <local:MyUserControl1 ProjectItem="{x:Bind}"/>
        </DataTemplate>

Upvotes: 6

mm8
mm8

Reputation: 169400

If you use this approach, you could (or rather need to) add a property to MyUserControl1.xaml.cs that casts the current DataContext to a ProjectItem and returns it:

public ProjectItem Item => DataContext as ProjectItem;

public MyUserControl1()
{
    InitializeComponent();
    DataContextChanged += (s, e) => Bindings.Update();
}

You then bind to this property in the XAML markup:

<StackPanel Orientation="Horizontal">
    <TextBlock Text="{x:Bind Item.Name}" />
    <TextBlock Text="{x:Bind Item.Description}" />
</StackPanel>

The other option would to use non-compiled {Bindings} or get rid of MyUserControl1 and revert to the inline DataTemplate.

Upvotes: 3

Related Questions