Region или ItemsSource для большого набора данных в ListBox

У меня проблемы с выяснением того, какое лучшее решение дается в следующей ситуации. Я использую Prism 4.1, MEF и.Net 4.0.

У меня есть объект Project которые могут иметь большое количество (~1000) Line объекты. Я решаю, лучше ли выставить ObservableCollection<LineViewModel> от моего ProjectViewModel и вручную создайте там видовые модели Line ИЛИ установите ListBox в качестве своего собственного региона и активируйте представления таким образом.

Я все еще хотел бы мой LineViewModel сделать инъекцию общих служб Prism (IEventAggregator и т. д.), но я не знаю, как это сделать, когда я вручную создаю LineViewModel, Есть предложения или мысли?

РЕДАКТИРОВАТЬ: Мои первые мысли:

Проект:

public class Project
{
    public List<Line> Lines { get; set; }
}

ProjectViewModel:

[Export(typeof(ProjectViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ProjectViewModel : NotificationObject, IRegionMemberLifetime
{
    private Project _localProject;

    /* 
        HERE WILL BE SOME PROPERTIES LIKE COST, PRICE THAT ARE CUMULATIVE FROM THE Lines 
     */

    public ObservableCollection<LineViewModel> Lines { get; private set; }

    private readonly IEventAggregator _eventAggregator;

    [ImportingConstructor]
    public ProjectViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        _eventAggregator.GetEvent<ProjectLoaded>().Subscribe(SetupProject, false);
        Lines = new ObservableCollection<LineViewModel>();
    }

    private void SetupProject(Project project)
    {
        _localProject = project;

        foreach(var l in _localProject.Lines)
        {
            LineViewModel lvm = new LineViewModel(l);
            lvm.PropertyChanged += // Some handler here
            Lines.Add(lvm);
        }
    }

    public bool KeepAlive
    {
        get { return false; }
    }
}

LineViewModel:

public class LineViewModel : NotificationObject
{
    private Line _localLine;

    public decimal Cost
    {
        get { return _localLine.Cost; }
        set
        {
            _localLine.Cost = value;
            RaisePropertyChanged(() => Cost);
        }
    }

    public LineViewModel(Line incoming)
    {
        _localLine = incoming;
    }
}

2 ответа

Решение

Я мог бы быть далеко от базы, может быть, это слишком просто, но поможет ли это вам вообще? Я создал быстрый проект, который продемонстрировал несколько основ. Если вам нужна дополнительная информация, может быть, я могу использовать ее, чтобы помочь вам больше.

Пример приложения с привязкой к "линиям"

Посмотреть

    <Window x:Class="WpfApplication1.LinesView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="LinesView" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="247" d:DesignWidth="348" SizeToContent="WidthAndHeight" Width="350" Height="250">
    <Window.Resources>
        <DataTemplate x:Key="LineView">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto" MinWidth="50"/>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Row="0" Grid.Column="0" Text="Line: " />
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />

                <TextBlock Grid.Row="1" Grid.Column="0" Text="X: " />
                <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding X}" />

                <TextBlock Grid.Row="2" Grid.Column="0" Text="Y: " />
                <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Y}" />
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="Total Cost" Margin="5" />
            <TextBlock Text="{Binding Cost}" Margin="5" />
        </StackPanel>
        <ContentControl Name="contentControl1" Content="{Binding ElementName=listBox1, Path=SelectedItem}" ContentTemplate="{StaticResource LineView}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="105" Margin="5" />
        <ListBox Height="234" 
                 HorizontalAlignment="Center"
                 Name="listBox1" 
                 VerticalAlignment="Center"
                 ItemsSource="{Binding Lines}"
                 ItemTemplate="{StaticResource LineView}" Width="152" Margin="5" />
    </StackPanel>
</Window>

ViewModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WpfApplication1.Models;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace WpfApplication1
{
    public class LinesViewModel : INotifyPropertyChanged
    {
        public int Cost
        {
            get
            {
                return Lines.Sum(x => x.X + x.Y); 
            }
        }

        public ObservableCollection<Line> Lines
        {
            get;
            private set;
        }

        public LinesViewModel()
        {
            Lines = new ObservableCollection<Line>();
            Lines.Add(new Line()
            {
                Name = "Line1",
                X = 0,
                Y = 1
            });
            Lines.Add(new Line()
            {
                Name = "Line2",
                X = 1,
                Y = 1
            });
            Lines.Add(new Line()
            {
                Name = "Line3",
                X = 2,
                Y = 2
            });

            foreach(Line line in Lines)
            {
                line.XChanged += new EventHandler(lineChanged);
                line.YChanged += new EventHandler(lineChanged);
            }

            Lines.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Lines_CollectionChanged);
        }

        private void Lines_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (Line line in e.NewItems)
                {
                    line.XChanged += new EventHandler(lineChanged);
                    line.YChanged += new EventHandler(lineChanged);
                }
            }
            if (e.OldItems != null)
            {
                foreach (Line line in e.OldItems)
                {
                    line.XChanged -= new EventHandler(lineChanged);
                    line.YChanged -= new EventHandler(lineChanged);
                }
            }
        }

        private void lineChanged(object sender, EventArgs e)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("Cost"));
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    }
}

модель

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WpfApplication1.Models
{
    public class Line
    {
        private int x;
        private int y;

        public String Name { get; set; }

        public int X
        {
            get
            {
                return x;
            }
            set
            {
                x = value;
                XChanged(this, EventArgs.Empty);
            }
        }

        public int Y
        {
            get
            {
                return y;
            }
            set
            {
                y = value;
                YChanged(this, EventArgs.Empty);
            }
        }

        public event EventHandler XChanged = delegate { };
        public event EventHandler YChanged = delegate { };
    }
}

Чтобы вручную создать ваши LineViewModels с помощью Prism/MEF, вы можете использовать контейнер для разрешения зависимостей, для чего он и нужен.

Например,

LineViewModel line = container.GetExportedValue<LineViewModel>();

См. Эту ссылку: Управление зависимостями: разрешение экземпляров с помощью MEF.

Я немного обеспокоен вашим дизайном, действительно ли необходимо, чтобы каждая из ваших строк имела ViewModel и создавалась контейнером, а зависимости вводились? Возможно ли, что мог быть один объект, который управляет всеми строками и имеет эти введенные зависимости? Возможно, какая-то модель репозитория может принести вам пользу?

Это может привести к значительным накладным расходам, если вы разрешаете тысячи объектов через контейнер. Книга Prism также упоминает, что это не может быть хорошей идеей. Соображения по использованию контейнера

Другие вопросы по тегам