重用 WPF 的绑定集合

发布于 2024-11-28 15:12:22 字数 3261 浏览 0 评论 0原文

我正在使用 MVVM 模式开发 WPF 应用程序,我正在学习该模式。 。它使用 EF4。我正在尝试使用类似的选项卡式文档界面风格;这些选项卡上的几个组合框具有相同的项目源(来自 SQL 数据库)。由于这些数据几乎永远不会改变,因此创建一个存储库对象来在应用程序启动时获取它们似乎是个好主意,然后为每个视图模型重用它们。不管出于什么原因,即使我在构造函数中使用 new,列表也是相连的。

如果我在一个选项卡上设置绑定组合框,它将在另一个选项卡上设置(或在创建新选项卡时设置)。我不希望这样的事情发生,但我不知道为什么会这样。

存储库对象在其他任何事情之前被初始化,并且只保存公共列表。这些视图只是使用绑定到 ObservableCollection 上的项目源。我正在使用本文中的 ViewModelBase 类。这是视图模型和模型代码。

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");
        }
    }

模型

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

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

编辑:我确信这是错误的方法,我仍在努力。这是新模型代码:

        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());
    }

这会导致新问题。每当您更改选项卡时,组合框中的选择都会被清除。

更多编辑: 这个问题我遇到过Rachels 的回答表明静态存储库是不好的做法,因为它使数据库连接在程序的生命周期中保持开放状态。我确认连接保持打开状态,但看起来对于非静态类也保持打开状态。这是存储库代码:

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 = "" });

但是,现在我又回到了上面的丑陋代码,这似乎不是复制集合的好解决方案,因为需要在任何需要它的代码中手动重新创建 Customer 对象的属性。看起来这应该是一个很常见的事情,重新使用列表,我觉得应该有一个解决方案。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

定格我的天空 2024-12-05 15:12:22

自定义对象(例如 Customer)通过引用而不是值传递。因此,即使您正在创建一个新的 ObservableCollection,它仍然充满存储库中存在的 Customer 对象。要创建一个真正的新集合,您需要为集合中的每个 Customer 对象创建一个新副本。

如果您因为要根据需要过滤集合而创建 CustomerList 的多个副本,则可以使用 CollectionViewSource 而不是 ObservableCollection。这允许您返回集合的过滤视图,而不是完整集合本身。

编辑

如果没有,您是否考虑过为 ComboBox 项目使用静态列表,并仅将 SelectedItem 存储在模型中?

例如,

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

这将使用 ObservableCollection填充 ComboBox。 CustomerList 属性位于静态类 Lists 上,并将 SelectedItem 绑定到 Model.Customer 属性

如果 SelectedItem不直接引用 ComboBox 的 ItemsSource 中的项目,如果它们的值为相同的。否则,它将比较两个对象的哈希码并判定这两个对象不相等,即使它们包含的数据相同。作为替代方案,您还可以在 ComboBox 上绑定 SelectedValueSelectedValuePath 属性,而不是 SelectedItem

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文