views:

739

answers:

2

I have my first WPF working fine with an ObjectDataProvider in the XAML:

<ObjectDataProvider x:Key="WaitingPatientDS" ObjectType="{x:Type local:clsPatients}">
    <ObjectDataProvider.ConstructorParameters>
        <sys:Boolean>True</sys:Boolean>
    </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>

However, I don't like using this because if there is a connection error, I can't trap it and the program just barfs out.

So, what I've been trying to do is to instantiate the collection object directly in the codebehind...

public partial class MainWindow : Window
{
  ListBox _activeListBox;
  clsPatients oPatients;

public MainWindow()
{
  oPatients = new clsPatients(true);

...and then reference it in my databinding as so:

<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3"
   DataContext="{Binding Mode=OneWay, Source={StaticResource local:oPatients}}">

But, I'm getting "local:oPatients was not found".

So...what am I doing wrong in referencing this and/or how would someone else accomplish this data binding so that I can actually trap for connection errors and divert the main form to a user-friendly error form?

THANKS!

+2  A: 

I'd move the data access logic into a separate service, and perhaps into its own assembly entirely to enforce your intended separation of concerns. Then I'd have a view model use said service to retrieve data and expose it for the view. Then the view would simply bind to the view model and wouldn't care whether the data came from a database or whatever.

I would suggest reading up on separation of concerns, service locator/dependency injection, and MVVM.

HTH, Kent

Kent Boogaart
Wish I had the time brother, wish I had the time! ;)
LSTayon
Well, whoever inherits your code will be wishing the same thing ;)
Kent Boogaart
+1  A: 

You're getting that error because oPatients is not a StaticResource. It would have to be defined in the ResourceDictionary the way your ObjectDataProvider was, but as a class member it is not. You could expose it as a public property:

public clsPatients Patients { get; set; }

Then bind to it directly:

<!-- MainWindowRoot is the x:Name of your Window element. -->
<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3"
   DataContext="{Binding Patients, ElementName=MainWindowRoot, Mode=OneWay}">

Assuming I haven't made some stupid mistake, that should work. However, it still doesn't solve your problem because you're loading the data in the constructor, which means that any exceptions will cause clsPatients construction to fail, which in turn will cause MainWindow construction to fail, which you cannot properly handle because it's a rats next of a stack trace that's indistinguishable from a legitimate construction failure.

Kent is 100% right: The data should come from an external provider.

You may have resource limitations, but you can still establish good design even if you can't implement a tiered architecture. At bare minimum, establish separation of concerns by loading the data in a separate data provider class then passing the fully-formed data into the window. That allows you to isolate failures where they occur, and keep your code somewhat more loosely coupled.

public class PatientDataProvider
{
    public PatientDataProvider(WhatItNeedsToConnect whatItNeedsToConnect) 
    { 
        // this doesn't connect because the constructor shouldn't fail because of a connection failure
    }
    public clsPatients GetPatientData(bool yesOrNo) 
    {
        // this can fail because of a connection error or some other data loading error
    }
}

and invoke it as:

PatientDataProvider provider = new PatientDataProvider(whatItNeedsToConnect);
clsPatients patients = null;
try { 
    patients = provider.GetPatientData(true); 
    MainWindow w = new MainWindow { Patients = patients; };
    w.Show();
}
catch (WhateverExceptionGetsThrownByProvider e) 
{ 
    MessageBox.Show("Could not load patients: " + e.Message);
}

Also, if clsPatients is self-refreshing, make sure it implements INotifyPropertyChanged or INotifyCollectionChanged as appropriate in order for the binding targets to update when the data changes.

AndyM