2
votes

I have a list view where I bind multiple data templates to using a DataTemplateSelector. However, I cannot build my project, I'm getting an error of "Object reference not set to an instance of an object". I narrowed down the issue to the data binding ItemsSource of the combobox. If I comment out the combobox, my project will build. As I understand, the Binding keyword evaluates at runtime, but this is a compile time error. I tried to get around the error by swapping out to x:Bind, as that evaluates at compile-time but then that required the type of the DataTemplate to be defined.

x:DataType={x:Type templates:FilterDropdownDataTemplate}

However, x:Type isn't defined in a Windows 10 universal app. Any suggestions?

Here is my XAML:

<Page.Resources>
    <DataTemplate x:Name="DateComboboxTemplate">
        <TextBlock Text="{Binding}"/>
    </DataTemplate>

    <DataTemplate x:Name="FilterDropdownDataTemplate">
        <StackPanel>
            <TextBlock Text="{Binding Value}"/>
            <ComboBox ItemsSource="{Binding DateRanges}"
                SelectionChanged="{Binding DateSelection}"
                SelectedItem="{Binding SelectedDateRange, Mode=TwoWay}"
                ItemTemplate="{StaticResource DateComboboxTemplate}">
            </ComboBox>
        </StackPanel>
    </DataTemplate>

    <config:MainFilterTemplateSelector
        x:Name="MainFilterSelector"
        FilterDropdownTemplate="{StaticResource FilterDropdownDataTemplate}"
        />
</Page.Resources>

<ListView x:Name="FilterCategories"
          Margin="0"
          Padding="0"
          BorderThickness="0"
          ItemsSource="{Binding Filters}"
          SelectedItem="{Binding SelectedFilterCategory, Mode=TwoWay}"
          IsItemClickEnabled="True"
          SelectionChanged="{x:Bind ViewModel.OpenSubFilters}"
          ItemTemplateSelector="{StaticResource MainFilterSelector}">
</ListView>

And here is the relevant C# code (I'm using a MVVM pattern):

public class MainFilterViewDropdownListTemplate : Filter
{
    private ObservableCollection<string> _dateRanges;
    private string _selectedDateRange;

    public MainFilterViewDropdownListTemplate() : base()
    {
        _dateRanges = new ObservableCollection<string>();
    }

    public ObservableCollection<string> DateRanges
    {
        get
        {
            return _dateRanges;
        }
        set
        {
            SetProperty(ref _dateRanges, value);
            // hard coding these 4 lines for testing for now
            _dateRanges.Add("Last 7 days");
            _dateRanges.Add("Last 14 days");
            _dateRanges.Add("Last 30 days");
            _dateRanges.Add("Custom");
        }
    }

    public string SelectedDateRange
    {
        get
        {
            return _selectedDateRange;
        }
        set
        {
            SetProperty(ref _selectedDateRange, value);
        }
    }

    public IEnumerable<Filter> AllFilters { get; set; }

    public void InitializeFields(Filter filter)
    {
        Column = filter.Column;
        DisplayType = filter.DisplayType;
        ID = filter.ID;
        IsSelected = filter.IsSelected;
        ParentId = filter.ParentId;
        SelectedCount = filter.SelectedCount;
        TotalCount = filter.TotalCount;
        Value = filter.Value;
        visibility = filter.visibility;
    }

    private void DateSelection()
    {

    }
}

And

public class MainFilterTemplateSelector : DataTemplateSelector
{
    public DataTemplate FilterDropdownTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object selector)
    {
        if (selector == null)
        {
            throw new ArgumentNullException("Template is null");
        }
        if (!(selector is FilterBase))
        {
            throw new ArgumentException("This list can only be populated by a class that extends Filter.");
        }
        else
        {
            var filterType = selector as Filter;
            switch (filterType.DisplayType)
            {
                case "date":
                    return FilterDropdownTemplate;
                default:
                    return base.SelectTemplateCore(selector);
            }
        }
    }
}

And

public class Filter : FilterBase
{
    private int _selectedCount;
    private int _totalCount;
    private Visibility _visibility;

    public IEnumerable<Filter> Children
    {
        get;
        set;
    }

    public int TotalCount
    {
        get
        {
            return _totalCount;
        }
        set
        {
            SetProperty(ref _totalCount, value);
        }
    }

    public int SelectedCount
    {
        get
        {
            return _selectedCount;
        }
        set
        {
            SetProperty(ref _selectedCount, value);
        }
    }

    public Visibility visibility
    {
        get
        {
            return _visibility;
        }
        set
        {
            SetProperty(ref _visibility, value);
        }

    }

    public void InitializeAllChildren(IEnumerable<Filter> allFilters)
    {
        foreach (Filter item in allFilters)
        {
            item.Children = allFilters.GetAllChildrenFiltersForParentID(item.ID) as IEnumerable<Filter>;
            if (item.Children == null)
            {
                item.TotalCount = 0;
            }
            else
            {
                item.TotalCount = item.Children.Count();
                item.SelectedCount = item.Children.Where(t => t.IsSelected.Value).Count();
            }
        }
        Children = allFilters.GetAllChildrenFiltersForParentID(ID);
    }
}

And

public class FilterBase : ViewModelBase
{
    public int ID
    {
        get
        {

            return _id;
        }
        set
        {
            SetProperty(ref _id, value);
        }
    }



    public string Value
    {
        get
        {
            return _value;
        }

        set
        {
            SetProperty(ref _value, value);
        }
    }
    public string Column
    {
        get
        {
            return _column;
        }

        set
        {
            SetProperty(ref _column, value);
        }
    }

    public int ParentId
    {
        get
        {
            return _parentId;
        }

        set
        {
            SetProperty(ref _parentId, value);
        }
    }

    public string DisplayType
    {
        get
        {
            return _displayType;
        }

        set
        {
            SetProperty(ref _displayType, value);
        }
    }
    public bool? IsSelected
    {
        get
        {
            return _isSelected;
        }

        set
        {
            SetProperty(ref _isSelected, value);
        }
    }

    private int _id;
    private string _value;
    private string _column;
    private int _parentId;
    private string _displayType;
    private bool? _isSelected;
}

And (BindableBase is from the Prism.Mvvm namespace)

public class ViewModelBase : BindableBase, IDisposable
{
    PropertyChangeActionHelper _propertyChangeActionHelper;

    protected void RegisterPropertyChangeAction(Action action, params string[] monitoredProperties)
    {
        if (_propertyChangeActionHelper == null)
        {
            _propertyChangeActionHelper = new PropertyChangeActionHelper(this);
        }

        _propertyChangeActionHelper.RegisterPropertyChangeAction(action, monitoredProperties);
    }

    #region IDisposable Support

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_propertyChangeActionHelper != null)
            {
                _propertyChangeActionHelper.Dispose();
                _propertyChangeActionHelper = null;
            }
        }
    }

    ~ViewModelBase()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(false);
    }

    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion
}
1
Is it really a build error? I.e. a fatal error that prevents the executable from building? WPF often emits non-fatal errors that occur simply because the editor is trying to evaluate something that's not set up at design-time. In any case, you may find you have better luck getting an answer if you provide a better code example (e.g. you've left out two of the templates referenced in the XAML), and if you can provide a repro case with a basic Windows Store app instead of universal (not everyone has upgraded to a Windows 10-compatible dev environment). - Peter Duniho
Yes, it does block the code from building. I removed the extra templates and added the classes that populate the list and its inheritance tree. - Kevin Tsang
Concerning x:Bind and x:DataType: Assuming that the templates namespace is declared and FilterDropdownDataTemplate is a type within that namespace, you should be able to define the DataTemplate's data type without using x:Type: <DataTemplate x:DataType="templates:FilterDropdownDataTemplate"> - andreask
Switching back to x:Bind and using your suggested syntax for the x:DataType worked, thanks! - Kevin Tsang
@andreask: since your comment led the OP to a solution, you should IMHO post your advice as an actual answer. This will ensure that other readers can easily find your answer, and that the question no longer appears unanswered (especially if the OP clicks the accept button for your answer, once posted). - Peter Duniho

1 Answers

0
votes

Assuming that the templates namespace is declared and FilterDropdownDataTemplate is a type within that namespace, you should be able to define the DataTemplate's data type without x:Type using the following syntax:

<DataTemplate x:DataType="templates:FilterDropdownDataTemplate">