Reusing Binding Collections for WPF

回眸只為那壹抹淺笑 提交于 2019-12-10 15:59:44

问题


I am working on a WPF app using the MVVM patterm, which I am learning. It uses EF4. I am trying to use a similar tabbed document interface style; several combo boxes on these tabs have the same items sources (from a sql db). Since this data almost never changes, it seemed like a good idea to make a repository object to get them when the app starts, and just reuse them for each viewmodel. For whatever reason though, even though I use new in the constructors, the lists are connected.

If I set a bound combo box on one tab, it gets set on another (or set when a new tab is created). I don't want this to happen, but I don't know why does.

The repository object is initialized before anything else, and just holds public lists. The views simply use items source binding onto the ObservableCollection. I am using the ViewModelBase class from the article. Here is the Viewmodel and model code.

ViewModel

TicketModel _ticket;

    public TicketViewModel(TableRepository repository)
    {
        _ticket = new TicketModel(repository);
    }

    public ObservableCollection<Customer> CustomerList
    {
        get { return _ticket.CustomerList; }
        set
        {
            if (value == _ticket.CustomerList)
                return;

            _ticket.CustomerList = value;

            //base.OnPropertyChanged("CustomerList");
        }
    }

Model

public ObservableCollection<Customer> CustomerList { get; set; }

    public TicketModel(TableRepository repository)
    {
        CustomerList = new ObservableCollection<Customer>(repository.Customers);
    }

EDIT: I am sure this is the wrong way to do this, I am still working on it. Here is the new model code:

        public TicketModel(TableRepository repository)
    {
        CustomerList = new ObservableCollection<Customer>((from x in repository.Customers
                                                           select
                                                               new Customer
                                                               {
                                                                   CM_CUSTOMER_ID = x.CM_CUSTOMER_ID,
                                                                   CM_FULL_NAME = x.CM_FULL_NAME,
                                                                   CM_COMPANY_ID = x.CM_COMPANY_ID
                                                               }).ToList());
    }

This causes a new problem. Whenever you change tabs, the selection on the combo box is cleared.

MORE EDITS: This question I ran into when uses Rachels answer indicates that a static repository is bad practice because it leaves the DB connection open for the life of the program. I confirmed a connection remains open, but it looks like one remains open for non-static classes too. Here is the repository code:

using (BT8_Entity db = new BT8_Entity())
        {
            _companies = (from x in db.Companies where x.CO_INACTIVE == 0 select x).ToList();
            _customers = (from x in db.Customers where x.CM_INACTIVE == 0 orderby x.CM_FULL_NAME select x).ToList();
            _locations = (from x in db.Locations where x.LC_INACTIVE == 0 select x).ToList();
            _departments = (from x in db.Departments where x.DP_INACTIVE == 0 select x).ToList();
            _users = (from x in db.Users where x.US_INACTIVE == 0 select x).ToList();
        }

        _companies.Add(new Company { CO_COMPANY_ID = 0, CO_COMPANY_NAME = "" });
        _companies.OrderBy(x => x.CO_COMPANY_NAME);

        _departments.Add(new Department { DP_DEPARTMENT_ID = 0, DP_DEPARTMENT_NAME = "" });
        _locations.Add(new Location { LC_LOCATION_ID = 0, LC_LOCATION_NAME = "" });

However, now I am back to the ugly code above which does not seem a good solution to copying the collection, as the Customer object needs to be manually recreated property by property in any code that needs its. It seems like this should be a very common thing to do, re-using lists, I feel like it should have a solution.


回答1:


Custom objects, such as Customer get passed around by reference, not value. So even though you're creating a new ObservableCollection, it is still filled with the Customer objects that exist in your Repository. To make a truly new collection you'll need to create a new copy of each Customer object for your collection.

If you are creating multiple copies of the CustomerList because you want to filter the collection depending on your needs, you can use a CollectionViewSource instead of an ObservableCollection. This allows you to return a filtered view of a collection instead of the full collection itself.

EDIT

If not, have you considered using a static list for your ComboBox items, and just storing the SelectedItem in your model?

For example,

<ComboBox ItemsSource="{Binding Source={x:Static local:Lists.CustomerList}}"
          SelectedItem="{Binding Customer}" />

This would fill the ComboBox with the ObservableCollection<Customer> CustomerList property that is found on the Static class Lists, and would bind the SelectedItem to the Model.Customer property

If the SelectedItem does not directly reference an item in the ComboBox's ItemsSource, you need to overwrite the Equals() of the item class to make the two values equal the same if their values are the same. Otherwise, it will compare the hash code of the two objects and decide that the two objects are not equal, even if the data they contain are the same. As an alternative, you can also bind SelectedValue and SelectedValuePath properties on the ComboBox instead of SelectedItem.



来源:https://stackoverflow.com/questions/6985283/reusing-binding-collections-for-wpf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!