Vinderguy
Vinderguy

Reputation: 93

UWP: Get the reference of control's bound object programmatically

How do I get a reference to a object bound to a control displayed as part of ItemsRepeater?

XAML:

<Grid>
        <controls:ItemsRepeater ItemsSource="{x:Bind cars}">
            <controls:ItemsRepeater.ItemTemplate>
                <DataTemplate x:DataType="local:Car">
                    <Button Content="{x:Bind Brand}" Tapped="Button_Tapped"/>
                </DataTemplate>
            </controls:ItemsRepeater.ItemTemplate>
        </controls:ItemsRepeater>
</Grid>

C#:

private ObservableCollection<Car> cars = new ObservableCollection<Car>()
{
            new Car()
            {
                Brand = "Audi",
                Model = "123",
                Color = "Red",
                Price = "100 000 $"
            },
            new Car()
            {
                Brand = "BMW",
                Model = "456",
                Color = "Black",
                Price = "150 000 $"
            },
};

private void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
   // Warning: The code line bellow does not work. The problem is here.
   Car selectedCar = (sender as FrameworkElement).DataContext as Car;

   Debug.WriteLine("Brand: " + selectedCar.Brand);
   Debug.WriteLine("Model: " + selectedCar.Model);
   Debug.WriteLine("Color: " + selectedCar.Color);
   Debug.WriteLine("Price: " + selectedCar.Price);

   // Expected result (if Audi button is tapped):
   // Brand: Audi
   // Model: 123
   // Color: Red
   // Price: 100 000 $

   // Actual result: 
   // System.NullReferenceException: 'Object reference not set to an instance of an object.'

}

EDIT: Updated the example code to be better illustrate the issue.

It looks like your post is mostly code; please add some more details.

Upvotes: 1

Views: 420

Answers (1)

Leander
Leander

Reputation: 859

Updated due to update of the original question.

While trying my previously used method for this I found an even easier solution which should enable you to just use your original code.

1. The simple solution

Just add

DataContext="{x:Bind}"

to your

<Button Content="{x:Bind Brand}" Tapped="Button_Tapped"/>

line.

2. My original solution

This solution is similar to above stated solution, however for this I introduce an extra DependencyProperty to a custom Button class:

public class CustomButton : Button
{
    public static readonly DependencyProperty BoundCarProperty =
        DependencyProperty.Register("BoundCar", typeof(Car), typeof(CustomButton), new PropertyMetadata(new Car(), new PropertyChangedCallback(BoundCarValueChanged)));
    private static void BoundCarValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SpecialButton s = d as SpecialButton;
        Car c = d.GetValue(BoundCarProperty) as Car;
    }

    public Car BoundCar
    {
        get => (Car)GetValue(BoundCarProperty);
        set => SetValue(BoundCarProperty, value);
    }
}

Now I change the XAML to:

<local:SpecialButton BoundCar="{x:Bind Mode=OneWay}" Content="{x:Bind Brand}" Tapped="Button_Tapped"/>

As an added benefit to the first solution, this enables you to catch changes to the bound property, which you can handle in the BoundCarValueChanged event.

Upvotes: 1

Related Questions