Search code examples
firemonkeyc++builderrad-studio

How to "refresh" TListBox?


I'm creating an app which shows bill numbers, like the one you see at Mcdonald's. A POS system send bill numbers to my app and the numbers are showed in a TListBox called "ListBoxPrep". Then, when the POS system sends my app the number of a bill to be removed, my app gets rid of it from "ListBoxPrep" and add it to "ListBoxReady". Every communication between the POS and my app is done via TCP connection and I have no problem with it.

The problem I'm facing is that I still see the number remain in "ListBoxPrep" even after deleting it by "pItem->Free();". "pItem" is a pointer of TListBoxItem. I want the numbers disappear as soon as my app receives the "delete signal" from the POS and especially without user's interation such as clicking the panel etc. I think of using TTimer, but I have no idea how to make "ListBoxPrep" refresh by itself. Do you have any idea to do that? Any suggestion would be appreciated. I'm using RAD Studio 10.4.

After my app received the "delete signal" from the POS, I still see the numbers at right side. They are supposed to disappear. After removing the number from right side

As soon as I click the "ListBoxPrep", the numbers disappear. enter image description here

void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
{
    //We receive data: POS --> Screen(PC)
    String sentDataFromPOS = AContext->Connection->Socket->ReadLn();

    if(sentDataFromPOS .IsEmpty())
    {
        ShowMessage("Data sent from POS is empty!");
        return;
    }

    // 1. Find an order number to move to the right (prep -> ready)
    int indexOrderToRemove = ListBoxPrep->Items->IndexOf(sentDataFromPOS);

    // 2. Add the order number to the "Ready list"
    addNumberToReady(sentDataFromPOS);

    // 3. Remove the order from the "Prep list"
    ListBoxPrep->BeginUpdate();
    TListBoxItem* pItem = ListBoxPrep->ItemByIndex(indexOrderToRemove);
    pItem->Free(); // HERE I have a problem

    // test: To refresh the screen
    LayoutLeft->Visible = false;
    LayoutLeft->Visible = true;

    /*
    ListBoxPrep->Enabled = false;
    ListBoxPrep->Visible = false;
    ListBoxPrep->Enabled = true;
    ListBoxPrep->Visible = true;
    ListBoxPrep->Repaint();
    */
    ListBoxPrep->EndUpdate();
}

Solution

  • TIdTCPServer is a multi-threaded component. Its OnExecute event is called in the context of a worker thread. As such, it MUST synchronize with the main UI thread when accessing UI controls (that goes for ShowMessage() too, BTW). You can use the RTL's TThread::Synchronize() (synchronous) or TThread::Queue() (asynchronous) method for that.

    Also, you should not be Free()'ing the TListBoxItem objects directly. You have the index of the desired item, you can use ListBoxPrep->Items->Delete() instead.

    If you are using one of the clang-based compilers, try something more like this:

    void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
    {
        //We receive data: POS --> Screen(PC)
        String sentDataFromPOS = AContext->Connection->Socket->ReadLn();
    
        if (sentDataFromPOS.IsEmpty())
        {
            TThread::Synchronize(nullptr,
                [](){ ShowMessage("Data sent from POS is empty!"); }
            );
            return;
        }
    
        TThread::Queue(nullptr, // or Synchronize(), your choice...
            [=, this](){ this->orderIsReady(sentDataFromPOS); }
        );
    }
    
    void __fastcall TForm1::orderIsReady(String orderNumber)
    {
        // 1. Find an order number to move to the right (prep -> ready)
        int indexOrderToRemove = ListBoxPrep->Items->IndexOf(orderNumber);
    
        // 2. Add the order number to the "Ready list"
        addNumberToReady(orderNumber);
    
        // 3. Remove the order from the "Prep list"
        if (indexOrderToRemove != -1)
            ListBoxPrep->Items->Delete(indexOrderToRemove);
    }
    

    If, on the other hand, you are using the "classic" Borland compiler, then try this instead:

    struct orderHelper
    {
        String orderNumber;
    
        orderHelper(const String &orderNumber)
            : orderNumber(orderNumber)
        {
        }
    
        void __fastcall orderIsReady()
        {
            Form1->orderIsReady(orderNumber);
        }
    };
    
    void __fastcall TForm1::orderIsEmpty()
    {
        ShowMessage("Data sent from POS is empty!");
    }
    
    void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
    {
        //We receive data: POS --> Screen(PC)
    
        String sentDataFromPOS = AContext->Connection->Socket->ReadLn();
    
        if (sentDataFromPOS.IsEmpty())
        {
            TThread::Synchronize(NULL, &orderIsEmpty);
            return;
        }
    
        orderHelper helper(sentDataFromPOS);
        TThread::Synchronize(NULL, &(helper.orderIsReady));
    }
    
    void __fastcall TForm1::orderIsReady(String orderNumber)
    {
        // 1. Find an order number to move to the right (prep -> ready)
        int indexOrderToRemove = ListBoxPrep->Items->IndexOf(orderNumber);
    
        // 2. Add the order number to the "Ready list"
        addNumberToReady(orderNumber);
    
        // 3. Remove the order from the "Prep list"
        if (indexOrderToRemove != -1)
            ListBoxPrep->Items->Delete(indexOrderToRemove);
    }
    

    Or this:

    struct orderHelper
    {
        String orderNumber;
    
        orderHelper(const String &orderNumber)
            : orderNumber(orderNumber)
        {
        }
    
        void __fastcall orderIsReady()
        {
            try {
                Form1->orderIsReady(orderNumber);
            } __finally {
                delete this;
            }
        }
    };
    
    void __fastcall TForm1::orderIsEmpty()
    {
        ShowMessage("Data sent from POS is empty!");
    }
    
    void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
    {
        //We receive data: POS --> Screen(PC)
    
        String sentDataFromPOS = AContext->Connection->Socket->ReadLn();
    
        if (sentDataFromPOS.IsEmpty())
        {
            TThread::Synchronize(NULL, &orderIsEmpty);
            return;
        }
    
        orderHelper *helper = new orderHelper(sentDataFromPOS);
        TThread::Queue(NULL, &(helper->orderIsReady));
    }
    
    void __fastcall TForm1::orderIsReady(String orderNumber)
    {
        // 1. Find an order number to move to the right (prep -> ready)
        int indexOrderToRemove = ListBoxPrep->Items->IndexOf(orderNumber);
    
        // 2. Add the order number to the "Ready list"
        addNumberToReady(orderNumber);
    
        // 3. Remove the order from the "Prep list"
        if (indexOrderToRemove != -1)
            ListBoxPrep->Items->Delete(indexOrderToRemove);
    }