Updating DynamicObject property in ValueTree - listeners not triggered

One of my ValueTree children holds a DynamicObject property. I can update and modify this object without any problems, but doing so does not triggering any ValueTree::Listener methods. Is this a known limitation or should this work?

Maybe it’s worth mentioning that I’m modifying the object by way of a Value which is retrieved using the getPropertyAsValue() method. But this works absolutely fine for all other property types.

FWIW, here is some simple code demonstrating the issue. The valueTreePropertyChanged() method is never triggered, even though one of the valueTree properties is being updated?

/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

  name:             MyComponentPIP

  dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics, juce_gui_basics
  exporters:        XCODE_MAC

  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

  type:             Component
  mainClass:        MyComponent

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once


//==============================================================================
class MyComponent  : public juce::Component, public ValueTree::Listener
{
public:
    //==============================================================================
    MyComponent(): valueTree("testValueTree")
    {
        setSize (600, 400);
        valueTree.addListener(this);
        juce::ValueTree child("child");
        child.setProperty("DyObj", new juce::DynamicObject(), nullptr);
        valueTree.addChild(child, -1, nullptr);
        auto childNode = valueTree.getChild(0);
        //update/change the object
        childNode.getProperty("DyObj").getDynamicObject()->setProperty("NewData", "a string");
    }

    ~MyComponent() override   {}

    void valueTreeChildAdded (juce::ValueTree& parentVt, juce::ValueTree& newVt)
    {
        DBG(newVt.getType().toString());
        
    }
    void valueTreePropertyChanged(juce::ValueTree& vt, const juce::Identifier& prop)
    {
        DBG(prop);
    }
    
    //==============================================================================
    void paint (juce::Graphics& g) override   {}

    void resized() override {}

    //ValueTree::Listener virtual methods....

    void valueTreeChildRemoved (juce::ValueTree&, juce::ValueTree&, int) override {}
    void valueTreeChildOrderChanged (juce::ValueTree&, int, int) override {}
    void valueTreeParentChanged (juce::ValueTree&) override {}
    

private:
    //==============================================================================
    // Your private member variables go here...
    ValueTree valueTree;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyComponent)
};

I’ve run into this as well. You only get a callback if you change to a different DynamicObject. You do not get callback if you change the properties within the DynamicObject.

You could just call sendPropertyChangeMessage manually.

Matt

1 Like

Thanks @matt :+1: I’m not sure how I missed that sendPropertyChangeMessage() method :see_no_evil: That will do the trick nicely.

Close, but no cigar just yet. If I call getPropertyAsValue() and assign to a value, and then call that value’s getValueSource().sendChangeMessage(true); it once again fails to trigger any ValueTree listeners:

        juce::ValueTree child("child");
        juce::var obj = new juce::DynamicObject();
        child.setProperty("DyObj", obj, nullptr);
        valueTree.addChild(child, -1, nullptr);
        auto childNode = valueTree.getChild(0);
        childNode.addListener(this);
        
        childNode.getProperty("DyObj").getDynamicObject()->setProperty("NewData", "a string");
        value = childNode.getPropertyAsValue("DyObj", nullptr);
        value.getValue().getDynamicObject()->setProperty("MoreNewData", "a string");
        
        //this doesn't trigger..
        value.getValueSource().sendChangeMessage(true);
        
        //this triggers..
        //childNode.sendPropertyChangeMessage("DyObj");

I can always pass the entire ValueTree to my property component, but I’d like to know if this is expected behaviour or is something broken somewhere?

Try childNode.sendPropertyChangeMessage(“DyObj”).

Calling value.getValueSource().sendChangeMessage(true) will generate callbacks to Value::Listener::valueChanged, not ValueTree::Listener::valueTreePropertyChanged.

Matt

Yes, I had that marked in my code as working fine. Thanks for the full break down. It’s clear how I can work with this now, and I’m happy to say everything is now working as it should be. Thanks again :+1:

Yes, I had that marked in my code as working fine.
Right, so you did; I missed that. Glad you’ve got it all sorted out.

Matt

1 Like

I just came across the ValueTreePropertyWithDefault class. Seems like this might be a better choice for me in this instance. :+1: