Databinding Nightmare csla 3.0

Databinding Nightmare csla 3.0

Old forum URL: forums.lhotka.net/forums/t/4221.aspx


smark posted on Wednesday, January 23, 2008

Using CslaDataProvider for WPF. I have a tab control with each tab loading a different instance of the same type. However, when I click on a tab other than the one currently active, the object simply seems to forget whether it is new or dirty or is savable or is valid unless I reload that object from the database. I just don't want to make unnecessary calls to the database and once retreived I store these objects in a collection (CAB's items collection). I have tried "nulling" the object reference and then rebinding to the same object without any succes. Any workaround suggestions would be greatly appreciated. Thanks.

Curelom replied on Wednesday, January 23, 2008

Could you post your xaml and binding information?  I've been using tabs without issue.

When you say losing the new/dirty/savable information, do you mean with the CSLA ObjectStatus controller, or within the objects themselves?

smark replied on Wednesday, January 23, 2008

 <Grid Name="MainGrid" DataContext="{Binding Source={StaticResource User}}" HorizontalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <csla:ObjectStatus>
            <Border Height="Auto" CornerRadius="10" Padding="5" BorderBrush="#969696" BorderThickness="1" Background="White" Margin="5,10,5,5" HorizontalAlignment="Left">
                <Grid Width="500" Height="Auto">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                        <RowDefinition Height="Auto"></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"></ColumnDefinition>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                        <ColumnDefinition Width="Auto"></ColumnDefinition>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <Label Grid.Row="0" Grid.Column="0" Margin="3" VerticalAlignment="Center">First Name:</Label>
                    <TextBox Name="FirstNameTextBox" Text="{Binding FirstName, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="0" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="1" Grid.Column="0" Margin="3" VerticalAlignment="Center">Middle Initial/Name:</Label>
                    <TextBox Name="MiddleNameTextBox" Text="{Binding MiddleInitial, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="1" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="2" Grid.Column="0" Margin="3" VerticalAlignment="Center">Last Name:</Label>
                    <TextBox Name="LastNameTextBox" Text="{Binding LastName, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="2" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="3" Grid.Column="0" Margin="3" VerticalAlignment="Center">Login ID:</Label>
                    <TextBox Name="LoginIDTextBox" Text="{Binding LoginID, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="3" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="4" Grid.Column="0" Margin="3" VerticalAlignment="Center">Password:</Label>
                    <TextBox Name="PasswordTextBox" Text="{Binding Password, Converter={StaticResource IdentityConverter}}"
                             Grid.Row="4" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="5" Grid.Column="0" Margin="3" VerticalAlignment="Center">Address 1:</Label>
                    <TextBox Name="Address1TextBox" Text="{Binding Address1, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="5" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="6" Grid.Column="0" Margin="3" VerticalAlignment="Center">Address 2:</Label>
                    <TextBox Name="Address2TextBox" Text="{Binding Address2, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="6" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="7" Grid.Column="0" Margin="3" VerticalAlignment="Center">City:</Label>
                    <TextBox Name="CityTextBox" Text="{Binding City, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="7" Grid.ColumnSpan="3" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="8" Grid.Column="0" Margin="3" VerticalAlignment="Center">State:</Label>
                    <TextBox Name="StateTextBox" Text="{Binding State, Converter={StaticResource IdentityConverter}}"
                         Grid.Row="8" Grid.Column="1" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>
                    <Label Grid.Row="8" Grid.Column="2" Margin="3" VerticalAlignment="Center">ZIP:</Label>
                    <TextBox Name="ZipTextBox" Text="{Binding Zip, Converter={StaticResource IdentityConverter}}"
                    Grid.Row="8" Grid.Column="3" Margin="3" Height="Auto" VerticalAlignment="Center"></TextBox>

                    <Button Grid.Row="9" Grid.Column="1" Command="ApplicationCommands.Save" Padding="20,0,20,0" Margin="0,0,50,0"
                            CommandTarget="{Binding Source={StaticResource FrameworkUser}, Path=CommandManager, BindsDirectlyToSource=True}"
                            HorizontalAlignment="Left" IsDefault="True">Save</Button>
                    <Button Grid.Row="9" Grid.Column="3" Command="ApplicationCommands.Undo" Padding="20,0,20,0" Margin="0,0,0,0"
                            CommandTarget="{Binding Source={StaticResource FrameworkUser}, Path=CommandManager, BindsDirectlyToSource=True}"
                            HorizontalAlignment="Left" IsCancel="True">Cancel</Button>
                    <Button Grid.Row="9" Grid.Column="0" Click="NewUser" HorizontalAlignment="Left" Padding="20,0,20,0">New User</Button>
                    <CheckBox Grid.Row="10" Grid.Column="0" IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsSavable}">IsSavable</CheckBox>
                    <CheckBox Grid.Row="10" Grid.Column="1" IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsValid}">IsValid</CheckBox>
                    <CheckBox Grid.Row="10" Grid.Column="2" IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsDirty}">IsDirty</CheckBox>
                    <CheckBox Grid.Row="10" Grid.Column="3" IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatus, AncestorLevel=1}, Path=IsNew}">IsNew</CheckBox>

                </Grid>

            </Border>

        </csla:ObjectStatus>
    </Grid>
A controller class (a controlled workitem within CAB) fetches the user object and shows it in one of the tabs. Let me know if you need any more information. Thanks.

Curelom replied on Wednesday, January 23, 2008

Is this grid within the tab item?  What Type/DataProvider is your StaticResource User?  Have you verified if it is the ObjectStatus object that is losing the isDirty information, or the business object itself?

smark replied on Wednesday, January 23, 2008

    The grid is in a user control that has nothing else. The user control gets displayed within a tab. If you are familiar with CompositeUI Application Block, there is a control named TabWorkspace that derives from the TabControl.

The static resource is  csladataprovider. No, I haven't verified whether it is the ObjectStatus object that is losing the isDirty, etc., info or the business object itself. It appears to be the ObjectStatus object becuase my Save or Cancel buttons simply wouldn't activate after I click back on a tab.

Here is the overview. User selects a user object from a list box whose id gets passed via a presenter object (MVP pattern) to the controller; the controller fetches the whole user object and passes it back to the presenter which is called in the OnLoad method of the view (the WPF user control). The view implements an interface; this implementation method has the user as a parameter. This user object is the csladataprovider. It is probably easier to just set the dataprovider data but it does not have a setter so, I am using a factory method to get the object. Makes sense?

RockfordLhotka replied on Wednesday, January 23, 2008

I know this isn't helpful, but I can't help myself after reading the last post.

And the CAB is helpful how? You just described an incredibly, dare I say insanely, complicated process to do something that is absurdly trivial (i.e. show a form bound to data).

I love design patterns as much as anyone, but sometimes they just run amok...

 

But this may help. You know you don't need to use a dataprovider control? You can just set the datacontext of a window/tab/usercontrol/grid/whatever to a business object directly?

Whatever level within your XAML that is bound to the dataprovider, you could skip doing that and just have your controller/presenter/whatsit set the DataContext property of that element directly:

view.Grid1.DataContext = myBusinessObject

The dataprovider controls are really designed to allow designers (not developers) to bind objects to the UI purely through XAML.

The only reason I like to use them as a developer is because they load data on a background thread automatically (just set a property and it works).

But it sounds like the CAB might be complicating the heck out of things by itself, and perhaps you can simplify by not using a dataprovider control?

smark replied on Wednesday, January 23, 2008

Yes, I was beginning to go that route as you suggested.

 ah well, at some point I was anticipating this thing with CAB! Yes, it does complicate the process, and yes I have bound data directly to forms before; and to be honest I am not fully sold on its (CAB's) benefits. There have been several positives though in this particular context. The app has five massive subsystems for project, marketing, document, HR, and records management. CAB helps me to load these as separate modules (assemblies) within a common "compositeUI" based on an authorization subsystem. What has been the more compelling reason to use CAB is its eventing and command handling systems without direct coupling between any of the several different components. And then of course the overall benefits of a dependency injection system, with its attendent complexties admittedly, that is CAB.

Rocky, you are one of the trailblazers of OO programming, particularly in the context of business objects, and I am not going to test my audacity by enumerating the theoretically possible benefits of CAB, except for the ones I've already mentioned before. When you have over 500 objects, it helps to have as much loose coupling as possible through interface oriented programming. Not the least, the presenters have helped in unit testing without tying them to a particular UI.

Copyright (c) Marimer LLC