0

I'm making a WPF application following the MVVM pattern.

I have one window with navigation controls as well as a place-holder (contentcontrol) to hold a single user control. The main window's viewmodel maintains an array of open child viewmodels, and has a 'CurrentView' property that points to a single child viewmodel from the array. This 'CurrentView' property is then bound to the contentcontrol placeholder on the main window. The viewmodels are all set up with a specific view (user control) via data templates.

When the 'CurrentView' property is set to one of the child viewmodels, the appropriate user control view is shown in the contentcontrol placeholder in the main window. Only one child viewmodel is shown at a time, the rest just sit in the array, waiting to be shown by being set to the CurrentView property.

Until now, I've had no problems with this pattern. It works perfectly for all standard WPF controls without any issues. My problem is only when one of my user controls hosts a WPF Crystal Report Viewer.

I can set the current view to a viewmodel which has a crystal report reportdocument. I load the report document and expose it as a public dependency property. The data-templated user control for this viewmodel has a WPF Crystal Report Viewer control. I've added an attached property to the report viewer control to allow me to bind to .ViewerCore.ReportSource. I bind the report document from the viewmodel to the attached property, and voila! All of this works as expected. I compile my program and navigate to the crystal report viewer... it loads the bound report and shows it properly.

Now, however, when I navigate away (replace the 'CurrentView' Property on the main window with a different viewmodel) I get an error.

At first, I was getting "Property not set: Window". After some searching, I found that the Crystal Report Viewer was trying to show a messagebox, but wasn't aware of it's owner window, so it couldn't. To fix this, I set the owner of the crystal report viewer to the main window in the user controls loaded event. That got rid of the "Property not set: Window" error.

I recompile, navigate to the report, it loads fine. I navigate away, and I now get a popup that says 'Object reference not set to an instance of an object." I hit okay, the popup goes away, and my program continues without any visible issues. I can navigate back to the report without any trouble, but this popup shows each time I navigate away.

I can't debug the source of the popup, because if I remove the owner, it gets swallowed by the viewer's inability to find the owner to show the error.

Sorry for the wordy description. The project is extremely large, so trying to fit source code would take a lot more room than the description of it. If you want to see any specific part of my code, just leave a comment and I'll update my question.

Chronicide
  • 1,112
  • 1
  • 9
  • 32

1 Answers1

1

I tried a bunch of different breakpoints, and was able to narrow it down to my attached dependency property for binding to the ViewerCore.ReportSource. When I changed to a different view, this property was unexpectedly set to null, and the attached propert didn't think to account for this. In case someone has a similar issue in the future, you can use the following:

public static class DependencyPropertyHost
{
    public static readonly DependencyProperty ReportSourceProperty = DependencyProperty.RegisterAttached("ReportSource", typeof(ReportDocument), typeof(DependencyPropertyHost), new PropertyMetadata(ReportSourceChanged));

    public static ReportDocument GetReportSource(DependencyObject obj)
    {
        return obj.GetValue(ReportSourceProperty) as ReportDocument;
    }

    public static void SetReportSource(DependencyObject obj, ReportDocument value)
    {
        obj.SetValue(ReportSourceProperty, value);
    }

    private static void ReportSourceChanged(object sender, DependencyPropertyChangedEventArgs args)
    {
        var viewer = sender as CrystalReportsViewer;
        if (viewer != null && args.NewValue != null)
        {
            viewer.ViewerCore.ReportSource = args.NewValue;
        }
    }
}
Chronicide
  • 1,112
  • 1
  • 9
  • 32