Silverlight 3 DataForm and DomainDataSource Binding

Because all Silverlight data access is Asynchronous, care must be taken to be sure that all of your data has come down the pipe before letting a user loose on the presentation and manipulation of that data. I am currently building a medium sized Line of Business (LOB) application on spec based on the Silverlight 3 Beta and the new .Net RIA Services. These new releases from Microsoft have some rather amazing features that have allowed me to prototype this application in a matter of days over the last several weeks since CTP 9 was released to those of us fortunate to be under NDA for early releases.

There are several lookup tables that I use in my application to reduce the amount of typing a user must do when filling in the details of the primary domain entities in this application (see my last post for details on one solution to the lookup ComboBox data binding issue). IN my application, each of these lookup entities gets a DataForm to allow maintenance by an individual with the proper credentials. In building my DataForms for each entity, I noticed that often, the navigation elements (see screen caps below) were not enabled once the form had rendered. What? I had set an event to handle the Loaded event for both my page and also for my DomainDataSource’s DataContext, or at least I thought I had.

image

What I was seeing…

image

The way it’s supposed to look…

When I would fire up one of these forms, I would see that the navigation elements were grayed out, and sometimes no data would appear in the form’s entry elements. I played with the various DataForm properties, and the binding syntax and/or markup, etc. to no avail.

It turns out that since I was using the markup approach for my DomainDataSource (with x:Name of ‘dds’), I was not handling the LoadedData event for that object. Furthermore, because that handler is responding to an Asynchronous event, I realized that I had to use the built-in Dispatcher.BeginInvoke to properly update the UI. And lastly, based on a response to a question I had on the now-defunct Alexandria forum at the Silverlight site, I knew that a DataForm’s CurrentIndex is usually –1 (no data) when it is rendered, so that was the key setting I had to make in my handler. Note that I’m using a Lambda expression for my anonymous delegate in that handler. Here’s some code that I hope will save you several hours of scratching your head.

XAML Markup snippet:

<ods:DomainDataSource x:Name="dds" 
          LoadMethodName="LoadBusinessCategories"
          AutoLoad="True"
          LoadSize="20">
    <ods:DomainDataSource.DomainContext>
            <ds:MyBusiness/>
    </ods:DomainDataSource.DomainContext>
</ods:DomainDataSource>
<dataControls:DataForm x:Name="dataForm1"  Margin="5,5,5,0" Width="400" Height="150"                          
                         AutoEdit="False" AutoCommit="True"    
                         VerticalAlignment="Top"       
                         CommandButtonsVisibility="All"
                         Header="Business Categories"        
                         ScrollViewer.VerticalScrollBarVisibility="Auto" 
                         CanUserAddItems="True" CanUserDeleteItems="True"
                         AutoGenerateFields="False" 
                         IsEnabled="True" IsReadOnly="False" IsHitTestVisible="True">
     <dataControls:DataForm.ItemsSource>
         <Binding ElementName="dds" Path="Data"/>
      </dataControls:DataForm.ItemsSource>
      <dataControls:DataForm.Fields>
            <dataControls:DataFormTextField FieldLabelContent="Short Name: " 
                                    Binding="{Binding ShortName, Mode=TwoWay }" />
            <dataControls:DataFormTextField FieldLabelContent="Description: " 
                                    Binding="{Binding Description, Mode=TwoWay }" />
      </dataControls:DataForm.Fields>
</dataControls:DataForm>

Code-behind snippet:

public partial class BusinessCategories : Page
{
    public BusinessCategories()
    {
        InitializeComponent();
        dds.LoadedData += 
            new EventHandler<System.Windows.Ria.Data.LoadedDataEventArgs>(dds_LoadedData);
        dds.SubmittedChanges += 
            new EventHandler<SubmittedChangesEventArgs>(dds_SubmittedChanges);
    }

    void dds_LoadedData(object sender, LoadedDataEventArgs e)
    {
        if (!dds.DataView.IsEmpty)
        {
            Dispatcher.BeginInvoke(() =>
            {
                dataForm1.CurrentIndex = 0;
            }
            );
        }
        else
            dds.Load();
    }

    void dds_SubmittedChanges(object sender, SubmittedChangesEventArgs e)
    {
        if (e.Error != null)
        {
            DisplayError(e);
        }
    }

    // Executes when the user navigates to this page.
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
    }

    private void submitButton_Click(object sender, RoutedEventArgs e)
    {
        if (dds.HasChanges)
        {
            dataForm1.CommitItemEdit();
            dds.SubmitChanges();
        }
    }

    private void DisplayError(System.Windows.Ria.Data.SubmittedChangesEventArgs e)
    {
        ChildWindow ErrorWin = new ErrorWindow(e.Error);
        ErrorWin.Show();
        ErrorWin.Focus();
    }
}

Hope this helps.

Bob Baker

P.S. I went to Orlando CodeCamp on March 28, 2009. Did you?

posted @ Friday, March 27, 2009 3:23 PM

Print

 

Comments have been closed on this topic.