cancel
Showing results for 
Search instead for 
Did you mean: 

DataTable.Rows.Remove kills SAP

former_member231954
Participant
0 Kudos

I am using SAP 9.2 PL03, C# UI and DI API and trying to delete rows from a Grid's DataTable, but every time I call

Grid oGrid = (Grid)oForm.Items.Item(...ItemID).Specific;
oGrid.DataTable.Rows.Remove(2); // Index does not matter, tried 1 too

SAP just stops working (placed a breakpoint just before the call, the it works until I do call it, and I made sure I have existing rows in the DataTable --> I added 10 rows and tried to delete the first and second).

I tried doing oForm.Freeze(true) before the deletion, but without success, and I tried it during and ItemPress and a Form Open event too. The executing Thread right before the call is MTA (Console.WriteLine( System.Threading.Thread.CurrentThread.GetApartmentState() 😉

Edit:

I could catch the exception:

The server threw an exception. (Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT))

Edit2:

I explicitly ran the code on STA:

Thread thread = new Thread(RunOnSTA);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join();

private void RunOnSTA()
{
    try
    {
        Grid oGrid = (Grid)oForm.Items.Item(....ItemID).Specific;
        oGrid.DataTable.Rows.Remove(2);
    }  
    catch (Exception e)
    {
        Console.Write("asd");
    }
}

Still crashes the SAP client.

Should I just give up and implement my own function where I get the DataTable as an XML string, Parse/Delete the row and Load it back from XML?

This kind of works, but messes up checkboxes for me:

public static void RemoveRowFromGridUsingXML(Grid oGrid, int rowIndex)
{
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(oGrid.DataTable.SerializeAsXML(BoDataTableXmlSelect.dxs_DataOnly));

    XmlNode node = doc.SelectSingleNode($"/DataTable/Rows/Row[{rowIndex}]");
    if (node != null)
    {
        XmlNode parent = node.ParentNode;
        parent.RemoveChild(node);
        string newXML = doc.OuterXml;
        oGrid.DataTable.LoadSerializedXML(BoDataTableXmlSelect.dxs_DataOnly, newXML);
    }
}

Accepted Solutions (1)

Accepted Solutions (1)

former_member231954
Participant
0 Kudos

Okay, It seems like If I do not set the rowheaders (oGrid.RowHeaders.SetValue(i, "indexVal+1")) and try to insert new rows and move around rows at the same time, the next Rows.Delete will fail. (Details in my comments)

Edit: After two years, I ran into this again. I think a better approach is to simply re-render the grid from your own datasets every time you make changes to the rows. Made a Row Model class and I use a utility to write to and read rows from the DataTable (you can set the visibility of specific grid columns in the SDK, so I write everything to the grid, even if I do not want the user to see a DocEntry for example). Next time I need the data, I just read a row or read everything from the DataTable. In the current case, I read the data, removed the row I wanted from my List<T> and wrote everything back. I also use a utility class to apply formatting (like disable rows..etc) and just re-run that as well. Much easier and less error prone.

edy_simon
Active Contributor
0 Kudos

Do you by chance intentionally or unintentionally bind the datatable to two grid objects?
I have problem when I do so.

Regards
Edy

Answers (1)

Answers (1)

ANKIT_CHAUHAN
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Szabolcs Kelemen,

1. Have you tested the same on the latest available version and patch?

2. Can this be reproduced on the DEMO Database on the latest available version and patch?

3. What are the exact steps to reproduce the issue?

4. Are you removing the row from the Grid once the Grid is populated with the data from the query?

5. Any other information about this issue?

Kind regards,

ANKIT CHAUHAN

SAP SME Support

former_member231954
Participant
0 Kudos

1. I haven't, the company I am developing for only has 9.2 PL03 available and no one mentioned version updates in the near future. Also, there are two other addon running, whose developers should be notified too, making this really hard for me.

2. I could not find the DEMO database in our current system. Do you have any resource about the DEMO database installation?

3,4. I made a new custom form using SAP Studio, saved the form as XML. In my addon, I place a new button on some system forms when they are opened. These buttons trigger the my form's opening, which basically means I load the XML and show it using FormCreationParams and Application.Forms.AddEx(). The Grid is empty by default and has a DataTable as a resource (not query based, manual columns). After the form is opened, I manually trigger a few row writes (I first add 8 rows, then manually create a few models and call WriteToGridRow on my model, which implements the method to set cell values on the grid):


public void WriteToGridRow(Grid oGrid, RowIndex rowIndex)
{
    DataTable oDataTable = oGrid.DataTable;

    if (oDataTable.Rows.Count <= rowIndex.AsZeroBased())
    {
        throw new InvalidOperationException($"No such a row exists. RowIndex (Zero based):{rowIndex.IndexAsZeroBased}, DT.Count: {oDataTable.Rows.Count}");
    }
    
    int i = rowIndex.IndexAsZeroBased;
    
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ISCHECKED, i, IsChecked ? "Y" : "N");
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_EXISTING_ITEM, i, IsExisting ? "Y" : "N");
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEM_CODE, i, ItemCode);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEM_DESC, i, ItemName);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_BRAND, i, Brand);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SWW, i, SWW);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SUPPCATNUM, i, SuppCatNum);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SUBCATNUM, i, SubCatNum);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_QUANTITY, i, Quantity);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_BUYUNITMSR, i, BuyUnitMsr);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_VENDOR, i, Vendor);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_PROCPRICE, i, ProcPrice);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_MULTIPLIER, i, Multiplier);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SALESPRICE, i, SalesPrice);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEMMAINCAT, i, ItemMainCat);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEMSUBCAT, i, ItemSubCat);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_VEHICLEMANUF, i, VehicleManufacturer);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_VEHICLETYPE, i, VehicleType);
}


These work as expected, up to this point, I do not trigger any row deletes and the rows are displayed correctly, without error.

I placed a new button on my form, which triggers a new event where I am trying to test the row delete feature (the event was caught without problem and everything works right until I try to call the Remove method): (Check the RunOnSTA method)


public void OnItemEvent(string FormUID, ref ItemEvent pVal, ref bool BubbleEvent)
{
    try
    {
        if (BubbleEvent)
        {
            if (FormUID.Equals(UniqueId))
            {
                if (pVal.BeforeAction)
                {

                }

                if (!pVal.BeforeAction)
                {
                    if (pVal.EventType == BoEventTypes.et_ITEM_PRESSED)
                    {
                        if (pVal.ItemUID.Equals(ItemManagerFormIdentifiers.ID_BUTTON_ELLENORZES.ItemID))
                        {
                            Grid oGrid = (Grid)oForm.Items.Item(ItemManagerFormIdentifiers.ID_GRID_MAIN.ItemID).Specific;
                            string xml = oGrid.DataTable.GetAsXML(); // just for debug

                            try
                            {
                                //  oGrid.DataTable.Rows.Remove(2); // throws exception on MTA too
                                Thread thread = new Thread(RunOnSTA);
                                thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
                                thread.Start();
                                thread.Join(); //Wait for the thread to end
                            } catch (Exception e)
                            {
                                Console.Write("asd");
                            } catch
                            {
                                Console.Write("asd");
                            }
                        //    SBOTools.RemoveRowFromGridUsingXML(oGrid, 2);
                        }
                    }
                }
            }
        }
    }
    catch (Exception e)
    {
        Logger.TraceException(e);
    }
}

private void RunOnSTA()
{
    try
    {
        Grid oGrid = (Grid)oForm.Items.Item(ItemManagerFormIdentifiers.ID_GRID_MAIN.ItemID).Specific;
        oGrid.DataTable.Rows.Remove(2); // <-- SBO Crash
    }  catch (Exception e)
    {
        Console.Write("asd");
    }
}


5. I use my own code to route the events between different modules inside my application, I only use the old event handling, I had lot's of problems using the event properties the SDK presents, but this issue does not seem to depend on the events.
The grid has checkboxes, text and number columns too.

ANKIT_CHAUHAN
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Szabolcs Kelemen,

If you have installed SAP Business One SDK on your Development System, you will find a sample for the Grid at the following location:

C:\Program Files (x86)\sap\SAP Business One SDK\Samples\COM UI\CSharp\19.Grid

Could you please help to edit it for me so that I can check/reproduce this issue my side?

Kind regards,

ANKIT CHAUHAN

SAP SME Support

former_member231954
Participant
0 Kudos

Okay I will do, but I am not in the office now. I will post a new reply on Monday with the details (will try to reproduce the issue using the demo code). Thanks

former_member231954
Participant
0 Kudos

Also, Is there any server side log which could give me more information about the error? (The exception on my side points to the server -->RPC_E_SERVERFAULT )

former_member231954
Participant
0 Kudos

I could not reproduce the issue, I have no idea what is causing the error and could not find any logs that could be helpful. I am stuck.

former_member231954
Participant
0 Kudos

I could somewhat narrow it down, by trying a lot...

I need to move rows around, so I made a method to push rows down and insert a new row at a certain index. This method seems to cause the issue when I am calling the delete (If I do not call the push method beforehand, the delete method succeeds, but If I call it more than once, SAP will fail on row delete. Note: The push method seems to work fine, the rows are in the expected position and no error is thrown until I call a delete).

The push method (the commented out portion does the same as the previous longer, both causes fail on delete):

public void PushDownRowsFromIndex(RowIndex rowIndex)
{
    Grid oGrid = (Grid)oForm.Items.Item(ItemManagerFormIdentifiers.ID_GRID_MAIN.ItemID).Specific;
    DataTable oDataTable = oGrid.DataTable;

    if (oDataTable.Rows.Count == 0)
        return;

    var rowDatas = new List<ItemManagerRow>();
    for (int i = 0; i < oDataTable.Rows.Count; i++)
    {
        var rowData = ItemManagerRow.FromGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
        rowDatas.Add(rowData);
    }

    oDataTable.Rows.Clear();
    oDataTable.Rows.Add(rowDatas.Count + 1);
    for (int i = 0; i < rowDatas.Count + 1; i++)
    {
        if (i == rowIndex.IndexAsZeroBased)
        {
            new ItemManagerRow().WriteToGridRow(oGrid, rowIndex);
        }
        else
        {
            if (i < rowIndex.IndexAsZeroBased)
            {
                rowDatas[i].WriteToGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
            }
            else
            {
                rowDatas[i - 1].WriteToGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
            }
        }
    }
    /*
    oDataTable.Rows.Add();

    for (int i = oDataTable.Rows.Count - 2; i >= rowIndex.IndexAsZeroBased; i--)
    {
        var rowData = ItemManagerRow.FromGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
        rowData.WriteToGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i + 1));
    }
    new ItemManagerRow().WriteToGridRow(oGrid, rowIndex);*/
}<br>

The ItemManagerRow object: https://paste.ubuntu.com/p/JHCvJzGNYp/

Even If I comment out the ItemManagerRow's Write method's body, SAP will freeze on delete.