Why is the uieditfield ValueChangingFcn function called twice?

15 views (last 30 days)
To get to know app designer I attempted to create an autocomplete thingy when using a uieditfield. The code is provided below as clear as I could. I cannot discover what's wrong with the code, but it's not working as expected and the callback function is called twice. When I put breakpoints in the callback function, it works as I expect it to (even though it's called twice), but without using breakpoints it doesn't. If I type anything in the uieditfield, then click somewhere else (shift focus), then continue typing in the uieditfield, that's when it gives the autocomplete for the previous value. I'm at a loss, does anyone understand?
clear, clc, close all
% Create figure
fig = uifigure;
% List of names
names = {'Bob';
'Eric';
'John';
'Phil';
'James';
'Anne';
'Suzanne';
'Karen';
'Eleanor';
'Janet';};
% Create table of names
namesTablePos = [28 28 504 203];
namesTable = uitable(fig, ...
'Position', namesTablePos, ...
'ColumnName', 'Names', ...
'Data', names);
% Create editfield
fieldPos = [224, 315, 150, 25];
field = uieditfield(fig, 'text', ...
'ValueChangingFcn', @(h,e) callbackAutoComplete(h,e), ...
'Position', fieldPos);
field.UserData = names;
% Create tree
tree = uitree(fig, ...
'SelectionChangedFcn', @(h,e) callbackSelectionChanged(h,e), ...
'Visible', 'off');
function callbackAutoComplete(src, ~)
% Get figure handle
tree = src.Parent.Children(1);
% Reset tree
tree.Visible = 'Off';
delete(tree.Children)
% Get field value
val = src.Value;
% If value is not empty, populate tree
if ~isempty(val)
% Get names
names = src.UserData;
% Filter names
indices = [];
for ix = 1:numel(names)
name = names{ix};
if contains(lower(name), lower(val))
indices = [indices ix];
end
end
% Set maximum visible suggestions
n = min(numel(indices), 3);
% If any, create nodes
if n > 0
% Set tree properties
fieldPos = src.Position;
treePos = fieldPos + fieldPos(4).*[0 -n 0 (n-1)];
tree.set(...
'Position', treePos, ...
'Visible', 'on');
% Add nodes
names = sort(names(indices));
for ix = 1 : numel(names)
uitreenode(tree, 'Text', names{ix});
end
end
end
end
function callbackSelectionChanged(src, ~)
% Get node value
val = src.SelectedNodes.Text;
% Set value to field
src.Parent.Children(2).Value = val;
end

Accepted Answer

Mario Malic
Mario Malic on 24 Apr 2021
Hey Anais,
That's some nice usage of tree component, thanks for that!
Here are two lines that you need to change in your code. If you inspect the src.Value, it does not populate the value unless the focus is shifted away from the component, so it's better to use event.Value since it uses the values you expected.
function callbackAutoComplete(src, event)
val = event.Value;
Here's your code posted with these two lines so the answer is clear for someone else who may encounter this.
clear, clc, close all
% Create figure
fig = uifigure;
% List of names
names = {'Bob';
'Eric';
'John';
'Phil';
'James';
'Anne';
'Suzanne';
'Karen';
'Eleanor';
'Janet';};
% Create table of names
namesTablePos = [28 28 504 203];
namesTable = uitable(fig, ...
'Position', namesTablePos, ...
'ColumnName', 'Names', ...
'Data', names);
% Create editfield
fieldPos = [224, 315, 150, 25];
field = uieditfield(fig, 'text', ...
'ValueChangingFcn', @(h,e) callbackAutoComplete(h,e), ...
'Position', fieldPos);
field.UserData = names;
% Create tree
tree = uitree(fig, ...
'SelectionChangedFcn', @(h,e) callbackSelectionChanged(h,e), ...
'Visible', 'off');
function callbackAutoComplete(src, event)
% Get figure handle
tree = src.Parent.Children(1);
% Reset tree
tree.Visible = 'Off';
delete(tree.Children)
% Get field value
val = event.Value;
% If value is not empty, populate tree
if ~isempty(val)
% Get names
names = src.UserData;
% Filter names
indices = [];
for ix = 1:numel(names)
name = names{ix};
if contains(lower(name), lower(val))
indices = [indices ix];
end
end
% Set maximum visible suggestions
n = min(numel(indices), 3);
% If any, create nodes
if n > 0
% Set tree properties
fieldPos = src.Position;
treePos = fieldPos + fieldPos(4).*[0 -n 0 (n-1)];
tree.set(...
'Position', treePos, ...
'Visible', 'on');
% Add nodes
names = sort(names(indices));
for ix = 1 : numel(names)
uitreenode(tree, 'Text', names{ix});
end
end
end
end
function callbackSelectionChanged(src, ~)
% Get node value
val = src.SelectedNodes.Text;
% Set value to field
src.Parent.Children(2).Value = val;
end
  6 Comments
Subhajit
Subhajit on 27 Sep 2023
Hi @Voss,
That is a nice hack to get rid of error. However, i still have to click twice to get the correct answer. Thank you for the solution. Have a nice day.
Voss
Voss on 27 Sep 2023
Edited: Voss on 27 Sep 2023
@Subhajit You're welcome!
I don't know why clicking twice is necessary; I noticed that too. If I were attempting to create this UI, I would probably try a uilistbox or uidropdown in place of the uitree (but maybe there's some reason a uitree is preferable - I don't know, I didn't try the uilistbox or uidropdown).
Anyway, you didn't ask about the clicking twice; you only asked about the error, so that's what I fixed.
Have a nice hack (and day)!

Sign in to comment.

More Answers (0)

Categories

Find more on Tables in Help Center and File Exchange

Products


Release

R2021a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!