简体   繁体   中英

How to fix “Object reference not set to an instance of object.” when binding an event to an element generated by ListView ItemsSource when using MVVM?

I have a ListView with an ItemsSource set to a kind of Movies collection (with Movie objects). And then inside I have a DataTemplate which contains a ComboBox and a TextBlock. On the ComboBox I bind the Selected item to the progress (of episodes) that the Movie object contains. Here I'm using normal "Binding" method. But the problem arrives when I want to bind a method to the "DropDownClosed" event. This works fine when using Code Behind, but using the ViewModel and x:Bind it gives me "Object reference not set to an instance of an object." when building my app.

The goal is to Bind to a method in the ViewModel, and not the Code Behind without the error.

Everything works smooth when i take away the "DropDownClosed" event. And no other Events works any better.

<ListView ItemsSource="{x:Bind ViewModel.MovieLibrary}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Spacing="20" Orientation="Horizontal">
                <ComboBox SelectedItem="{Binding Progress}"
                                          ItemsSource="{Binding Media.Episodes, Converter={StaticResource NumberToArrayConverter}}"
                                          DropDownClosed="{x:Bind ViewModel.UpdateStuff}">
                </ComboBox>
                <TextBlock Text="{Binding Media.Title}" TextWrapping="Wrap" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

And this, but this works just as bad if it's empty so idk if this is necessary (from view model) :

public void UpdateStuff(object sender, object e)
        {
            //blabla not important
        }

The problem is I can't use x:Bind at all to any property.

To use x:Bind in the DataTemplate you must specify x:DataType="local:Movie" in the DataTemplate. The path in x:Bind within the template should then be relative to the Movie object.

The "object reference not set" just means you have not assigned a pointer. Perhaps either ViewModel is null or ViewModel.UpdateStuff is null.

I try to present you now a solution with a small example. You will see that some things changed. I hope It will still fit your need:

    <ListView ItemsSource="{Binding MovieLibrary}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <ComboBox  ItemsSource="{Binding Episodes}" 
                               SelectedItem="{Binding Progress}" >
                    </ComboBox>
                    <TextBlock Text="{Binding Title}" TextWrapping="Wrap" />
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

and that is a example view model:

public class MainWindowModel
{
    public MainWindowModel()
    {
        MovieLibrary = new ObservableCollection<Media>();

        //
        //Exmaple Fill
        //
        var m1 = new Media() { Title = "Breaking Bad", Episodes = new List<string> { "1", "2", "3", "4", "5", "6" } };
        var m2 = new Media() { Title = "The Big Bang Theory", Episodes = new List<string> { "1", "2", "3" } };
        m1.MediaChangedAction += OnMediaChanged;
        m2.MediaChangedAction += OnMediaChanged;
        MovieLibrary.Add(m1);
        MovieLibrary.Add(m2);
    }
    public ObservableCollection<Media> MovieLibrary { get; set; }

    private void OnMediaChanged(Media movie)
    {
        // do something
    }

}

public class Media
{
    public event Action<Media> MediaChangedAction;

    public Media()
    {
    }

    public string Title { get; set; }
    public List<string> Episodes { get; set; }


    private string _progress;
    public string Progress
    {
        get { return _progress; }
        set
        {
            _progress = value;
            MediaChangedAction?.Invoke(this);
        }
    }
}

You see I simplified it a little bit (no converter etc.). Also the "event" is moved to the view model. As for me the advantage is, that it now would not depend on the view to call the event, it's just for view model important. So in case you make a different view to show the Media, it would still work well.

I kept away also the "two way" stuff, as for the moment there is no need to send from view model to the view. If needed, "Media" would need INotifyPropertyChanged implemented.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM