triggering change of StandardValuesCore

Editors for WPF Forum

Posted 3 years ago by John Dunn
Version: 19.1.1
Avatar

I have a CustomPropertyModel which derives from PropertyDescriptorPropertyModel. I've overridden HasStandardValues, IsLimitedToStandardValuesCore and StandardValuesCore per the CustomDatafactory example. It's working fine for a single non-changing set of values but I need to change the set of values based on another property. I've tried forcing a change to StandardValues but that didn't seem to have any affect. Here's my test code.

using ActiproSoftware.Windows;
using ActiproSoftware.Windows.Controls.Grids.PropertyData;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;

namespace WpfApp3
{
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      DataContext = new Data()
      {
        Option = Options.No
      };
    }
  }

  public enum Options { Yes, No, Maybe };

  public class Data : ObservableObjectBase, IPropertyOwner
  {
    protected void MyPropertyChanged([CallerMemberName] string propertyName = "")
    {
      this.NotifyPropertyChanged(propertyName);
    }

    public List<string> FirstChoices = new List<string>{ "With A", "With B", "With C"};
    public Dictionary<string, List<string>> SecondChoices = new Dictionary<string, List<string>>()
    {
      { "With A", new List<string>() { "A 1", "A 2", "A 3" }},
      { "With B", new List<string>() { "B 1", "B 2", "B 3" }},
      { "With C", new List<string>() { "C 1", "C 2", "C 3" }},
    };

    string firstChoice = "With A";
    [Category("General")]
    [DisplayName("First Choice")]
    public string FirstChoice
    {
      get => firstChoice;
      set
      {
        if (firstChoice != value)
        {
          firstChoice = value;
          MyPropertyChanged();
          SecondChoice = SecondChoices[FirstChoice][0];
        }
      }
    }

    string secondChoice = "A 1";
    [Category("General")]
    [DisplayName("Second Choice")]
    public string SecondChoice
    {
      get => secondChoice;
      set
      {
        if (secondChoice != value)
        {
          secondChoice = value;
          MyPropertyChanged();
        }
      }
    }

    Options option;
    [Category("General")]
    [DisplayName("Some Options")]
    public Options Option
    {
      get => option;
      set
      {
        if (option != value)
        {
          option = value;
          MyPropertyChanged();
        }
      }
    }

    bool showIt;
    public bool ShowIt
    {
      get => showIt;
      set
      {
        if( showIt != value)
        {
          showIt = value;
          MyPropertyChanged();
          NotifyPropertyChanged("It");
        }
      }
    }

    int it;
    public int It
    {
      get => it;
      set
      {
        if( it != value)
        {
          it = value;
          MyPropertyChanged();
        }
      }
    }

    public bool GetPropertyVisibility(string name)
    {
      if (name == "It") return ShowIt;
      return true;
    }

    public IEnumerable GetChoices(string name)
    {
      if (name == "FirstChoice") return FirstChoices;
      if( name == "SecondChoice")
      {
        return SecondChoices[FirstChoice];
      }
      return null;
    }
  }

  interface IPropertyOwner
  {
    bool GetPropertyVisibility(string name);
    IEnumerable GetChoices(string name);
  }

  public class CustomPropertyModel : PropertyDescriptorPropertyModel
  {
    public CustomPropertyModel(object target, PropertyDescriptor propertyDescriptor) : base(target, propertyDescriptor) { }
    public bool IsVisible
    {
      get
      {
        if (this.Target is IPropertyOwner owner) return owner.GetPropertyVisibility(this.Name);
        return true;
      }
    }
    bool updating = false;
    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
      // Call the base method
      base.OnPropertyChanged(e);
      if (!updating)
      {
        updating = true;
        this.NotifyPropertyChanged("IsVisible");
        this.NotifyPropertyChanged("StandardValues");
        updating = false;
      }
    }
    private IEnumerable CustomStandardValues
    {
      get
      {
        if (Target is IPropertyOwner owner) return owner.GetChoices(this.Name);
        return null;
      }
    }
    public override bool HasStandardValues
    {
      get
      {
        return (CustomStandardValues != null ? true : base.HasStandardValues);
      }
    }

    protected override bool IsLimitedToStandardValuesCore
    {
      get
      {
        return (CustomStandardValues != null) || (base.IsLimitedToStandardValuesCore);
      }
    }
    protected override IEnumerable StandardValuesCore
    {
      get
      {
        return CustomStandardValues ?? base.StandardValuesCore;
      }
    }

  }

  public class CustomDataFactory : TypeDescriptorFactory
  {
    protected override IPropertyModel CreatePropertyModel(object target, PropertyDescriptor propertyDescriptor, IDataFactoryRequest request)
    {
      var prop = new CustomPropertyModel(target, propertyDescriptor);
      return prop;
    }
  }
}

Comments (6)

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi John,

Depending on which DataTemplate is pulled for the value editor UI, it could be binding the ComboBox in the DataTemplate to StandardValuesAsStrings.  First, try notifying that property has changed, right after you notify StandardValues has changed.  Let's see if that gets the ComboBox updated.


Actipro Software Support

Posted 3 years ago by John Dunn
Avatar

That did update the choices - now the only thing is that when I assign SecondChoice a valid value based on FirstChoice the editor doesn't show the updated value - it shows an empty choice.

Screenshot-2021-05-25-152002

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi John,

The DataTemplates that use StandardValues also bind to the ValueAsString property (instead of Value), so maybe notify that it was also updated.


Actipro Software Support

Posted 3 years ago by John Dunn
Avatar

That didn't fix the issue. I think the actual issue is that SecondChoice is getting set to an empty string from somewhere inside the Actipro Library

Here's the call stack I see

 	WpfApp3.exe!WpfApp3.Data.SecondChoice.set(string value = "") Line 72	C#
 	[External Code]	
 	WpfApp3.exe!WpfApp3.CustomPropertyModel.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e = {System.ComponentModel.PropertyChangedEventArgs}) Line 164	C#
 	[External Code]	
 	WpfApp3.exe!WpfApp3.CustomPropertyModel.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e = {System.ComponentModel.PropertyChangedEventArgs}) Line 172	C#
 	[External Code]	
 	WpfApp3.exe!WpfApp3.Data.MyPropertyChanged(string propertyName = "SecondChoice") Line 30	C#
 	WpfApp3.exe!WpfApp3.Data.SecondChoice.set(string value = "B 1") Line 75	C#
 	WpfApp3.exe!WpfApp3.Data.FirstChoice.set(string value = "With B") Line 52	C#
 	[External Code]	

You can see the SecondChoice.set with the value 'B 1' at the bottom. The second ( at the top ) is with an empty string. That second set is coming from inside the Actipro library. Here's a more detailed call stack with External Code enabled

 	WpfApp3.exe!WpfApp3.Data.SecondChoice.set(string value = "") Line 72	C#
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	System.dll!System.SecurityUtils.MethodInfoInvoke(System.Reflection.MethodInfo method, object target, object[] args)	Unknown
 	System.dll!System.ComponentModel.ReflectPropertyDescriptor.SetValue(object component = {WpfApp3.Data}, object value = "")	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.PropertyDescriptorPropertyModel.ValueCore.set(object value)	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.CachedPropertyModelBase.Value.set(object value)	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.CachedPropertyModelBase.ValueAsStringCore.set(string value)	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.CachedPropertyModelBase.ValueAsString.set(string value)	Unknown
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.SetValue(object item, object value)	Unknown
 	PresentationFramework.dll!MS.Internal.Data.ClrBindingWorker.UpdateValue(object value)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateSource(object value = null)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.UpdateValue()	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateOverride()	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Update()	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.ProcessDirty()	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Dirty()	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.SetValue(System.Windows.DependencyObject d, System.Windows.DependencyProperty dp, object value)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp = {System.Windows.DependencyProperty}, object value = null, System.Windows.PropertyMetadata metadata = {System.Windows.FrameworkPropertyMetadata}, bool coerceWithDeferredReference = false, bool coerceWithCurrentValue = true, System.Windows.OperationType operationType = Unknown, bool isInternal)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetCurrentValueInternal(System.Windows.DependencyProperty dp, object value)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.UpdatePublicSelectionProperties()	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.SelectionChanger.End()	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemsControl.OnItemCollectionChanged2(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args = {System.Collections.Specialized.NotifyCollectionChangedEventArgs})	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemCollection.OnViewCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.ListenerList<System.Collections.Specialized.NotifyCollectionChangedEventArgs>.DeliverEvent(object sender = {System.Windows.Data.ListCollectionView}, System.EventArgs e = {System.Collections.Specialized.NotifyCollectionChangedEventArgs}, System.Type managerType = {Name = "CollectionChangedEventManager" FullName = "System.Collections.Specialized.CollectionChangedEventManager"})	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.DeliverEvent(object sender, System.EventArgs args)	Unknown
 	WindowsBase.dll!System.Collections.Specialized.CollectionChangedEventManager.OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args = {System.Collections.Specialized.NotifyCollectionChangedEventArgs})	Unknown
 	PresentationFramework.dll!System.Windows.Data.ListCollectionView.RefreshOverride()	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.RefreshInternal()	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.Refresh()	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.EndDefer()	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.DeferHelper.Dispose()	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemCollection.SetCollectionView(System.Windows.Data.CollectionView view)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemCollection.SetItemsSource(System.Collections.IEnumerable value, System.Func<object, object> GetSourceItem)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemsControl.OnItemsSourceChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp = {System.Windows.DependencyProperty}, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry = {System.Windows.EffectiveValueEntry}, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.InvalidateProperty(System.Windows.DependencyProperty dp, bool preserveCurrentValue)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Invalidate(bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.TransferValue(object newValue, bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.ScheduleTransfer(bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!MS.Internal.Data.ClrBindingWorker.NewValueAvailable(bool dependencySourcesChanged, bool initialValue, bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(int k, System.ComponentModel.ICollectionView collectionView, object newValue, bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!MS.Internal.Data.ClrBindingWorker.OnSourcePropertyChanged(object o, string propName)	Unknown
 	PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.ListenerList<System.ComponentModel.PropertyChangedEventArgs>.DeliverEvent(object sender = {WpfApp3.CustomPropertyModel}, System.EventArgs e = {System.ComponentModel.PropertyChangedEventArgs}, System.Type managerType = {Name = "PropertyChangedEventManager" FullName = "System.ComponentModel.PropertyChangedEventManager"})	Unknown
 	WindowsBase.dll!System.ComponentModel.PropertyChangedEventManager.OnPropertyChanged(object sender = {WpfApp3.CustomPropertyModel}, System.ComponentModel.PropertyChangedEventArgs args)	Unknown
 	ActiproSoftware.Shared.Wpf.dll!ActiproSoftware.Windows.ObservableObjectBase.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.PropertyModelBase.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.CachedPropertyModelBase.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)	Unknown
 	WpfApp3.exe!WpfApp3.CustomPropertyModel.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e = {System.ComponentModel.PropertyChangedEventArgs}) Line 164	C#
 	ActiproSoftware.Shared.Wpf.dll!ActiproSoftware.Windows.ObservableObjectBase.NotifyPropertyChanged(string propertyName)	Unknown
 	WpfApp3.exe!WpfApp3.CustomPropertyModel.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e = {System.ComponentModel.PropertyChangedEventArgs}) Line 172	C#
 	ActiproSoftware.Shared.Wpf.dll!ActiproSoftware.Windows.ObservableObjectBase.NotifyPropertyChanged(string propertyName)	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.PropertyModelBase.#QOj()	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.PropertyModelBase.Refresh(ActiproSoftware.Windows.Controls.Grids.PropertyData.PropertyRefreshReason reason)	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.PropertyDescriptorPropertyModel.#o4k()	Unknown
 	ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.PropertyData.PropertyDescriptorPropertyModel.#Cdb.OnPropertyValueChanged(object #xhb, System.EventArgs #yhb)	Unknown
 	System.dll!System.ComponentModel.PropertyDescriptor.OnValueChanged(object component, System.EventArgs e)	Unknown
 	System.dll!System.ComponentModel.ReflectPropertyDescriptor.OnValueChanged(object component, System.EventArgs e)	Unknown
 	System.dll!System.ComponentModel.ReflectPropertyDescriptor.OnINotifyPropertyChanged(object component, System.ComponentModel.PropertyChangedEventArgs e)	Unknown
 	ActiproSoftware.Shared.Wpf.dll!ActiproSoftware.Windows.ObservableObjectBase.OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)	Unknown
 	ActiproSoftware.Shared.Wpf.dll!ActiproSoftware.Windows.ObservableObjectBase.NotifyPropertyChanged(string propertyName)	Unknown
>	WpfApp3.exe!WpfApp3.Data.MyPropertyChanged(string propertyName = "SecondChoice") Line 30	C#
 	WpfApp3.exe!WpfApp3.Data.SecondChoice.set(string value = "B 1") Line 75	C#
 	WpfApp3.exe!WpfApp3.Data.FirstChoice.set(string value = "With B") Line 52	C#
Posted 3 years ago by John Dunn
Avatar

I tried with the 21.1.1.0 version with the same result.

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi John,

Can you wrap up your example above with some XAML into a new simple sample project and send that to our support address?  Then we will debug with steps you give to reproduce it and see what's going on.  Be sure to exclude the bin/obj folders from the .zip you send so it doesn't get spam blocked.  Thanks!


Actipro Software Support

The latest build of this product (v24.1.1) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.