No BSD License  

Highlights from
PropListener - add a callback to property value get/set event

from PropListener - add a callback to property value get/set event by Yair Altman
Attach a listener callback to a property value get/set event(s)

proplistener(hContainer, hSrc, eventName, callback)
function hdl = proplistener(hContainer, hSrc, eventName, callback)
%  PROPLISTENER  Attach a listener callback to property value change event(s)
%
%  PROPLISTENER(HContainer, HSrc, EventNames, Callback)
%  Adds a listener to each object in HContainer for events
%  originating from each property in HSrc having one of the names listed
%  in EventNames.
%
%  If EventNames contains only property events (PreGet, PreSet, PostGet,
%  or PostSet), hSrc may contain a list of property handles or a list
%  of property names (string or cell array of strings).
%
%  PROPLISTENER always attaches the listener to each container object
%  so that the listener survives as long as the longest-lived
%  container object.
%
%  Callback should be specified in the normal Matlab Callback format:
%  'string', @function_handle or {@function_handle, params...}. A
%  sample callback function is included at the bottom of this m-file.
%  If Callback is empty, the corresponding event listeners are removed.
%
%  PROPLISTENER(HSrc, EventNames, Callback)
%  Adds a listener to each object in HSrc for events originating from
%  each object in HSrc having one of the names listed in EventNames.
%
%  Note: This is an enhancement of Matlab's hidden and unsupported
%        addlistener function within the uitools folder.
%
%  Note: If you wish to modify the property value in set/get, you need to
%  ^^^^  specify a schema.prop SetFunction (or GetFunction) instead of
%        using this function. The reason is that this function is only a
%        listener for set/get events, and not really a programming hook.
%
%        Here's a skeleton for setting a property's SetFunction:
%          set(findprop(handle(gcf),propName), 'SetFunction', @mySetFunc)
%          ...
%          function updatedValue = mySetFunc(hObj,proposedValue)
%             updatedValue = ...
%          end

% Copyright 2003-2005 The MathWorks, Inc.
% Modified 12-Jan-2008 by Yair M. Altman

    % make sure we have handle objects
    hContainer = handle(hContainer);

    % Get schema.prop handle(s) for the specified property/ies
    if (nargin == 3)
        callback = eventName;
        eventName = hSrc;
        hSrc = hContainer;
    elseif ischar(hSrc) && numel(hContainer) == 1
        try
            hSrc = hContainer.findprop(hSrc);
        catch
            error(['Property ''' hSrc ''' was not found in requested object'])
        end
    elseif iscell(hSrc) && numel(hContainer) == 1
        for i = 1:length(hSrc)
            try
                temp(i) = hContainer.findprop(hSrc{i});  %#ok grow
            catch
                error(['Property ''' hSrc{i} ''' was not found in requested object'])
            end
        end
        hSrc = temp;
    end

    % Fix event name(s) & attach property listener(s) if non-empty callback
    hl = handle([]);
    if ischar(eventName)
        eventName = processEventName(eventName);
        hl = attachListener(hContainer, hSrc, eventName, callback);
    elseif iscell(eventName)
        for i = 1:length(eventName)
            eventName{i} = processEventName(eventName{i});
            hTemp = attachListener(hContainer, hSrc, eventName{i}, callback);
            if ~isempty(hTemp)
                hl(i) = hTemp;
            end
        end
    end

    % Return a handle to the newly created listener, if requested
    if nargout > 0
        hdl = hl;
    end

%% Check & fix EventName
function eventName = processEventName(eventName)
    eventName = strrep(lower(eventName),'property','');
    switch eventName
        case {'preset','preget','postset','postget'}
            eventName = ['P' eventName(2:end-3) upper(eventName(end-2)) 'et'];
        otherwise
            error(['unrecognized EventName ''' eventName ''':' char(10) 'only ''PreGet'', ''PreSet'', ''PostGet'' & ''PostSet'' are supported']);
    end
    eventName = ['Property' eventName];

%% Attach a property listener
function hl = attachListener(hContainer, hSrc, eventName, callback)

    % Attach the prop listener, if callback is not empty
    hl = handle([]);
    if ~isempty(callback)
        hl = handle.listener(hContainer, hSrc, eventName, callback);
    end

    % Persist property listeners (or remove if empty callback)
    for i = 1:length(hContainer)
        hC = hContainer(i);
        p = findprop(hC, 'Listeners__');
        if (isempty(p))
            p = schema.prop(hC, 'Listeners__', 'handle vector');
            % Hide this property and make it non-serializable and
            % non copy-able.
            set(p,  'AccessFlags.Serialize', 'off', ...
                'AccessFlags.Copy', 'off',...
                'FactoryValue', [], 'Visible', 'off');
        end
        % filter out any non-handles
        hC.Listeners__ = hC.Listeners__(ishandle(hC.Listeners__));

        % If empty callback, remove (delete) listeners
        if isempty(callback)
            removeIdx = strcmp(get(hC.Listeners__,'EventType'),eventName);
            delete(hC.Listeners__(removeIdx));
            hC.Listeners__(removeIdx) = [];

        % Otherwise, persist them in the listeners list
        else
            hC.Listeners__ = [hC.Listeners__; hl];
        end
    end

%% Sample PropertyPreSet function
function samplePreSet(hProp, eventData, varargin)  %#ok unused
    eventType  = eventData.Type;  %#ok unused
    hContainer = eventData.AffectedObject;
    try
        newValue = eventData.NewValue          %#ok unused
        oldValue = get(hContainer,hProp.Name)  %#ok unused
    catch
        % NewValue is only available in Property sets - not gets!
    end
    
    

Contact us at files@mathworks.com