Create a DependencyPropery with Observable Collect

2019-08-15 12:51发布

This question already has an answer here:

I have WPF application where I am following MVVM. I have a class called Session as follows: Session.cs

 public class Session:ObservableCollection<Session>
    {
        public int value { get; set; }
        public string name { get; set; }



    }

    public class CustomSession:DependencyObject
    {
        public static readonly DependencyProperty SessionCollectionProperty =
            DependencyProperty.Register("SessionCollection", typeof(Session), typeof(CustomSession), new PropertyMetadata());

        public Session SessionCollection
        {
            get { return (Session)GetValue(SessionCollectionProperty); }
            set { SetValue(SessionCollectionProperty, value); }
        }


    }

I have ViewModel as follows: ViewModel.cs

 public class ViewModel:BindableBase
{
    private ObservableCollection<Session> _sessions;
            public ObservableCollection<Session> sessionsCollection
            {
                get { return _sessions; }
                set { SetProperty(ref _sessions, value); }
            }

    public ViewModel()
            {
    sessionsCollection = allSessions();
    }

    public ObservableCollection<Session> allSessions()
            {
                CustomSession custom = new CustomSession();
                custom.SessionCollection.Add(new Session() { name = "LocateSession", value = 10 }); //System.Null Reference Exception.
                custom.SessionCollection.Add(new Session() { name = "TrackSession", value = 20 });
                custom.SessionCollection.Add(new Session() { name = "MonitorSession", value = 25 }); 
                custom.SessionCollection.Add(new Session() { name = "MassSnapshot", value = 18 });
                custom.SessionCollection.Add(new Session() { name = "MassContinuous", value = 9 });
                return custom.SessionCollection;
            }
}

I have a UI where I want to bind this Observable Collection. Whenever I try to add in an item like custom.SessionCollection.Add(new Session() { name = "LocateSession", value = 10 }); I get Null Reference exception. I want to populate the ObservableCollection from the ViewModel. How do I do it. Please help.

标签: c# wpf mvvm
3条回答
SAY GOODBYE
2楼-- · 2019-08-15 13:17

I don't think that not initializing the _sessions field caused the null error. You have never directly used that field(or property). It was only assigned so can't cause null exception.

Your problem was that your DependencyProperty SessionCollection doesn't have a default value. So when line custom.SessionCollection.Add(..) gets executed you get a null exception. AS SessionCollection will have null default value.

Try to change your property like this(In PropertyMetadata constructor give a default value, as I have given a new object (new Session())):

public static readonly DependencyProperty SessionCollectionProperty =
        DependencyProperty.Register(  "SessionCollection"
                                    , typeof(Session)
                                    , typeof(CustomSession)
                                    , new PropertyMetadata(new Session())
                                    );

    public Session SessionCollection
    {
        get { return (Session)GetValue(SessionCollectionProperty); }
        set { SetValue(SessionCollectionProperty, value); }
    }

See below where the exception occurs:

enter image description here

查看更多
相关推荐>>
3楼-- · 2019-08-15 13:25

First of all your Null reference exception will go away if you follow the correct mvvm approach which i'll describe (partially) below

MODEL

Following MVVM enforces that only you presentation layer knows about WPF. The exception i accept is the ObservableCollection type

Your Session model is fine with the ObservableCollection property but I would call it Sessions. On the other hand i see no reason to have CustomSession at all - try to remove it

public class Sessions:ObservableCollection<Session>
    {
        public int value { get; set; }
        public string name { get; set; }
    }

VIEW MODEL

It knows about your Sessions type but it must not know about what is a DependencyObject.

In the code below i often leave sessionsCollection property as a getter only. If I implement the set i always follow the INotifyPropertyChanged pattern

public class ViewModel: BindableBase
{
    private Sessions _sessions;
    public Sessions sessionsCollection
    {
       get { return _sessions; }
    }

    public Session SelectedSession
    {
        get;set; //properly implemented with the INotifyPropertyChanged
    }

    public ViewModel()
    {
      sessionsCollection = allSessions();
    }

    public Sessions allSessions()
    {
                var custom = new Sessions();//notice this is already a collection
                custom.Add(new Session() { name = "LocateSession", value = 10 }); //System.Null Reference Exception.
                custom.Add(new Session() { name = "TrackSession", value = 20 });
                custom.Add(new Session() { name = "MonitorSession", value = 25 }); 
                custom.Add(new Session() { name = "MassSnapshot", value = 18 });
                custom.Add(new Session() { name = "MassContinuous", value = 9 });
                return custom;
            }
}

VIEW

In the WPF simply bind some combobox, listbox, etc to the Sessions property. I don't have wpf right here so the syntax might be wrong.

<ListBox Items={Binding sessionsCollection} SelectedItem={Binding SelectedSession} />

I'd like to add that although the original question only referred to NRE it only happened because it was exposing DependencyObject in the wrong scope. I truly believe my refactoring helps to understand the simplicity (and advantages) of MVVM

查看更多
劫难
4楼-- · 2019-08-15 13:42

If you are trying to fill this from code (which I assume you do) the problem is that you never initialized _sessions. So just do this simple code exchange:

// old
// private ObservableCollection<Session> _sessions;

// now
private ObservableCollection<Session> _sessions = new ObservableCollection<Session>();

If you do not initialize _sessions it will have no object its referencing to. This is what the NULL reference means. If you try to access it anyway you will run into an error because it does not know which object you mean.

Edit

I think I got confused with the different variables here. As mentioned in the comments this should actually not solve the problem as you are accessing a different property. However: The general problem stays the same (accessing a collection that has not been initialized). To solve that problem I would recommend the solution proposed by @Kylo-Ren

查看更多
登录 后发表回答