New behavior in WPF 3.5?

New behavior in WPF 3.5?

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


RockfordLhotka posted on Wednesday, May 28, 2008

I am observing what appears to be new/different behavior from WPF 3.0 to 3.5, and I'm wondering if anyone else has seen this.

In WPF 3.0, like Windows Forms, a PropertyChanged event causes all controls for the related data source (business object) to refresh. So changing a property causes all other properties of that object bound to the UI to refresh in the display.

In WPF 3.5 it appears that only the changed property's control is refreshed. Other controls on the form are not refreshed. This is a problem for validation error reporting, because it is also the case that the ValidatesOnErrors setting only causes revalidation when the display is refreshed.

Has anyone else seen this, or is it a quirk I'm running into in my project?

Curelom replied on Wednesday, May 28, 2008

I have come across this same issue.  To work around it, I've added a PropertyChanged method to the form that walks through the visual tree, grabs the binding expressions, then calls UpdateSource on the appropriate expressions.  This hasn't been working perfectly, as I have a tab control, and walking the visual tree doesn't go through the controls on the unselected tabs.  I also have to account for the different binding expressions for the different type of controls such as textboxes and Comboboxes.

public void ValidationControls(Visual myVisual) {

   for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++) {

      // Retrieve child visual at specified index value.

      Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);

      // Do processing of the child visual object.

      if (childVisual is Control && ((Control)childVisual).Name != null) {

         Control ctl = (Control)childVisual;

         Console.WriteLine(ctl.Name);

         BindingExpression be = null;

         if (ctl is TextBox || ctl is ComboBox) {

            if (ctl is TextBox)

              be = ctl.GetBindingExpression(TextBox.TextProperty);

           else if (ctl is ComboBox)

              be = ctl.GetBindingExpression(ComboBox.SelectedValueProperty);

           if(be != null)

               be.UpdateSource();

         }

      }

      // Enumerate children of the child visual object.

      ValidationControls(childVisual);

   }

}

RockfordLhotka replied on Wednesday, May 28, 2008

Good, at least I'm not making it up Smile [:)]

I think I can solve it inside CSLA - but it means divergent behavior between data binding for Windows Forms and for WPF. Up to now I've been able to keep them consistent, so this is sad. But Microsoft has apparently decided to diverge their behavior, so I think we're stuck.

The solution I think might work (and I haven't dug into the code on this) is for SetProperty() (or more likely PropertyHasChanged()) to get back a list of changed properties from CheckRules(), and then call OnPropertyChanged() for each property in the list.

In other words, ValidationRules.CheckRules() could return an array of property names for each property that changed - or at least each property for which rules were checked. PropertyHasChanged() is running in the actual business object (in BusinessBase) and so it can raise PropertyChanged events.

In Windows Forms this would be very inefficient, since each of the PropertyChanged events would cause a refresh of the UI. But in WPF they are doing what Windows Forms probably should have done and only refreshing controls bound to changed properties. Raising multiple PropertyChanged events makes sense in WPF.

So then there needs to be a configuration switch so you could activate this new behavior. Perhaps Csla.ApplicationContext.PropertyChangedMode, which would be either Default or Xaml (assuming Silverlight follows the WPF model).

I think I'd make this PropertyChangedMode load from app.config, or be something you can set in code. In other words, it would be a read-write property.

Thoughts?

Curelom replied on Wednesday, May 28, 2008

That certainly sounds like a better solution then my kludge.Wink [;)]  I'm suprised you were able to keep them from diverging up to this point.

RockfordLhotka replied on Wednesday, May 28, 2008

In talking to a member of the WPF team, it sounds like this is NOT a behavioral change from 3.0 to 3.5.

I imagine I never noticed the issue in 3.0 because I was using Csla.Wpf.Validator for validation error display, and it replicates the Windows Forms ErrorProvider behavior. Now in 3.5 I'm using the new validation behavior built-in to WPF and so am seeing the issue.

Ultimately the current WPF behavior is clearly the correct behavior, in that I'd expect only a changed property to be updated in the UI. The Windows Forms model never seemed right in that regard.

I'll work on adding the solution I proposed above to 3.5.1 and all will be well.

tetranz replied on Wednesday, May 28, 2008

RockfordLhotka:
Ultimately the current WPF behavior is clearly the correct behavior, in that I'd expect only a changed property to be updated in the UI. The Windows Forms model never seemed right in that regard.

But, thankfully (I think), it did enable the chkIsDirty trick hack in CSLA version 1. Smile [:)]

Curelom replied on Friday, June 20, 2008

Hot dog, your fix works.

Thank you

Adam replied on Wednesday, June 03, 2009

Hi there

I have created some calculating columns in silverlight using the business objects as found in this thread
http://forums.lhotka.net/forums/33722/ShowThread.aspx#33722 but I am unable to get them to update with user changes to the UI. Shawn has suggested that PropertyChangedMode might help but I can't get it do update the caluated fields in either mode.

I have tried the following line in both App.xaml.cs Application_Startup and in the constructor for my invoice viewer without success.

//Fire events for all xaml not just the current control http://forums.lhotka.net/forums/thread/23824.aspx
            Csla.ApplicationContext.PropertyChangedMode = Csla.ApplicationContext.PropertyChangedModes.Xaml;


Any help would be greatly appreciated.

Adam

RockfordLhotka replied on Wednesday, June 03, 2009

When a property changes, a PropertyChanged event for that property must be raised.

 

If the property is a managed property and you use SetProperty(), then CSLA raises the event for you.

 

If you implement the property in any other manner (so you don’t call SetProperty()), then you must raise the PropertyChanged event for that property. You do that by calling OnPropertyChanged().

 

In a WPF setting, you must set the PropertyChangedMode to Xaml, or data binding simply won’t refresh correctly, because CSLA won’t raise enough PropertyChanged events. In the older Windows Forms model you wanted to minimize the number of PropertyChanged events raised, but in WPF you need to explicitly raise an event for every changed property. The PropertyChangedMode tells CSLA which model to use.

 

Rocky

 

Adam replied on Thursday, June 04, 2009

Brilliant, that works a treat - OnPropertyChnaged("VATAmount") in the Setting for the property that changes!

Then only question now is I need to update the Invoice's Totals but can't see how to do that from the property that changes. InvoiceItem( BusinessBase) is a child of InvoiceItemList(BusinessListBase) which is a child of Invoice(BusinessBase)

Any ideas?

This is also in thread http://forums.lhotka.net/forums/33750/ShowThread.aspx#33750

RockfordLhotka replied on Thursday, June 04, 2009

I don’t have the full picture here.

 

Your VATAmount property is in a child of a collection? And you have a property on the parent object that needs to recalculate?

 

This has been discussed in numerous threads – the answer is that the parent object needs to handle the ListChanged event of the collection and use that as a trigger to do the calculation.

 

Rocky

Copyright (c) Marimer LLC