Unexpected focus change using NotifyParentPropertyAttribute

Grids for WPF Forum

Posted 9 years ago by Phil Devaney - Senior Software Engineer, Serck Controls Ltd
Version: 10.1.0523
Platform: .NET 4.0
Environment: Windows Vista (64-bit)
Avatar
I have a property in my property grid which uses NotifyParentPropertyAttribute. When I change the value of the property using the property editor and press Tab, focus moves to the parent property instead of the next property as I would have expected. If I tab out of the child property without changing the value, focus moves to the next child property as expected.

This can easily be reproduced in the Notify Parent Property Quickstart in the sample application.

Comments (5)

Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Phil,

We've marked down a TODO item to see about adding a work around. The issue is the NotifyParentPropertyAttribute tells the parent property to "refresh", which includes rebuilding the children properties and their visuals.


Actipro Software Support

Posted 8 years ago by Jb RIGUET
Avatar
Any update on this bug, as it is currently happening to us, and as we achieved to 'hack' it, we'd like to see a fix, for a proper solution :)

Thanks !
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Jb,

Sorry, no. Properly restoring the focus is not trivial. There can be any number of controls under the item being refreshed. There can also be any number of items and nested items, each with their own controls.

Also, keep in mind that after the refresh new instances of the controls are created. For nested items, their parent must be expanded before their controls are recreated.

So it's not a matter of just saving the control that last had focus.


Actipro Software Support

Posted 8 years ago by Jb RIGUET
Avatar
Hi,

Here's how we hacked this :

  private void m_propertyGrid_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            ImmutablePropertyDescriptorDataAccessor oldValue = e.OldValue as ImmutablePropertyDescriptorDataAccessor;
            PropertyDescriptorDataAccessor newValue = e.NewValue as PropertyDescriptorDataAccessor;

            if (oldValue == null || newValue == null)
                return;

            if (PropertyDescriptorDataAccessorEquals(oldValue.Parent, newValue) == false)
                return;

            // Retrieves the hierarchy of the property
            IPropertyDataAccessor currentProperty = oldValue;
            List<String> hierarchy = new List<string>();

            while (currentProperty != null)
            {
                hierarchy.Add(currentProperty.ValueName);

                currentProperty = currentProperty.Parent;
            }

            hierarchy.Reverse();

            Action action = () =>
            {
                PropertyGridDataAccessorItem item = GetByName(m_propertyGrid, ref hierarchy, 0);
                if (item == null) return;

                item.IsSelected = true;
                item.BringIntoView();

                UIElement element = VisualTreeHelperExtended.GetFirstFocusableDescendant(item);
                if (element != null)
                {
                    element.Focus();
                }
                
            };

            m_propertyGrid.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, action);
         }

        bool PropertyDescriptorDataAccessorEquals(IPropertyDataAccessor a, IPropertyDataAccessor b)
        {
            if (a.ValueName != b.ValueName)
                return false;

            if (a.Parent == null && b.Parent != null)
                return false;

            if (b.Parent == null && a.Parent != null)
                return false;

            if (a.Parent == null && b.Parent == null)
                return true;

            return PropertyDescriptorDataAccessorEquals(a.Parent, b.Parent);
        }
        

        private PropertyGridDataAccessorItem GetByName(ItemsControl control , ref List<string> names , int deepIndex)
        {
            foreach (object item in control.Items)
            {
                PropertyGridDataAccessorItem container = (PropertyGridDataAccessorItem)control.ItemContainerGenerator.ContainerFromItem(item);
                if (container != null)
                {
                    if (container.DataAccessorType == DataAccessorType.Category)
                    {
                        PropertyGridDataAccessorItem pgdai = GetByName(container, ref names, deepIndex);

                        if (pgdai != null)
                            return pgdai;
                    }

                    if (container.DataAccessorType == DataAccessorType.Property)
                    {
                        if ( container.ValueName == names[deepIndex] && container.IsReadOnly == false && deepIndex == names.Count - 1)
                            return container;

                        if (container.HasItems && container.ValueName == names[deepIndex] )
                        {
                            PropertyGridDataAccessorItem tmp = GetByName(container,ref  names, deepIndex + 1);
                            if (tmp != null)
                                return tmp;
                        }
                    }
                }
            }

            return null;
        }
It will need some others tricks to allow users to select a parent with the mouse but it seems to work.
Do you notice some possible side effect in this code ?

Thank you.
Posted 8 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Jb,

You can't really assume that the ItemContainerGenerator has generated it's children. There is a Status property and StatusChanged event that can be used to determine if and when the containers are generated.

The original question was asking about moving focus to the control after the one that caused the updated (i.e. when pressing the tab key). Your code above could be moving the focus backwards, instead of forward if the property editor has several controls. There would also be cases where the user may click on another control (that isn't next, but maybe a few stops away) and other edge cases that would need to be handled.


Actipro Software Support

The latest build of this product (v2018.1 build 0675) was released 2 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.