none
NullReferenceException with PagedCollectionView

    Question

  • I inherited an internal SL app which has just been converted to SL5 from SL3.  I'm trying to filter a DataGrid with a textbox that contains a date that lets users press navigation buttons for first, previous, next and last date.  It uses the textbox's TextChanged event to apply the filter to the DataGrid via PagedCollectionView.  Using linq to sql and WCF RIA Services on the data side with sql server 2008.  If it matters, this is all in a TabItem of a TabControl.  Here's the xaml:

        <Grid>
    	<Grid.ColumnDefinitions>
    	    <ColumnDefinition />
    	</Grid.ColumnDefinitions>
    
    	<Grid.RowDefinitions>
    	    <RowDefinition Height="Auto"/>
    	    <RowDefinition Height="*" />
    	</Grid.RowDefinitions>
    
    	<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10,0,10">
    	    <Button x:Name="btnFirst" Click="btnFirstWAPT_Click" Content="|&lt;" Margin="0,0,5,0" />
    	    <Button x:Name="btnPrevious" Click="btnPreviousWAPT_Click" Content="&lt;" Margin="0,0,5,0" />
    	    <TextBox Width="78" x:Name="dateWAPT" TextChanged="dateWAPT_TextChanged" Text="" Grid.Row="1" Grid.Column="1" Margin="0,0,5,0" />
    	    <Button x:Name="btnNext" Click="btnNextWAPT_Click" Content="&gt;" Margin="0,0,5,0" />
    	    <Button x:Name="btnLast" Click="btnLastWAPT_Click" Content="&gt;|" Margin="0,0,15,0" />
    	    <Button x:Name="btnAddJobDay" Content="Add Day" Click="btnAddJobDay_Click" Margin="0,0,15,0" />
    	    <Button x:Name="btnRemoveJobDay" Content="Remove Day" Click="btnRemoveJobDay_Click" Margin="0,0,15,0" />
    	    <Button x:Name="btnAddJobTime" Content="Add Row" Click="btnAddJobRow_Click" Margin="0,0,15,0" />
    	    <Button x:Name="btnRemoveJobTime" Content="Remove Row" Click="btnRemoveJobRow_Click" Margin="0,0,15,0" />
    	</StackPanel>
    
    	<custom:CustomDataGrid Grid.Row="1" x:Name="dgJobTime" AutoGenerateColumns="False" ItemsSource="{Binding JobTimeSorted}">
    	    <data:DataGrid.Columns>
    		<data:DataGridTextColumn Header="Date" Binding="{Binding DateFormatted, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
    		<data:DataGridTextColumn Header="Day" Binding="{Binding Day, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
    		<data:DataGridTextColumn Header="Equip / Labor" Binding="{Binding Description, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>        
    		<data:DataGridTextColumn Header="Start Time" Binding="{Binding StartTimeFormatted, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
    		<data:DataGridTextColumn Header="End Time" Binding="{Binding EndTimeFormatted, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
    	    </data:DataGrid.Columns>
    	</custom:CustomDataGrid>
        </Grid>
    

    And here's pertinent code.  First the location of the JobTimeSorted, in Models' folder in Job.Shared.cs, inside partial class Job (Jobs have JobTimes):

    public IQueryable<JobTime> JobTimeSorted
    {
        get
        {
    	return JobTimes.OrderByDescending(x => x.Day).AsQueryable();
        }
    }
    

    And here's the entire contents of the Models' JobTime.shared.cs class (in the same folder with Job.Shared.cs):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace rj.JobBoard.Web.Models
    {
        public partial class JobTime
        {
            public String DateFormatted
            {
                get
                {
                    return Date.ToShortDateString();
                }
                set
                {
                    Date = DateTimeParser.Parse(value);
                }
            }
    
            public String StartTimeFormatted
            {
                get
                {
                    return DateTimeParser.ParseTime(StartTime);
                }
                set
                {
                    StartTime = DateTimeParser.ParseTime(value, true);
                }
            }
    
            public String EndTimeFormatted
            {
                get
                {
                    return DateTimeParser.ParseTime(EndTime);
                }
                set
                {
                    EndTime = DateTimeParser.ParseTime(value, true);
                }
            }
    
            public decimal Total
            {
                get
                {
                    decimal total = 0;
    
                    if (StraightTime != null && StraightTimeRate != null)
                        total += (StraightTimeRate.Value * StraightTime.Value);
    
                    if (OverTime != null && OverTimeRate != null)
                        total += (OverTimeRate.Value * OverTime.Value);
    
                    return total;
                }
            }
    
            public decimal Markup
            {
                get
                {
                    decimal total = 0;
                    if (SubRates.HasValue)
                        total = .15m * SubRates.Value;
    
                    // The + 0.01m will force it to round up on .50
                    total = Math.Round(total + 0.01m, 0);
    
                    return total;
                }
            }
    
            public decimal FuelSurchargeAmount
            {
                get
                {
                    decimal total = 0;
    
                    if (FuelSurcharge.HasValue)
                        total = FuelSurcharge.Value * Total;
    
                    // The + 0.01m will force it to round up on .50
                    total = Math.Round(total + 0.01m, 0);
                    
                    return total;
                }
            }
    
            public decimal TotalRevenue
            {
                get
                {
                    //total 
                    decimal total = Total;
    
                    if (Permits.HasValue)
                        total += Permits.Value;
    
                    if (Other.HasValue)
                        total += Other.Value;
    
                    if (SubRates.HasValue)
                        total += SubRates.Value;
    
                    total += Markup;
    
                    total += FuelSurchargeAmount;
    
                    return total;
                }
            }
        }
    }
    

    And here's three attempts (all currently commented out) to filter the dgJobTime DataGrid using PagedCollectionView that all produce an object reference not set to an instance of an object NullReferenceException when trying to return the results.  Do I need to put this somewhere else, like an event of the DataGrid?  I've looked at lots of examples, but just not sure if the complexity of the dependency on the data classes is throwing me.  This is the TextBox's TextChanged event:

    private void dateWAPT_TextChanged(object sender, TextChangedEventArgs e)
    {
        var pcv = dgJobTime.ItemsSource as PagedCollectionView;
        Job job = CurrentJob;
        IQueryable<JobTime> jobTimes = job.JobTimeSorted;            
    
        if (dateWAPT.Text != "")
        {                
    	// third attempt 
    	//pcv.Filter = c =>
    	//    {
    	//        var jobTime = CurrentJob.JobTimeSorted;
    	//        return jobTime != null && Convert.ToBoolean(jobTime.Where(x => x.DateFormatted.Equals(dateWAPT.Text)).ToString());
    	//    };
    
    	// second attempt
    	//pcv.Filter = c => ((JobTime)c).DateFormatted.Contains(dateWAPT.Text);
    
    	// first attempt
    	//pcv.Filter = c =>
    	//{
    
    	//    var jobTime = c as JobTime;
    	//    return jobTime != null && jobTime.DateFormatted == dateWAPT.Text;
    	//};
        }
        else
    	pcv.Filter = null;
    }
    

    Thanks in advance for any help,

    Monday, May 07, 2012 12:05 PM

All replies

  • I moved the binding for the dgJobTime grid to the code behind.  The XAML for the grid looks like this

    <custom:CustomDataGrid Grid.Row="1" x:Name="dgJobTime" AutoGenerateColumns="False">

    And the code behind to bind the grid now looks like this:

    PagedCollectionView pcv = new PagedCollectionView(CurrentJob.JobTimeSorted);
    dgJobTime.ItemsSource = pcv;
    

    And the TextChanged event for the TextBox now looks like this:

    private void dateWAPT_TextChanged(object sender, TextChangedEventArgs e)
    {
        PagedCollectionView pcv = dgJobTime.ItemsSource as PagedCollectionView;
    
        if (pcv != null && pcv.CanFilter == true)
        {
    	// Apply the filter
    	pcv.Filter = c => ((JobTime)c).DateFormatted.Contains(dateWAPT.Text);
        }
    }
    

    I'm now getting an InvalidationOperationException error - "'Filter' is not allowed during an AddNew or EditItem transaction".  Exception detail is

    System.InvalidOperationException was unhandled by user code
      Message='Filter' is not allowed during an AddNew or EditItem transaction.
      StackTrace:
           at System.Windows.Data.PagedCollectionView.set_Filter(Predicate`1 value)
           at RjCorman.JobBoard.Dispatch.dateWAPT_TextChanged(Object sender, TextChangedEventArgs e)
           at MS.Internal.CoreInvokeHandler.InvokeEventHandler(UInt32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
           at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName, UInt32 flags)
      InnerException:

    Am I closer to the solution, or further away? : )

    Monday, May 07, 2012 3:07 PM
  • That last change now breaks my DomainDataSource.SubmitChanges() updates to the DataGrid however.  If we have to go this route with the binding, I guess I'll need to handle CRUD operations differently : ( (e.g., row additions/updates/deletions)?

    Monday, May 07, 2012 3:22 PM
  • I would suggest you Bind DataGrid's ItemsSource to DomainDataSource and use the FilterDescriptors of DomianDataSource to filter data

    http://msdn.microsoft.com/en-us/library/system.windows.controls.domaindatasource.filterdescriptors%28v=vs.91%29.aspx#Y0

    Tuesday, May 08, 2012 11:17 PM
  • That was a good answer, but did not solve my problem.  In fact, it led me to follow the excellent tutorial/hands on lab at http://www.silverlight.net/learn/overview/silverlight-4-training-course.  I went through the SL 4 Event Manager Module 2 lab and it covered the DomainDataSource.  Highly recommended for the beginner.

    But, I am having trouble binding the underlying objects as they exist today with the DomainDataSource.  Unfortunately, the application I'm working on is fairly complex IMO for a SL newby.  When I go to Data Sources, I can drag the Job entity object's JobTime entity to the design surface, and it creates a DataGrid, but it will not populate with any data.  I even tried to update the OnNavigatedTo event on the main page, but it doesn't even fire when changing between records in the app.  So, I'm lost as far as what to do next to get this working.  I've contacted the original developer, but there's no guarantees when I'll get an answer.

    Wednesday, May 16, 2012 8:27 AM