1

I am trying to create popup with a generic filter for a list view. I want to use the parameter 'T' to cast a new object with. I have a TextBox and a ListView in a control. My 'BaseClass' has the shared property that multiple classes inherit from. In the below example, the classes City, Company, and Contact all inherit from BaseClass. These different classes are the ones being passed as T. My VM is declared as such:

public class SearchViewModel<T> : BaseViewModel
    where T : BaseClass, new()
{ 
    ... 
}

My ListView's Itemsource is bound to "Items"

public ICollectionView Items { get; set; }

My TextBox is bound to "SearchText". At present I have it setup as follows:

private string _SearchText;
public string SearchText
    {
        get => _SearchText;
        set
        {
            _SearchText = value;
            OnPropertyChanged(nameof(SearchText));

            if (string.IsNullOrEmpty(value))
                Items.Filter = null;
            else
            {
                var typedText = value.ToLower();
                switch (typeof(T).Name)
                {
                    case "City":
                        Items.Filter = new Predicate<object>(o => ((City)o).FilterByName.ToLower().Contains(typedText));
                        break;
                    case "Company":
                        Items.Filter = new Predicate<object>(o => ((Company)o).FilterByName.ToLower().Contains(typedText));
                        break;
                    case "Contact":
                        Items.Filter = new Predicate<object>(o => ((Contact)o).FilterByName.ToLower().Contains(typedText));
                        break;
                }
            }
        }
    }

I would like to do something like this:

private string _SearchText;    
public string SearchText
    {
        get => _SearchText;
        set
        {
            _SearchText = value;
            OnPropertyChanged(nameof(SearchText));

            if (string.IsNullOrEmpty(value))
                Items.Filter = null;
            else
            {
                Items.Filter = new Predicate<object>(o => ((T)o).FilterByName.ToLower().Contains(value.ToLower()));
            }
        }
    }

Doing like this, the compiler does not give any complaints. However, during runtime I get the following error:

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

Now the question: Is there a way to use 'T' to cast the class for the filter?

EDIT:

Adding photo of debug results based on Dave's suggestion DebugREsult

UPDATE:

1) Response to Duplicate: This is not a question about the Null reference, but how to cast a generic parameter as a class.

2) The error is thrown at

Items.Filter = new Predicate<object>(o => 
  ((T)o).FilterByName
        .ToLower()
        .Contains(value.ToLower()))
Red_Phoenix
  • 340
  • 3
  • 20
  • does it identify which line that exception was thrown from? – Dave Aug 31 '18 at 15:56
  • Items.Filter = new Predicate(o => ((T)o).FilterByName.ToLower().Contains(value.ToLower())) – Red_Phoenix Aug 31 '18 at 15:56
  • A bit of a random stab in the dark, but I did notice one difference you made between your two version. In the newer version your predicate is using value.ToLower. In the original version (the one that works) you put value.ToLower() in a local variable and use that. Try doing that in the second version and see what happends – Dave Aug 31 '18 at 15:58
  • @Dave I have also tried that way, same result. I was just trying to cut out as much code as I can. – Red_Phoenix Aug 31 '18 at 16:00
  • 1
    @Erik Phillips I dont think thats a fair duplicate flag. While yes it is a null reference exception, OP does I think have a valid question that isnt answered by 'What is a null ref exception' – Dave Aug 31 '18 at 16:01
  • you can put the body of the lambda in curley braces and you can execute multiple statements, which will allow you to null check all of the objects you access and find out which is null – Dave Aug 31 '18 at 16:02
  • @Dave in order for the question to be solved, someone has to *debug the code to find where the NullReferenceException is being thrown* . I don't see how not doing the exact same thing as the duplicate (Find the null reference exception location) doesn't solve this problem. – Erik Philips Aug 31 '18 at 16:14
  • Most likely, when he solves the NullReferenceException there will be another problem... that is again another question.. or he can remove the *Why is it throwing a NullReferenceException* and it wouldn't be a duplicate. Also your last comment is help with debugging *how do I fix a null reference exception*... the same thing as the duplicate. – Erik Philips Aug 31 '18 at 16:18
  • I just think the point of flagging a dupe, is to say, "here look, there's your answer already", however this won't give OP the answer or anyone who might come across this in future. I agree yes maybe with a bit more investigation the question would change to the root of the issue but we can try help OP get there – Dave Aug 31 '18 at 16:20
  • @Dave, using braces the (T)o is a System.Data.Entity.DynamicProxies.City, FilterByName gets set to null, then the error is thrown at ToLower(). I think that T should resolve to ProjectData.City as my database is in EF in a different project. – Red_Phoenix Aug 31 '18 at 16:22
  • If you're object O is of Type T or inherits or implements T, then the cast will work, if it isn't it will throw an invalid cast exception. So it isn't the cast. FilterByName is null for some reason. Casting it from being an object to a T won't nullify properties, I promise – Dave Aug 31 '18 at 16:24
  • @Dave, I edited with a photo of the debug window. – Red_Phoenix Aug 31 '18 at 16:34
  • FilterByName is null. You can't call methods on a null object... – Dave Aug 31 '18 at 16:36
  • I understand that. But I I do the call as (o => ((Contact)o).FilterByName.ToLower().Contains(typedText)) it works. But if I do it with the 'T' parameter instead of Contact it fails. I was just wanting to make this as universal as I could without having to have a lengthy switch statement. – Red_Phoenix Aug 31 '18 at 16:39
  • @ErikPhilips, The casting of the parameter T vs using the actual class is what makes it null. I can not see anything in that article that will help with solving on how to use 'T' as the class that is passed in. – Red_Phoenix Aug 31 '18 at 17:01
  • @Red_Phoenix You may want to try making a much smaller generic class here, and see that casting in this way actually works like you think it should. Basically, do a small test class, and if that works like you expect, then you figure out why the "larger" one doesn't. But beyond that, if all the classes inherit from the same base, couldn't they just all have that same property? Why are you casting at all? – Kevin Anderson Aug 31 '18 at 18:55

0 Answers0