Combobox' selecteditem set in xaml but bound property is null

0

I would like to train in c# and I created a WPF project in which are displayed some persons(with each a name and a work), and by selecting a field ('name' or 'work') and a search string, I would get all the persons filtered. for example, the field is 'name', the string is 'tin' and the only person whose name contains 'tin' is 'tintin'. I wrote all the program but I get a null reference error with the search engine(the parameterized class whose purpose is to filter the persons).

here is the person class:

namespace VueEnPremier.Model
{
    public class Person : ViewModelBase
    {
        public string Name { get; set; }

        public string Work { get; set; }

    }
}

here is my view:

<Window
        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"
        xmlns:local="clr-namespace:VueEnPremier"
        xmlns:vm="clr-namespace:VueEnPremier.ViewModel"
        xmlns:System="clr-namespace:System;assembly=mscorlib" x:Class="VueEnPremier.MainWindow"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <!--<vm:VMSearchName x:Key="ByName" />
        <vm:VMSearchWork x:Key="ByWork"/>-->
        <vm:VMSearch x:Key="ByName" NameOfTypeToSearch="Name"/>
        <vm:VMSearch x:Key="ByWork" NameOfTypeToSearch="Work"/>

    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource Locator}}">
        <StackPanel>
            <StackPanel x:Name="liste">
                <ComboBox x:Name="comboBox" Width="150" HorizontalAlignment="Left" Margin="10,10,0,10" 
                          SelectedItem="{Binding Main.SearchEngine, Mode=OneWayToSource}">
                    <ComboBoxItem Content="{StaticResource ByName}" HorizontalAlignment="Center" IsSelected="True"/>
                    <ComboBoxItem Content="{StaticResource ByWork}" HorizontalAlignment="Center"/>

                </ComboBox>
                <TextBox Text="{Binding Main.ToFind, Mode=OneWayToSource}"></TextBox>
                <ItemsControl 
                    ItemsSource="{Binding ElementName=comboBox,Path=SelectedItem.(vm:VMSearch.PersonsFiltered)}"/>
            </StackPanel>

        </StackPanel>
    </Grid>

</Window>

here is my search engine:

namespace VueEnPremier.ViewModel
{
    public class VMSearch : ViewModelBase
    {
        public string ToFind { get; set; }

        public string NameOfTypeToSearch { get; set; }


        public VMSearch(string nameOfTypeToSearch)
        {
            NameOfTypeToSearch = nameOfTypeToSearch;
        }

        public VMSearch() { }

        protected List<Person> Persons { set; get; }


        public void UpdatesDatas(List<Person> persons, string toFind)
        {
            Persons = persons;
            ToFind = toFind;

            RaisePropertyChanged(() => this.PersonsFiltered);

        }

        public override string ToString() => NameOfTypeToSearch;

        public List<Person> PersonsFiltered
        {
            get
            {
                return Persons?.Where(c => (c.GetType().GetProperty(NameOfTypeToSearch).GetValue(c,null) as string).Contains(ToFind)).ToList();
            }
        }

    }
}

The getter of PersonsFiltered is a little tricky, it gets the value of a property from the string name of this property ("Name" --> c.Name is obtained). The point is that in XAML, the combobox contains directly the 2 search engines, and the selectedItem property should be binded (one way to source) to the SearchEngine property of the viewmodel: all the logic stands in the bindings.

and finally here is my viewmodel:

using System.Collections.Generic;
using System.Windows.Documents;
using System.Windows.Media.Animation;
using GalaSoft.MvvmLight;
using VueEnPremier.Model;

namespace VueEnPremier.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {

        public List<Person> AllNames { get; set; }

        #region ToFind

        private string _toFind = string.Empty;


        public string ToFind
        {
            get => _toFind;
            set
            {
                _toFind = value;
                RaisePropertyChanged(()=>this.ToFind);
                SearchEngine.UpdatesDatas(AllNames,value);
            }
        }



        #endregion


        #region SearchEngine

        private VMSearch _searchEngine;


        public VMSearch SearchEngine
        {
            get { return _searchEngine; }
            set
            {
                if (value != _searchEngine)
                    _searchEngine = value;
                RaisePropertyChanged("SearchEngine");
            }
        }
        #endregion


        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            ////if (IsInDesignMode)
            ////{
            ////    // Code runs in Blend --> create design time data.
            ////}
            ////else
            ////{
            ////    // Code runs "for real"
            ////}

            Fill();

        }

        private void Fill()
        {
            AllNames = new List<Person>();
            AllNames.Add(new Person() {Name = "sanzot", Work = "boucher"});
            AllNames.Add(new Person() {Name = "buck dany", Work = "pilote"});
            AllNames.Add(new Person() {Name = "lefuneste", Work = "cuistre"});
            AllNames.Add(new Person() {Name = "tintin", Work = "reporter"});
            AllNames.Add(new Person() {Name = "blake", Work = "pilote"});

        }
    }
}

I use MVVM light. The error I get is, at startup :

System.NullReferenceException HResult=0x80004003 Message=La référence d'objet n'est pas définie à une instance d'un objet.
Source=VueEnPremier Arborescence des appels de procédure : à VueEnPremier.ViewModel.MainViewModel.set_ToFind(String value) dans C:\Users\osain\source\repos\MVVMBook\VueEnPremier\ViewModel\MainViewModel.cs :ligne 38

it says that in the line with 'searchEngine.updateDatas(AllNames,value);' the searchEngine variable is null.

thank you.

c#
wpf
xaml
data-binding
asked on Stack Overflow Feb 6, 2020 by lolveley

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0