Controlling Cursor/Focus on Phone/Tablet/Web Client while Scanning Barcodes

barandenizbarandeniz Member Posts: 44
Hello, some of our customer needs to use NAV with mobile devices in Warehouses. They want to make processes with bar code system. We have a problem on Mobile clients which is even you disable other fields, making false of editable and quick entry property of the page, after scanning the bar code, the cursor goes away or focuses other field. This behavior makes impossible to make continuous scanning such as Inventory Counting or Adding multiple Items(Bar codes) to i.e Transfer Order. User always have to click the bar code scanning field after scanning a bar code which is very unproductive and time consuming action. Any ideas to resolve this issue?

Best Answers

  • barandenizbarandeniz Member Posts: 44
    Answer ✓
    We got an answer from Microsoft. Bad news..
    Hello,
    Thanks for your feedback.

    I am providing you a copy of our scope agreement for your issue.
    It always not that easy to sell the message about the design limitations of our product Dynamics NAV.
    But we have to handle limitations and boundaries and this s on of them so I hope you at least appreciate that we can give her a clear and solid answer about the problems and limitations which comes with this.

    Issue Definition:
    You are running NAV 2017 with the build 15601 from CU 4 On the NAV 2017 Phone/Tablet Client, after barcode scanning, the barcode field automatically looses it’s focus after barcode scanner’s keystroke. This behavior does not exist on the RTC, so the customer can do serial scanning.

    Hence, we need a solution to keep the cursor and the focus on the barcode field after barcode scanning for a customer to streamline their barcode scanning process.
    Scope Agreement:
    The case is considered as resolved when we clarified the current design on the NAV App and if your plans can be accomplished with the current design of the NAV app.

    We got this question a lot of times in the past and we discussed this with the product team.

    Currently your reported behavior is the intended design of the NAV app.

    “We intentionally do not set focus to the first field on touch devices, because this always results in the soft keyboard appearing. This could be on a phone, tablet or hybrid mobile device. This is a conscious decision based on user studies, where we see the soft keyboard is distracting and space-consuming upon entering the page, especially given that most users still need to swipe to locate the field they would like to edit. In most cases it also results in having to press the Back button twice to exit the page.”

    We reported this problem as feature change request and this is still open as deliverable by the development to have this maybe with a special configuration switch added to control the behavior for the future versions but there are no short terms to have this working.

    But for now and the current versions this is not possible.

    Best Regards,
    Sebastian

Answers

  • barandenizbarandeniz Member Posts: 44
    Answer ✓
    We got an answer from Microsoft. Bad news..
    Hello,
    Thanks for your feedback.

    I am providing you a copy of our scope agreement for your issue.
    It always not that easy to sell the message about the design limitations of our product Dynamics NAV.
    But we have to handle limitations and boundaries and this s on of them so I hope you at least appreciate that we can give her a clear and solid answer about the problems and limitations which comes with this.

    Issue Definition:
    You are running NAV 2017 with the build 15601 from CU 4 On the NAV 2017 Phone/Tablet Client, after barcode scanning, the barcode field automatically looses it’s focus after barcode scanner’s keystroke. This behavior does not exist on the RTC, so the customer can do serial scanning.

    Hence, we need a solution to keep the cursor and the focus on the barcode field after barcode scanning for a customer to streamline their barcode scanning process.
    Scope Agreement:
    The case is considered as resolved when we clarified the current design on the NAV App and if your plans can be accomplished with the current design of the NAV app.

    We got this question a lot of times in the past and we discussed this with the product team.

    Currently your reported behavior is the intended design of the NAV app.

    “We intentionally do not set focus to the first field on touch devices, because this always results in the soft keyboard appearing. This could be on a phone, tablet or hybrid mobile device. This is a conscious decision based on user studies, where we see the soft keyboard is distracting and space-consuming upon entering the page, especially given that most users still need to swipe to locate the field they would like to edit. In most cases it also results in having to press the Back button twice to exit the page.”

    We reported this problem as feature change request and this is still open as deliverable by the development to have this maybe with a special configuration switch added to control the behavior for the future versions but there are no short terms to have this working.

    But for now and the current versions this is not possible.

    Best Regards,
    Sebastian
  • BrimstarBrimstar Member Posts: 14
    1. Lovely, I just wrote a little module for our system using Barcodes and I'm going to need that functionality.

    2. I'd say an add-on using javascript should be possible to set the focus if required. It is certainly a lot more complicated, but assuming you can set it up to call

    document.getElementByID('Barcode Box ID').focus();

    at the end of your routine for processing the barcode that should solve your focus issue.

    On a different note, I find this to be a horrible choice and the original behavior should really be made an option.
  • barandenizbarandeniz Member Posts: 44
    Brimstar wrote: »
    1. Lovely, I just wrote a little module for our system using Barcodes and I'm going to need that functionality.

    2. I'd say an add-on using javascript should be possible to set the focus if required. It is certainly a lot more complicated, but assuming you can set it up to call

    document.getElementByID('Barcode Box ID').focus();

    at the end of your routine for processing the barcode that should solve your focus issue.

    On a different note, I find this to be a horrible choice and the original behavior should really be made an option.

    Thank for you advice. I am a bit new to development side, which variable should i use in order to use document variable. Because you can not use any ".net" objects on mobile clients. If it should be an add in which deployed to server side, can i find it on internet?
  • TimSimmondsTimSimmonds Member Posts: 47
    Hi,
    I was wondering if the OP had resolved their issue? We have implemented a "control add-in" using similiar javascript, as per Barandeniz's suggestion, to keep focus on a "scanning entry" field in the mobile APP. It works well in most browsers and on Android devices... it is just not supported on IOS devices. Any one had any luck with autofocus on an iPhone/iPad?
    Thanks for any suggestions...
  • matwangmatwang Member Posts: 3
    Hi,
    I was wondering if the OP had resolved their issue? We have implemented a "control add-in" using similiar javascript, as per Barandeniz's suggestion, to keep focus on a "scanning entry" field in the mobile APP. It works well in most browsers and on Android devices... it is just not supported on IOS devices. Any one had any luck with autofocus on an iPhone/iPad?
    Thanks for any suggestions...

    Hi Tim,
    wondering if you could give me a hint how you handled the the control add-in to focus the first or any specific input field?
    I created a control-add-in with javascript, and added it at the end of a test page.
    The script looping through all document forms, skipping hiddens forms, to focus on the first element.
    When running the script on a test html page, it's working.
    Unfortunatly it's not working within the nav webclient context.
    Dont't know how to get the input field id, as it seems to be assigned dynamically.


  • TimSimmondsTimSimmonds Member Posts: 47
    Hi Matwang,
    Are you testing on an actual mobile device? If i remember correctly I think the focus would not work for us in a windows web browser for example. It worked on a windows or android mobile device (not iphone!) with the NAV app or a web browser on that device.
    We could only get one field to be set in focus, so we could only have one scan field, at the top of a page for example, that was set with the "focus" control-add in.
    I hope this helps.
    Cheers.
  • matwangmatwang Member Posts: 3
    edited 2020-11-05
    Hi Tim,
    i tested on several web brower using the phone client view phone.aspx, due to lack of a testdevice at the moment.
    i will test it tomorrow within the nav app.

    but i'm afraid that my approach is maybe not the right one, looping through all elements to focus the first non hidden one.

    but when i try to access the field directly with something like this seemed not to work:

    document.getElementByID('MyBarCodeFieldID').focus()

    The documentID seems not to be the name or id of the nav field.
    or maybe the add-in is firing too early before receiving all document elements.

    can you give me a hint how you passed the elementid of the scan field to the focus control-add in?
    maybe a code snippet?

    cheers



  • TimSimmondsTimSimmonds Member Posts: 47
    Hi Matwang.

    Looking at our solution again... maybe it's not entirely what you are looking for.

    We used javascript to insert a single "scan box" field into our NAV page. This then we could control and have focus set to it. The single "scan box" then controls all operation of that page.

    If it helps... our code and setup is below. Please excuse me but once I've worked on anything "fancy" outside of NAV i soon forget it! :)

    From what I remember (and i'm sure we followed the examples given in previous posts)... this is what we have working in NAV2016 and NAV2018:


    Our .Net code is, which produces the dll you add to you "add-in" service folder :
    using Microsoft.Dynamics.Framework.UI.Extensibility;
    
    namespace InputBoxWithFocusControlAddin
    {
        [ControlAddInExport("InputBoxWithFocus")]
        public interface IInputBoxExample
        {
            [ApplicationVisible]
            event ApplicationEventHandler ControlAddInReady;
    
            [ApplicationVisible]
            event DataEventHandler PassDataToNAV;
    
            [ApplicationVisible]
            event DataEventHandler SpecialKeyDownAction;
    
            [ApplicationVisible]
            void SetFocus();
    
            [ApplicationVisible]
            void ClearTextControl();
    
            [ApplicationVisible]
            void SetTextControlValue(string newValue);
        }
    
        public delegate void DataEventHandler(object data);
    }
    


    Then you need the "resources" zip file that you import against your "control add-in" record in nav.
    The zip file contains the:
    • javascript file (script.js)
    • jquery library file (jquery-3.3.1min.js)
    • manifest.xml
    • stylesheet.css


    Our javascript is:
    var navControlContainer;
    
    function onEnterKeyDownAction(ele) {    
        Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("PassDataToNAV", [ele.value]);
        ele.value = "";
    }
    
    function onSpecialKeyDownAction(keyString) {
        Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("SpecialKeyDownAction", [keyString]);
    }
    
    $(document).ready(function () {
        navControlContainer = $('#controlAddIn');
        navControlContainer.empty(); //Clear control before reusing it
        navControlContainer.append('<div class="container"><div class="login"><input type="text" class="pickInputObject" value=""/><div></div>');
        navControlContainer.find('.pickInputObject').keyup(function(event) {                
            if (event.keyCode === 13) {
                if (this.value) {
                  onEnterKeyDownAction(this);
                }
            }
            if (event.keyCode === 27) {
                onSpecialKeyDownAction(event.key);
            }
            if (event.keyCode === 9) {
                if (this.value) {
                    onEnterKeyDownAction(this);
                }
            }        
        });    
    
        SetFocus();
    
        // Send notification to the backend that the control has been fully loaded
        Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('ControlAddInReady', null);
    });
    
    function ClearTextControl() {
        navControlContainer.find('.pickInputObject').val("");
    }
    
    function SetTextControlValue(newTextBoxValue) {
        navControlContainer.find('.pickInputObject').val(newTextBoxValue);
    }
    
    function SetFocus() {
        //focus should be here
        navControlContainer.find('.pickInputObject').focus();
    }
    
    function passDataToNAVPlease(mydata){
        Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("PassDataToNAV", [mydata]);
    }
    



    Our manifest.xml file:
    <?xml version="1.0" encoding="UTF-8"?>
    
    -<Manifest>
    
    
    -<Resources>
    
    <Script>jquery-3.3.1.min.js</Script>
    
    <Script>Script.js</Script>
    
    <StyleSheet>StyleSheet.css</StyleSheet>
    
    </Resources>
    
    
    -<Script>
    
    -<![CDATA[Microsoft.Dynamics.NAV.InvokeExtensibilityMethod ('ControlAddInReady', null);]]>
    </Script>
    
    <RequestedHeight>52</RequestedHeight>
    
    <RequestedWidth>50</RequestedWidth>
    
    <VerticalStretch>true</VerticalStretch>
    
    <HorizontalStretch>true</HorizontalStretch>
    
    </Manifest>
    


    Our stylesheet.css:
    .pickInputObject {
        background-color: rgba(255, 255, 255, 0.85);
        background-color: rgba(255, 255, 255, 0.85);
        border-bottom-color: rgb(171, 171, 171);
        border-bottom-left-radius: 0px;
        border-bottom-right-radius: 0px;
        border-bottom-style: solid;
        border-bottom-width: 1px;
        border-image-outset: 0;
        border-image-repeat: stretch;
        border-image-slice: 100%;
        border-image-source: none;
        border-image-width: 1;
        border-left-color: rgb(171, 171, 171);
        border-left-style: solid;
        border-left-width: 1px;
        border-right-color: rgb(171, 171, 171);
        border-right-style: solid;
        border-right-width: 1px;
        border-top-color: rgb(171, 171, 171);
        border-top-left-radius: 0px;
        border-top-right-radius: 0px;
        border-top-style: solid;
        border-top-width: 1px;
        box-sizing: border-box;
        color: rgb(68, 68, 68);
        font-family: "Segoe UI", "Segoe WP", Segoe, device-segoe, Tahoma, Helvetica, Arial, sans-serif;
        font-size: 13.3333px;
        font-weight: 400;
        margin-bottom: 0px;
        margin-left: 0px;
        margin-right: 0px;
        margin-top: 0px;
        min-height: 28px;
        padding-bottom: 4px;
        padding-left: 4px;
        padding-right: 4px;
        padding-top: 4px;    
        visibility: visible;    
        -moz-appearance: none;
        display: block;
        width: 100%;
    }
    


    As said, we added our "control add-in" setup:

    znrbr4obub7v.png

    Then in the page design we have one field that is using the ControlAddIn (2) that will have focus. We have another field (1) that is a normal NAV field that we use when we are not running the page from the webclient.

    mq1lfx05fuoa.png

  • TimSimmondsTimSimmonds Member Posts: 47
    Example source for page is:
    OBJECT Page 50000 Assemble Whse. Shipment
    {
      OBJECT-PROPERTIES
      {
        Date=27/06/19;
        Time=09:01:00;
        Version List=TDS;
      }
      PROPERTIES
      {
        CaptionML=ENU=Assemble Whse. Shipment;
        PageType=StandardDialog;
        OnOpenPage=BEGIN
                     IF CURRENTCLIENTTYPE = CLIENTTYPE::Phone THEN
                       CurrPage.CAPTION := 'Assemble Shipment';
    
                     IF CURRENTCLIENTTYPE IN [CLIENTTYPE::Web,CLIENTTYPE::Phone,CLIENTTYPE::Tablet] THEN
                       InputBoxWebClientVisible := TRUE
                     ELSE
                       InputBoxWebClientVisible := FALSE;
    
                     //setup display
                     IF PickLineCount = 0 THEN
                       PickLineCountAsText := 'None'
                     ELSE
                       PickLineCountAsText := FORMAT(PickLineCount);
    
                     Field1Caption := LoadNo + '-' + SalesOrderNo + '-' + ItemNo;
    
                     CurrentStepCaption := 'Current Step: Scan Lot No.';
                     IF LastScannedLotNo <> '' THEN
                       CurrentStepText := 'Last Scanned: ' + LastScannedLotNo
                     ELSE
                       CurrentStepText := 'Last Scanned: (blank)';
                   END;
    
        OnQueryClosePage=BEGIN
                           //NOTE: on a StandardDialog type page, this trigger only fires when clicking OK or ESC
                           //MESSAGE(FORMAT(CloseAction));
                           IF CloseAction IN [ACTION::OK,ACTION::LookupOK] THEN
                             OkOnPush;
                         END;
    
      }
      CONTROLS
      {
        { 1900000001;0;Container;
                    ContainerType=ContentArea }
    
        { 1000000012;1;Field  ;
                    Name=Field1Data;
                    CaptionML=ENU=Load Detail;
                    SourceExpr=Field1Caption }
    
        { 1000000000;1;Field  ;
                    Name=CurrentStepText;
                    CaptionML=ENU=Current Step;
                    SourceExpr=CurrentStepText;
                    CaptionClass=CurrentStepCaption;
                    Editable=FALSE }
    
        { 1000000009;1;Field  ;
                    Name=ScannerInputBox;
                    CaptionML=ENU=Scanner Input;
                    SourceExpr=ScannerInput;
                    Visible=NOT InputBoxWebClientVisible;
                    OnValidate=BEGIN
                                 ValidateScannerInput;
                               END;
                                }
    
        { 1000000001;1;Field  ;
                    Name=ScannerInputBox2;
                    Visible=InputBoxWebClientVisible;
                    ControlAddIn=[InputBoxWithFocus;PublicKeyToken=9544f6b1dca3755f] }
    
        { 1000000005;1;Field  ;
                    Name=PickLinesCount;
                    CaptionML=ENU=No. of Pick Lines;
                    SourceExpr=PickLineCountAsText;
                    Editable=FALSE;
                    OnAssistEdit=BEGIN
                                   WarehouseMngt.ShowLoadPickLines(ItemNo,LoadNo,SalesOrderNo);
                                 END;
                                  }
    
        { 1000000004;1;Field  ;
                    Name=QtyRequired;
                    CaptionML=ENU=Qty Required;
                    SourceExpr=OriginalQtyRequired;
                    Editable=FALSE }
    
        { 1000000006;1;Field  ;
                    Name=QtyPicked;
                    CaptionML=ENU=Qty Picked;
                    SourceExpr=QtyPicked;
                    Editable=FALSE }
    
        { 1000000007;1;Field  ;
                    Name=QtyLeftToPick;
                    CaptionML=ENU=Qty Left to Pick;
                    SourceExpr=QtyLeftToPick;
                    Editable=FALSE }
    
      }
      CODE
      {
        VAR
          Load@1000000002 : Record 50000;
          SalesHeader@100000004 : Record 36;
          TempGlobalWaL@1000000020 : TEMPORARY Record 5767;
          WarehouseMngt@1000000012 : Codeunit 50001;
          LoadNo@1000000000 : Code[20];
          SalesOrderNo@1000000001 : Code[20];
          ItemNo@1000000003 : Code[20];
          OK@1000000010 : Boolean;
          InputBoxWebClientVisible@1000000022 : Boolean INDATASET;
          PickLineCountAsText@1000000011 : Text[10];
          PickLineCount@1000000013 : Integer;
          ScannerInput@1000000026 : Text[1024];
          Field1Caption@1000000017 : Text[100];
          CurrentStepText@1000000018 : Text[100];
          CurrentStepCaption@1000000021 : Text[100];
          LastScannedLotNo@1000000016 : Code[20];
          QtyRequired@1000000004 : Decimal;
          QtyPicked@1000000014 : Decimal;
          QtyLeftToPick@1000000015 : Decimal;
          OriginalQtyRequired@1000000019 : Decimal;
    
        PROCEDURE SetParams@1000000001(NewItemNo@1000000001 : Code[20];NewLoadNo@1000000000 : Code[10];NewSalesOrderNo@1000000002 : Code[10];NewLastScannedLotNo@1000000008 : Code[20];NewOriginalQtyRequired@1000000003 : Decimal;NewQtyRequired@1000000006 : Decimal;NewQtyPicked@1000000005 : Decimal;NewQtyLeftToPick@1000000004 : Decimal;NewPickLineCount@1000000007 : Integer);
        BEGIN
          ItemNo := NewItemNo;
          LoadNo := NewLoadNo;
          SalesOrderNo := NewSalesOrderNo;
          LastScannedLotNo := NewLastScannedLotNo;
    
          OriginalQtyRequired := NewOriginalQtyRequired;
          QtyRequired := NewQtyRequired;
          QtyPicked := NewQtyPicked;
          QtyLeftToPick := NewQtyLeftToPick;
          PickLineCount := NewPickLineCount;
        END;
    
        PROCEDURE GetValues@1000000002(VAR SetTempWaL@1000000001 : TEMPORARY Record 5767;VAR SetLastScannedLotNo@1000000002 : Code[20]);
        BEGIN
          SetTempWaL := TempGlobalWaL;
          SetLastScannedLotNo := LastScannedLotNo;
        END;
    
        LOCAL PROCEDURE OkOnPush@1000000010();
        BEGIN
          IF ScannerInput = '' THEN
            IF NOT CONFIRM('No data input. Are you sure you pressed Enter?') THEN
              ERROR('');
        END;
    
        LOCAL PROCEDURE ValidateScannerInput@1000000008();
        BEGIN
          CLEAR(TempGlobalWaL);
          OK := WarehouseMngt.ValidateLotNoOnLoad(
                  ScannerInput,ItemNo,LoadNo,SalesOrderNo,
                  QtyPicked,OriginalQtyRequired,LastScannedLotNo,TempGlobalWaL);
    
          IF OK THEN
            CurrPage.CLOSE;
    
          CurrPage.ScannerInputBox2.SetFocus();
        END;
    
        EVENT ScannerInputBox2@-1000000001::ControlAddInReady@3();
        BEGIN
          CurrPage.ScannerInputBox2.SetFocus();
        END;
    
        EVENT ScannerInputBox2@-1000000001::PassDataToNAV@4(data@1000000000 : Variant);
        BEGIN
          IF data.ISTEXT THEN BEGIN
            ScannerInput := data;
            IF ScannerInput <> '' THEN BEGIN
              ValidateScannerInput;
            END;
          END;
        END;
    
        EVENT ScannerInputBox2@-1000000001::SpecialKeyDownAction@5(data@1000000000 : Variant);
        BEGIN
          CurrPage.CLOSE;
        END;
    
        BEGIN
        {
          Note: ScannerInput var is defined as Text1024 to support scanning of 2D matrix barcodes
          Note2: display fields that should update on refresh need to be set as "enabled:FALSE" (setting to "editable:FALSE" will not work on NAV2016!)
          Note3: NavigatePage type not supported by Phone Client in NAV2016!
        }
        END.
      }
    }
    
Sign In or Register to comment.