Codename One back command update form data - codenameone

I have an Android application that uses Back Command to go back to the start screen.
The start screen has a label with a number inside, that I want to update when the back command is used.
I could figure out a solution with the code inside the back command, but I don't know if my approach is the best, since the ClassOne gets sort of loaded twice.
Here is the code I already have:
public class ClassOne {
public ClassOne(ClassPojo classPojo) {
// I want to change the text of this label when calling the back command
labelOne.setText(classPojo.getStringTest());
formOne.show();
}
}
public class ClassTwo {
public ClassTwo(Form a , ClassPojo classPojo) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
// I am adding the new value for the label here inside the back command
classPojo.setStringTest("testing");
a.showBack();
new ClassOne(classPojo);
}
};
formTwo.setBackCommand(back);
}

I'm not sure what the problem is, your example is a bit generic. However, a complete minimal example where the startScreen form instance is not recreated is this one:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
button1.addActionListener(l -> {
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
form2.show();
});
If you don't even want to recreate the form2 instance, then you can do so:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
button1.addActionListener(l -> {
form2.show();
});
In my opinion, whether or not to recreate the instances of a Form should be evaluated on a case-by-case basis. Among the variables between taking into consideration, according to my modest opinion, there is also the readability of the code and what it does, especially in complex cases.

The overhead of recreating a form instance is negligible so that wouldn't be a problem but in recent years we try to reuse form instances more. Not because of the performance.
The benefit is in minor behaviors e.g. scroll position within the form. These are very hard to replicate.

During testing, I found an easy solution that is adding the label to the constructor. I hope this snippet can be helpful.
public ClassTwo(Form a, ClassPojo classPojo, Label label) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
label.setText(classPojo.getStringTest());
a.showBack();
}
};

Related

Why can not update autocomplete suggestions?

I am using two AutocompleteTextFilters as depended filters. I want the second one filter to change its options depending on the suggestion of the first filter.
I have bind an event listener on the first filter so as when it loose focus it triggers a proccess on the second filter.
The proble is that the second filter never changes its options. I even have setup hardcoded values in case somethig was wrong on my code but no luck.
The code I use is below:
public CreateSubmission(com.codename1.ui.util.Resources resourceObjectInstance, Map<String, ProjectType> projectTypes) {
this.projectTypes = projectTypes;
initGuiBuilderComponents(resourceObjectInstance);
gui_ac_projecttype.clear();
gui_ac_projecttype.setCompletion( this.projectTypes.keySet().toArray( new String[0]) );
gui_ac_projecttype.addFocusListener( new ProjectTypeFocusListener( this ));
gui_ac_steps.setCompletion( new String[]{"t10", "t20"});
}
public void makeSteps (String selection) {
ProjectType projectType = this.projectTypes.get( selection );
if (projectType != null) {
this.selectedProjectType = selection;
int length = projectType.projectSteps.length;
String[] steps = new String[ length ];
for(int i =0; i < length; i ++) {
steps[i] = projectType.projectSteps[i].projectStep;
}
// String[] s = gui_ac_steps.getCompletion();
gui_ac_steps.setCompletion( new String[]{"t1", "t2"} );
gui_ac_steps.repaint();
}
else {
}
}
public class ProjectTypeFocusListener implements FocusListener{
private CreateSubmission parent;
public ProjectTypeFocusListener( CreateSubmission parent ) {
this.parent = parent;
}
#Override
public void focusGained(Component cmp) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void focusLost(Component cmp) {
this.parent.makeSteps (
((AutoCompleteTextField)cmp).getText()
);
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
On the above code the initialization happens on "public CreateSubmission" method.
"gui_ac_projecttype" is the first AutocompletionTextField that triggers the whole proccess through it's FocusListener handler (class ProjectTypeFocusListener )
"gui_ac_steps" is the second AutocompleteTextField filter that must change its values. On the code above I initialize it's suggestions to "t10", "t20". Those two values are shown correctly.
Later from iside the FoculListenerHandler's method "ProjectTypeFocusListener.focusLost" I call method "makeSteps" which sets the suggestion options to "t1", "t2 and then I repaint the component. These two last values are never shown. It remains on the first values "t10", "t20".
The Strange thing is that in debugger when I ask gui_ac_steps.getCompletion(); to see the current options ( the code that is commentd out into makeSteps method) I get the correct values "t1", "t2".
But on the screen it keeps showing "t10", "t20".
any help is aprreciated.
You shouldn't do anything "important" in a focus listener. Especially not with a text field. They are somewhat unreliable because the text field switches to native editing and in effect transfers the focus there. The problem is that some events are delayed due to the back and forth with the native editing so by the time the focus event is received you've moved on to the next field.
Try something like this for this specific use case https://www.codenameone.com/blog/dynamic-autocomplete.html

How to code an iPhone style popup menu in CN1?

It has probably been covered before, but I couldn’t google anything. What is the best approach for making an iPhone-style pop-up selection menu like attached picture? I've tried with a Dialog, but I haven't found an elegant way to add the Commands so they appear nicely and both trigger the action and close the dialog at the same time. And showing a Cancel entry separately is not supported by a ComponentGroup.
See this sample:
Form hi = new Form("Pop");
Button pop = new Button("Pop");
pop.addActionListener(e -> {
Dialog dlg = new Dialog();
// makes the dialog transparent
dlg.setDialogUIID("Container");
dlg.setLayout(BoxLayout.y());
Command optionACmd = new Command("Option A");
Command optionBCmd = new Command("Option B");
Command optionCCmd = new Command("Option C");
Command cancelCmd = new Command("Cancel");
dlg.add(
ComponentGroup.enclose(
new Button(optionACmd),
new Button(optionBCmd),
new Button(optionCCmd)
)).
add(ComponentGroup.enclose(new Button(cancelCmd)));
Command result = dlg.showStretched(BorderLayout.SOUTH, true);
ToastBar.showMessage("Command " + result.getCommandName(), FontImage.MATERIAL_INFO);
});
hi.add(pop);
hi.show();
Which results in this:
Thanks Shai!
I made it into a component in case anybody has a similar need:
class MyPopupMenu extends Dialog {
private Command cancelCmd = null;
MyPopupMenu(boolean includeCancel, Command... commands) {
this(includeCancel?new Command("Cancel"):null, commands);
}
MyPopupMenu(Command cancelOptional, Command... commands) {
super();
setDialogUIID("Container");
setLayout(BoxLayout.y());
setDisposeWhenPointerOutOfBounds(true); //close if clicking outside menu
ComponentGroup group = new ComponentGroup();
for (Command cmd : commands) {
group.add(new Button(cmd));
}
add(group);
this.cancelCmd = cancelOptional;
if (cancelCmd != null) {
add(ComponentGroup.enclose(new Button(cancelCmd)));
}
/**
* show the menu and execute the selected Command,
* or do nothing if Cancel is selected
*/
public void popup() {
Command choice = showStretched(BorderLayout.SOUTH, true);
if (choice != null && choice != cancelCmd) {
choice.actionPerformed(null);
}
}
}
This is awesome, thanks guys. Any reason my buttons seem to be so small? Trying to figure out which style needs to change to increase the height. Changing the Button padding did not seem to change anything. I used the Business theme as a starting point.

Why are all of my labels blank on this Windows Form?

I’m trying to show and hide a pop-up when certain events occur. The pop-up is appearing and disappearing properly, but all of its labels are blank. I originally was trying to populate the labels prior to showing the form, but I’ve commented-out all of that logic. The labels are all blank, but the space is properly allocated for each label (see screenshot).
my popup control:
public MyPopUp()
{
InitializeComponent();
}
my separate class:
MyPopUp _MyPopUp;
protected override void OnLoad(IServiceProvider myServiceProvider)
{
_MyPopUp = new MyPopUp();
}
protected override void WhenSomethingHappens() {
_MyPopUp.Show();
}
protected override void WhenSomethingElseHappens() {
_MyPopUp.Hide();
}
Here is part of my designer.cs file. I've only copy/pasted the code from one of the labels, but the other 5 labels on the form have nearly-identical code.
private System.Windows.Forms.Label label1;
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(58, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(41, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Some Label Text";
//
// MyPopUp
//
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "MyPopUp";
this.Text = "Some Text";
this.ResumeLayout(false);
this.PerformLayout();
}
You need to call Refresh() function to force every child control to invalidate, i.e. redraw, its client area.
For your case:
protected override void WhenSomethingHappens() {
_MyPopUp.Show();
_MyPopUp.Refresh();
}
This is happening because you are running the form modelessly.
If you want to you can check that your font/background colours are good etc. by running it Modally:
System.Windows.Forms.Application.Run(YourFormName);
then add the DoEvent line to your modeless call:
Form2 F2 = new Form2();
F2.Show();
System.Windows.Forms.Application.DoEvents();
and that should do it!
I have made a test and found a possibility to make the Label Text disappear. The Label Text disappears if the ForeColor is the same as the BackColor. From your Label-adding code, I see that you didn't specify these colors. WinForms Controls inherit a lot of properties from the parent control (in this case the Form itself), if they are not defined. This probably means that ForeColor and BackColor of the Form are the same and this propagates to the labels. Either change the ForeColor color of the Form or of the Label to fix this issue.
Try to invoke before showing the form, this way the form will be created in the main thread.
if (this.InvokeRequired)
this.BeginInvoke((Action)(() => //'this.' is the form
{
_MyPopUp.Show();
}));

Application Won't 'new' when called

I am calling a new xaml application with some parameter inputs using the word new, but it does not seem to be working. I am also attempting to use onclosing to set it to null. When launching it for the first time it works (everything is new), but launching it after it finished, it seems to continue its previous state (brings to finished score board). Here is the snipplet of the code . . .
quizUI = new QuizzUI.MainWindow(App.User, true);
quizUI.Closed += (o, s) =>
{
quizUI = null;
};
quizUI.LaunchQuiz(qSet);
this is hooked to a button event. does anyone know how i can absolutely new this object's state every time? the two paramters are user's info and second one is to shortcut without prompt screen/loading screen.
Here is the code for QuizzUI.MainWindow.LaunchQuizz:
public void LaunchQuiz(GameQuizzSet quiz)
{
this.Hide();
quizz = new QuizzContainer()
{
QSet = quiz,
};
if (isShortCutted)
{
bool? diag = quizz.ShowDialog();
if (diag.HasValue)
{
quizz.Close();
Close();
}
}
else
{
quizz.ShowDialog();
this.Show();
}
}
the QuizzUI.MainWindow allows the user to select their profile and which quiz to execute.

Best way of designing the flow of code when reading/writing from database

So I'm trying out a concept tool of mine where I need to be able to read and write data from a database real easy. I've set up the form as I like and spread around different text boxes and dropdownboxes to read the data from the database. And I've got it all to work and all, but there's a small bug I don't fully understand why's there. Some textboxes don't update the text from the database. But it seems as it only occurs if the data in the database is nothing. So the value from the last row is still hanging in the textbox and thus, clicking "Update" actually updates the value from the field from the last row, into the new row. Messing everything up.
Now, what I'm the most interested in is the shear flow of the code. What's the best way of laying out the code to do all this? So far I've got this:
This is the code when clicking on a cell in the datagridview:
Private Sub DataGridView_CellClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView.CellClick
On Error Resume Next
selectedName = Me.DataGridView.CurrentRow.Cells(0).Value
selectedGenre = Me.DataGridView.CurrentRow.Cells(1).Value
selectedRhytm = Me.DataGridView.CurrentRow.Cells(2).Value
selectedLength = Me.DataGridView.CurrentRow.Cells(3).Value
selectedFinished = Me.DataGridView.CurrentRow.Cells(4).Value
selectedSoundFile = Me.DataGridView.CurrentRow.Cells(5).Value
txtBoxName.Text = selectedName
txtBoxGenre.Text = selectedGenre
txtBoxRhytm.Text = selectedRhytm
txtBoxLength.Text = selectedLength
txtBoxFinished.Text = selectedFinished
txtBoxSoundFile.Text = selectedSoundFile
End Sub
The "selected"-variables are all declared in a GlobalCode.vb I've got where I create all of them for use later. They are defined like this:
Friend Module GlobalVariables
Friend selectedName As String = Nothing
Friend selectedGenre As String = Nothing
Friend selectedRhytm As String = Nothing
Friend selectedLength As String = Nothing
Friend selectedFinished As String = Nothing
Friend selectedSoundFile As String = Nothing
End Module
I haven't really done anything like this before. I'm more of a designer rather than programmer, but I really need to try out a concept so I'm not sure this is the way of doing this at all. I've found that it works, most of the times. But I reckon skilled programmers have a way of designing the layout of the code so it's efficient, clean and easy to read.
So how does this look?
(I can't see anything database related in the question, btw)
Perhaps the best way of laying out this code is... not to. Don't write code for things that the standard data-binding frameworks can handle. For example (sorry it is C#, but it should translate - all of the "good" bits here are provided by the .NET framework, not the language); some UI code - note no code to copy values:
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
// some sample data
BindingList<Track> tracks = new BindingList<Track>();
tracks.Add(new Track { Name = "foo", Genre = "Rock", Rhythm = "insane", Length = 180 });
tracks.Add(new Track { Name = "bar", Genre = "Classic", Rhythm = "sedate", Length = 240 });
// show the data on a form
using (Form form = new Form {
Controls = {
new DataGridView { DataSource = tracks, Dock = DockStyle.Fill },
new TextBox { DataBindings = {{"Text", tracks, "Name"}}, Dock = DockStyle.Bottom},
new TextBox { DataBindings = {{"Text", tracks, "Genre"}}, Dock = DockStyle.Bottom},
new TextBox { DataBindings = {{"Text", tracks, "Rhythm"}}, Dock = DockStyle.Bottom},
new TextBox { DataBindings = {{"Text", tracks, "Length"}}, Dock = DockStyle.Bottom},
}
}) {
Application.Run(form);
}
}
}
With supporting data entity:
class Track : INotifyPropertyChanged {
private string name, genre, rhythm;
private int length;
public event PropertyChangedEventHandler PropertyChanged;
private void SetField<T>(ref T field, T value, string propertyName) {
if (!EqualityComparer<T>.Default.Equals(field, value)) {
field = value;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public string Name { get { return name; } set { SetField(ref name, value, "Name"); } }
public string Genre { get { return genre; } set { SetField(ref genre, value, "Genre"); } }
public string Rhythm { get { return rhythm; } set { SetField(ref rhythm, value, "Rhythm"); } }
public int Length { get { return length; } set { SetField(ref length, value, "Length"); } }
}
Try commenting out On Error Resume Next for a bit and seeing what happens. I've managed to confuse myself more times than I can count with that statement.
EDIT
I just realized this is VB.Net. You shouldn't ever be using On Error Resume Next in this case. Use Try Catch structures.

Resources