Search code examples
c#wpfprismmefprism-4

Region or ItemsSource for large data set in ListBox


I'm having trouble figuring out what the best solution is given the following situation. I'm using Prism 4.1, MEF, and .Net 4.0.

I have an object Project that could have a large number (~1000) of Line objects. I'm deciding whether it is better to expose an ObservableCollection<LineViewModel> from my ProjectViewModel and manually create the Line viewmodels there OR set the ListBox as it's own region and activate views that way.

I'd still want my LineViewModel to have Prism's shared services (IEventAggregator, etc.) injected, but I don't know how to do that when I manually create the LineViewModel. Any suggestions or thoughts?

EDIT: My initial thoughts:

Project:

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

Solution

  • I could be way off base here, maybe this is too simple, but does this help you at all? I created a quick project which demonstrated a few basics. If you need more info maybe I can use it to help you more.

    Sample Application With Binding To "Lines"

    View

        <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 { };
        }
    }
    

    Model

    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 { };
        }
    }