uwp xaml DataBinding to nested property inside an ObservableCollection

1

This is my first time building a UWP app and I'm new to c#/Windows in general. I am trying to use a Pivot in the UI. I want the pivot headers to be from an ObservableCollection of usb devices connected, which go by the class Device_Info. Each USB Device has a property called HardwareRevNumMajor that I would like to display as each pivots header. My xaml looks like this:

<Page
x:Class="usb_test.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:usb_test"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:code="using:usb_test"
mc:Ignorable="d">
<Page.DataContext>
    <local:View_Model/>
</Page.DataContext>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Pivot x:Name="pivot1" Title="Pivot" Opacity="0.99" ItemsSource="{Binding Devices}">
        <Pivot.HeaderTemplate>
            <DataTemplate>
                <TextBlock>
                    <TextBlock.Text>
                        <Binding Path="Device_Info.HardwareRevNumMajor"/>
                    </TextBlock.Text>
                </TextBlock>

                <!--<TextBlock Text="{Binding HardwareRevNumMajor}">
                </TextBlock>-->
            </DataTemplate>
        </Pivot.HeaderTemplate>
        <Pivot.ItemTemplate>
            <DataTemplate>
                <ItemsControl ItemsSource="{Binding DeviceFiles}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding numBlocks}"/>
                                <TextBlock Text="{Binding syncTime}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </DataTemplate>
        </Pivot.ItemTemplate>
    </Pivot>
</Grid>

As soon as a Device_Info object gets added to the Devices observableCollection I get an error. Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED)). Then if I click in my MainPage.xaml file in the designer I see this:

TargetException: Object does not match target type.
StackTrace:
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, 
BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
InnerException: None

You can see from the xaml above that I've tried a number of ways to display the HarwareRevNumMajor property that lives on a Device_Info by the commented out TextBlock code.

My ViewModel looks like this:

namespace usb_test
{
    public class View_Model
    {
        public ObservableCollection<Device_Info> _devices;
        public ObservableCollection<File_Info> _deviceFiles;
        public CoreDispatcher _dispatcher;
    public View_Model() {
        _devices = new ObservableCollection<Device_Info>();
        _deviceFiles = new ObservableCollection<File_Info>();
    }

    public ObservableCollection<Device_Info> Devices
    {
        get
        {
            return _devices;
        }
    }

    public ObservableCollection<File_Info> DeviceFiles
    {
        get
        {
            return _deviceFiles;
        }
    }

In My MainPage.xaml.cs I have this:

namespace usb_test
{
    public sealed partial class MainPage : Page
    {
     Device_List devList = new Device_List();
     Device_Structure deviceStruct = new Device_Structure();
     View_Model viewModel = new View_Model();
     public MainPage()
     {
         this.InitializeComponent();
         viewModel._dispatcher = Dispatcher;
         this.DataContext = viewModel;
         devList.devices.CollectionChanged += this.OnCollectionChanged;
         deviceStruct.deviceFiles.CollectionChanged += this.OnCollectionChanged;
...

This line of code is what adds a device to the list:

await viewModel._dispatcher.RunAsync(CoreDispatcherPriority.Normal, async() => { devList.devices.Add(devInfo); });

This line succeeds and adds a Dev_Info object into the devices observableCollection and shortly after the application crashes.

I'm sure there are more errors when I try to display File Info stuff later in the xaml but I'd really appreciate just getting the pivot header to display correctly. Like I said I'm very new to this so I'm assuming there are a number of problems, I'd appreciate any advice/clarity into what is stopping me from not being able to display a property from a Dev_Info object.

Thanks!

c#
mvvm
data-binding
uwp-xaml
pivotitem
asked on Stack Overflow Aug 17, 2017 by fgv

2 Answers

0

Your items from Devices are of type Device_Info. In your DataTemplate your bind the text property to Device_Info.HardwareRevNumMajor. Since the context is your item (Device_Info), you try to access a property Device_Info.

If your write:

<TextBlock Text="{Binding HardwareRevNumMajor}" />

You try to access the property HardwareRevNumMajor wich probably exists in Device_Info.

answered on Stack Overflow Aug 18, 2017 by S. Spindler
0

Thanks for the suggestions above. I made some progress.

I rewrote my xaml like this:

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

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Pivot x:Name="pivot1" Title="Pivot" Opacity="0.99" ItemsSource="{x:Bind viewModel.Devices}">
        <Pivot.HeaderTemplate>
            <DataTemplate x:DataType="local:Device_Info">
                <TextBlock Text="{x:Bind HardwareRevNumMajor}"></TextBlock>
            </DataTemplate>
        </Pivot.HeaderTemplate>
        <Pivot.ItemTemplate>
            <DataTemplate x:DataType="local:Device_Info">
                <ListView ItemsSource="{x:Bind devStruct.deviceFiles}" Grid.Row="1">
                    <ListView.ItemTemplate>
                        <DataTemplate x:DataType="local:File_Info">
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Foreground="Red" Text="{x:Bind fileName}" HorizontalAlignment="Center" Margin="0,0,20,0"/>
                                <TextBlock Foreground="Red" Text="{x:Bind numBlocks}" HorizontalAlignment="Center" Margin="0,0,20,0"/>
                                <Button Content="Download File" Click="{x:Bind onDownloadClick}" HorizontalAlignment="Center" Margin="0,0,20,0">
                                </Button>
                            </StackPanel>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </DataTemplate>
        </Pivot.ItemTemplate>
    </Pivot>
</Grid>

You'll notice I took out the

<Page.DataContext>
  <local:View_Model/>
</Page.DataContext>

from my previous xaml code.

Then I used ItemsSource="{x:Bind viewModel.Devices}"> in the Pivot. viewModel is defined in my MainPage.xaml.cs and is an instance of the View_Model class I created above. Then from there I added a DataType to DataTemplate which is a Device_Info and then I could easily access an attribute of the Device_Info object, in this case HardwareRevNumMajor.

The PivotItemTemplate is a little more confusing. The class Device_Info has a property on it called devStruct which is a class I created (Device_Struct). An instance of Device_Struct has an ObservableCollection of File_Info objects. So this collection tells us the files on the device and it's called deviceFiles. That is the collection I wanted to use inside the Pivot Items. I'm not sure if there's a way to do this without inserting a <ListView> into the XAML but this is what I found works. With this I can now display information about the files on each Pivot Item.

I also switched from using Binding to x:Binding. To be honest I'm not sure exactly why that helped but when I used Binding I was getting: Object reference not set to an instance of an object. If anyone has a better understanding of that, I'd love to hear about it. Thanks again!

answered on Stack Overflow Aug 18, 2017 by fgv

User contributions licensed under CC BY-SA 3.0