WPF and csla:ValidationPanel

WPF and csla:ValidationPanel

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


FreeAsInBeer posted on Thursday, April 19, 2007

Hi Rocky/All,

I've been 'playing' around with WPF and the early versions of CSLA 3.0.

So far, so good. My (simple) application works without any changes to the code after the reference change (csla.dll, that is).

csla:ObjectStatusPanel works fine, and gives me bindable access to IsDirty etc.

However, when using csla:ValidationPanel in my elements (user controls etc) I get quite a big performace hit when loading them. For example; when changing between TabItems in a TabControl there is a very noticable delay when loading a TabItem that contains UCs that contain the ValidationPanel.

The delay is about 0.25 to 0.5 of a second. Enough to think 'did I click?' or 'ooops, db error'. Take away the ValidationPanel and the load is instant.

My object has only one validation rule;

        protected override void AddBusinessRules()
        {
            ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired, "BusinessName");
        }

thats all for now, I'll report back more as I can

RockfordLhotka replied on Thursday, April 19, 2007

The validationpanel works very much like the ErrorProvider in Windows Forms. What I mean by that, is that a change to a data source property within the panel causes the panel to refresh the error status of all the bound properties for the data source.

To put it another way, if your object has properties A, B and C - all bound to controls. And you then change property B, the panel will check to see if A, B or C are in error to update the UI.

Due to this, it is very important that you keep the scope of validationpanel as small as possible. Don't wrap your whole form with it, but rather wrap only the part of the form that has data.

Of course it is also possible that you've hit some bug scenario, in which case it would be nice to know what it is.

I say that, because in my testing I haven't seen any visible slowdown. Certainly nothing on the scale of .25 seconds! So you have some scenario going on that is very different from what I've been doing. And it really might be a scenario where I could short-circuit some extraneous processing that might be happening or something.

Is this a huge form? Dozens of bound fields?

Is it parent-child? Do you have the child data control (list or whatever) somehow inside the parent's validationpanel?

FreeAsInBeer replied on Thursday, April 19, 2007

The form isnt 'huge'. Biggish, but not huge. Each time the TabItem loads I reset the DataContext. This could very well be the wrong way to do it, and now that I have more of a grasp of WPF I might go back and change it. But.. the DataContext will still update at some stage, ie when a new object is loaded.

The scope of the validation panel was limited to contain only the elements that were bound.

It is not a parent-child, just a flat BusinessBase object.

Here's a copy of my UC. I've trimmed all the fat (ie row defs, grid/row posi, styling etc). It's a big big, sorry.

The validation panel was just inside the ObjectStatusPanel.

I'll have a play with it today and remove elements to see if i can isolate anything in particular.


<csla:ObjectStatusPanel>
   <StackPanel Name="Wrapper" Orientation="Vertical">
      <Grid Width="600">
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="170" />
            <ColumnDefinition Width="7" />
            <ColumnDefinition />
            <ColumnDefinition Width="5" />
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <!-- ROWS REMOVED -->
         </Grid.RowDefinitions>

         <TextBlock Text="business name" />
         <TextBox Text="{Binding Path=BusinessName, Mode=TwoWay}" IsEnabled="{Binding Path=IsBranchChild, Mode=OneWay, Converter={StaticResource InvertBoolConverter}}"  />

         <TextBlock Text="business structure" />
         <ComboBox SelectedValuePath="Value" SelectedValue="{Binding Path=BusinessStructure, Mode=TwoWay}" ItemsSource="{Binding Source={x:Static Member=Application.Current}, Path=Properties[ss_cl_BusinessStructure]}" />

         <TextBlock Text="ACN" />
         <TextBox Text="{Binding Path=ACN, Mode=TwoWay}" />

         <TextBlock Text="ABN" />
         <TextBox Text="{Binding Path=ABN, Mode=TwoWay}" />

         <TextBlock Text="business numbers validated?" />
         <CheckBox IsChecked="{Binding Path=BusinessNumbersValidated, Mode=TwoWay}" />

         <TextBlock Text="registered for GST?" />
         <CheckBox IsChecked="{Binding Path=RegisteredForGST, Mode=TwoWay}" />

         <TextBlock Text="industry type" />
         <ComboBox SelectedValuePath="Value" SelectedValue="{Binding Path=IndustryType, Mode=TwoWay}" ItemsSource="{Binding Source={x:Static Member=Application.Current}, Path=Properties[ss_cl_IndustryType]}" />

         <TextBlock Text="is active?" />
         <CheckBox IsChecked="{Binding Path=IsActive, Mode=TwoWay}" />

         <TextBlock Text="credit limit ($)" />
         <TextBox Text="{Binding Path=CreditLimit, Mode=TwoWay}" />

         <TextBlock Text="base discount (%)" />
         <TextBox Text="{Binding Path=DiscountBase, Mode=TwoWay}" />

         <TextBlock Text="early payment discount (%)" />
         <TextBox Text="{Binding Path=DiscountEarlyPayment, Mode=TwoWay}" />

         <TextBlock Text="payment terms" />
         <ComboBox SelectedValuePath="Value" SelectedValue="{Binding Path=PaymentTerms, Mode=TwoWay}" ItemsSource="{Binding Source={x:Static Member=Application.Current}, Path=Properties[ss_cl_PaymentTerms]}" />

         <TextBlock Text="roles" />
            <StackPanel Orientation="Vertical">
               <CheckBox IsChecked="{Binding Path=IsCustomer, Mode=TwoWay}" Margin="3">is customer?</CheckBox>
               <CheckBox IsChecked="{Binding Path=IsSupplier, Mode=TwoWay}" Margin="3">is supplier?</CheckBox>
               <CheckBox IsChecked="{Binding Path=IsAgent, Mode=TwoWay}" Margin="3">is agent?</CheckBox>
            </StackPanel>

         <TextBlock Text="is branch parent?" />
         <CheckBox IsChecked="{Binding Path=IsBranchParent, Mode=TwoWay}" IsEnabled="{Binding Path=IsBranchChild, Mode=OneWay, Converter={StaticResource InvertBoolConverter}}" />

         <TextBlock Text="is branch child?" />
         <CheckBox IsChecked="{Binding Path=IsBranchChild, Mode=TwoWay}" IsEnabled="{Binding Path=IsBranchChild, Mode=OneWay}" />

         <TextBlock Text="branch name" />
         <TextBox Text="{Binding Path=BranchName, Mode=TwoWay}">
            <TextBox.IsEnabled>
               <MultiBinding Converter="{StaticResource IsInFranchiseConverter}" Mode="OneWay">
                  <Binding Path="IsBranchParent"/>
                  <Binding Path="IsBranchChild"/>
               </MultiBinding>
            </TextBox.IsEnabled>
         </TextBox>

         <TextBlock Text="include branch events in statements?" />
         <CheckBox IsChecked="{Binding Path=IncludeBranchEventsInStatements, Mode=TwoWay}" IsEnabled="{Binding Path=IsBranchParent, Mode=OneWay}" />

         <TextBlock Text="branches get own statements?" />
         <CheckBox IsChecked="{Binding Path=BranchesGetOwnStatements, Mode=TwoWay}" IsEnabled="{Binding Path=IsBranchParent, Mode=OneWay}" />

         <TextBlock Text="branches inherit discounts?" />
         <CheckBox IsChecked="{Binding Path=BranchesInheritDiscounts, Mode=TwoWay}" IsEnabled="{Binding Path=IsBranchParent, Mode=OneWay}" />

         <Button Click="AddNewBranchClick" Width="150" IsEnabled="{Binding Path=IsBranchParent, Mode=OneWay}">add new branch</Button>

         <TextBlock Text="business ID" />
         <TextBlock Text="{Binding Path=BusinessID, Mode=OneWay}" />

         <TextBlock Text="date added" />
         <TextBlock Text="{Binding Path=DateAdded, Mode=OneWay}" />

         <TextBlock Text="branch parent ID" />
         <TextBlock Text="{Binding Path=ParentID, Mode=OneWay, Converter={StaticResource GUIDtoStringConverter}}" />

         <TextBlock Text="info" />
         <StackPanel Orientation="Vertical">
            <CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatusPanel, AncestorLevel=1}, Path=IsSavable}">IsSavable</CheckBox>
            <CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatusPanel, AncestorLevel=1}, Path=IsValid}">IsValid</CheckBox>
            <CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatusPanel, AncestorLevel=1}, Path=IsDirty}">IsDirty</CheckBox>
            <CheckBox IsEnabled="False" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatusPanel, AncestorLevel=1}, Path=IsNew}">IsNew</CheckBox>
         </StackPanel>

         <TextBlock Text="edit level" />
         <TextBlock Text="{Binding Path=DiscountEarlyPayment, Mode=OneWay}" />
      </Grid>

      <StackPanel Orientation="Horizontal">
         <Button Click="SaveEntityClick" Width="150" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=csla:ObjectStatusPanel, AncestorLevel=1}, Path=IsSavable}">save changes</Button>
         <Button Click="UndoChangesClick" Width="150">undo changes</Button>
      </StackPanel>

   </StackPanel>
</csla:ObjectStatusPanel>

RockfordLhotka replied on Thursday, April 19, 2007

Well certainly the control has to do extra work any time the datacontext changes, because it has to re-scan the object for its metadata and recheck all the validation. But it sounds like your perf issue wasn’t due to the datacontext being changed, rather due to a single property being changed, right?

 

Rocky

FreeAsInBeer replied on Thursday, April 19, 2007

I've done a bit more testing now, and it doesn't seem to have anything to do with the refreshing of the datacontext. I took that code out and it still was 'slow'.

I wonder if it is a WPF rendering issue? No events are firing, that I can think of, other than SelectedIndexChanged on the TabControl. My user control's Loaded event calls OnLoad, but I've commented out all lines (ie the bit that refreshes the datacontext). Nothing is happening other than the rendering of the form.

I commented out all inputs, down to the first one. There was a VERY small delay, just noticable. I then un-commented them out, one by one. As each one was added back in, the delay increased slightly. By the time there were 4 inputs, I'd say the delay in rendering the UC/opening the TabItem was noticable enough to mention.

This is not a big issue for me. My plan is to just bind the BrokenRulesCollection of my object to a styled listbox at the top of the page. This gives instant feedback of all broken rules. What it doesnt do, though, is highlight the inputs with broken rules. Not perfect, I know.

Still, I'll keep poking around and see if it's my code that might be slowing down the ValidationPanel





RockfordLhotka replied on Thursday, April 19, 2007

I appreciate your help and any further insight you can provide.

 

It certainly is my intent to have the validationpanel work for typical data entry forms.

 

Rocky

Paul Czywczynski replied on Thursday, April 19, 2007

We have forms with dozens of data entry controls and I haven't noticed any delay in rendering from the validation panel. We are using the CslaDataProvider with the IsAsync property set to true (BTW Rocky, your last check-in fixed our issues with it). We do have a slight delay, about a half second, between form loaded and data population because of the async setting of the data provider but the form loads and renders pretty much instantaneously.

Copyright (c) Marimer LLC