Problemas con Matrix y ChooseFromList en form

Hola colegas, estoy trabajando con SAP Business One Studio for Visual Studio (UI API) y tengo una duda importante sobre la programación de un formulario personalizado que incluye una Matrix vinculada a un DataTable y columnas con ChooseFromList (CFL). El problema que tengo es que el comportamiento no está siendo el esperado: al cargar el formulario, algunas celdas aparecen inactivas (en gris), otras no muestran el CFL correctamente, y en general no logro que los datos seleccionados desde el CFL se reflejen bien en las celdas correspondientes del DataTable. ¿Cuál sería la forma correcta y ordenada de gestionar la programación del DataTable, la Matrix y el ChooseFromList para que todo funcione de manera fluida, como ocurre en los formularios nativos de SAP? Agradezco mucho cualquier orientación o buenas prácticas que puedan compartir.

Por cierto, estoy intentando realizar el Addon en C#. Se agradece cualquier aporte colegas. Saludos.

Hola, ayudaría mucho si muestras una pantalla y tu código, para orientarte más fácil

Buenas, apreciado Julian… Ok, te comparto algunas capturas y el código de mi proyecto de prueba.

using System;
using SAPbouiCOM;
using SAPbouiCOM.Framework;
using System.Windows.Forms;

namespace PlanifProdAddon
{
    [FormAttribute("PlanifProdAddon.Form1", "Form1.b1f")]
    class Form1 : UserFormBase
    {
        private Timer _cflTimer;
        private bool cflActivado = false;
        private SAPbouiCOM.Matrix Matrix0;

        public Form1()
        {
        }

        public override void OnInitializeComponent()
        {
            // Vincula el control visual de la matrix desde el diseñador
            this.Matrix0 = ((SAPbouiCOM.Matrix)(this.GetItem("mtx_Planif").Specific));
            this.UIAPIRawForm.Mode = BoFormMode.fm_ADD_MODE;
            this.OnCustomInitialize();
        }

        public override void OnInitializeFormEvents()
        {
            // Suscribe el evento global para CFL
            SAPbouiCOM.Framework.Application.SBO_Application.ItemEvent += SBO_Application_ItemEvent;

        }

        private void OnCustomInitialize()
        {
            this.UIAPIRawForm.Mode = BoFormMode.fm_ADD_MODE;

            // Crear el DataTable si no existe
            bool existeDT = false;
            for (int i = 0; i < this.UIAPIRawForm.DataSources.DataTables.Count; i++)
            {
                if (this.UIAPIRawForm.DataSources.DataTables.Item(i).UniqueID == "dtPlanif")
                {
                    existeDT = true;
                    break;
                }
            }

            if (!existeDT)
            {
                this.UIAPIRawForm.DataSources.DataTables.Add("dtPlanif");
            }

            SAPbouiCOM.DataTable dt = this.UIAPIRawForm.DataSources.DataTables.Item("dtPlanif");

            bool existeColumna = false;
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                if (dt.Columns.Item(i).Name == "codigo")
                {
                    existeColumna = true;
                    break;
                }
            }

            if (!existeColumna)
            {
                dt.Columns.Add("codigo", BoFieldsType.ft_AlphaNumeric, 50);
            }


            // Crear ChooseFromList si no existe
            if (!CFL_Existe("CFL_Prod"))
            {
                ChooseFromListCreationParams cflParams =
    (ChooseFromListCreationParams)SAPbouiCOM.Framework.Application.SBO_Application.CreateObject(
        BoCreatableObjectType.cot_ChooseFromListCreationParams);


                cflParams.MultiSelection = false;
                cflParams.ObjectType = "4"; // OITM
                cflParams.UniqueID = "CFL_Prod";
                this.UIAPIRawForm.ChooseFromLists.Add(cflParams);
            }

            // Asociar columna visual a campo del DataTable
            SAPbouiCOM.Column col = Matrix0.Columns.Item("col_Cod");
            col.DataBind.Bind("dtPlanif", "codigo");
            col.Editable = true;

            // Asignar CFL a la columna
            EditTextColumn colCod = col as EditTextColumn;
            if (colCod != null)
            {
                colCod.ChooseFromListUID = "CFL_Prod";
                colCod.ChooseFromListAlias = "ItemCode";
            }

            // Agregar fila si está vacío
            if (dt.Rows.Count == 0)
            {
                dt.Rows.Add();
            }

            // Cargar la matrix después de modificar el DataTable
            Matrix0.LoadFromDataSource();

            // Activar clic solo si:
            // - Hay filas en el DataTable
            // - La matrix tiene por lo menos una fila visual visible
            // - El formulario está en modo adecuado
            if (Matrix0.RowCount > 0 && this.UIAPIRawForm.Mode == BoFormMode.fm_ADD_MODE)
            {
                try
                {
                    // Asegurar que la celda esté activa antes del click
                    //Matrix0.Columns.Item("col_Cod").Cells.Item(1).Click(BoCellClickType.ct_Regular);
                }
                catch (Exception ex)
                {
                    // Mostrar mensaje en la barra de SAP en lugar de lanzar error
                    SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText(
                        "Error al activar CFL automáticamente: " + ex.Message,
                        BoMessageTime.bmt_Short,
                        BoStatusBarMessageType.smt_Error);
                }
            }


            // Agregar fila si está vacío
            if (dt.Rows.Count == 0)
            {
                dt.Rows.Add();
                Matrix0.LoadFromDataSource();
            }
            // Activar CFL automáticamente usando un Timer
            _cflTimer = new Timer();
            _cflTimer.Interval = 500; // medio segundo
            _cflTimer.Tick += (s, e) =>
            {
                try
                {
                    Matrix0.Columns.Item("col_Cod").Cells.Item(1).Click(BoCellClickType.ct_Regular);
                }
                catch (Exception ex)
                {
                    SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText(
                        "Error al activar CFL automáticamente: " + ex.Message,
                        BoMessageTime.bmt_Short,
                        BoStatusBarMessageType.smt_Error);
                }
                finally
                {
                    _cflTimer.Stop();
                }
            };
            _cflTimer.Start();

        }

        private bool CFL_Existe(string uid)
        {
            try
            {
                var test = this.UIAPIRawForm.ChooseFromLists.Item(uid);
                return test != null;
            }
            catch
            {
                return false;
            }
        }

        private void SBO_Application_ItemEvent(string FormUID, ref ItemEvent pVal, out bool BubbleEvent)
        {
            BubbleEvent = true;

            // ⚠️ Evitar errores si pVal es null
            // ⚠️ Validar que el formulario esté cargado antes de usarlo
            if (this.UIAPIRawForm == null || pVal == null)
                return;


            // ⚠️ Validar el evento de activación del formulario y que no sea BeforeAction
            if (FormUID == this.UIAPIRawForm.UniqueID &&
                pVal.EventType == BoEventTypes.et_FORM_ACTIVATE &&
                !pVal.BeforeAction &&
                !cflActivado) // ← bandera para ejecutar una sola vez
            {
                try
                {

                    // Activar el ChooseFromList automáticamente haciendo clic programático
                    Matrix0.Columns.Item("col_Cod").Cells.Item(Matrix0.RowCount).Click(BoCellClickType.ct_Regular);
                    cflActivado = true;
                }
                catch (Exception ex)
                {
                    SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText(
                        "Error al activar CFL automáticamente: " + ex.Message,
                        BoMessageTime.bmt_Short,
                        BoStatusBarMessageType.smt_Error);
                }
            }

            // 🎯 Aquí continúa el resto de tu lógica para capturar los valores del CFL
            if (FormUID == this.UIAPIRawForm.UniqueID &&
                pVal.EventType == BoEventTypes.et_CHOOSE_FROM_LIST &&
                !pVal.BeforeAction)
            {
                try
                {
                    IChooseFromListEvent cfl = (IChooseFromListEvent)pVal;
                    DataTable oDataTable = cfl.SelectedObjects;

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

                    string itemCode = oDataTable.GetValue("ItemCode", 0).ToString();
                    string itemName = oDataTable.GetValue("ItemName", 0).ToString();
                    string uom = oDataTable.GetValue("SalUnitMsr", 0).ToString();

                    int row = pVal.Row - 1;

                    if (row >= 0 && row < Matrix0.RowCount)
                    {
                        SAPbouiCOM.DataTable dt = this.UIAPIRawForm.DataSources.DataTables.Item("dtPlanif");
                        dt.SetValue("codigo", row, itemCode);
                        dt.SetValue("descripcion", row, itemName);
                        dt.SetValue("unidad", row, uom);

                        Matrix0.LoadFromDataSource();
                    }
                }
                catch (Exception ex)
                {
                    SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText(
                        "Error al capturar valores del CFL: " + ex.Message,
                        BoMessageTime.bmt_Short,
                        BoStatusBarMessageType.smt_Error);
                }
            }
        }

Como verán, a modo de realizar la prueba de funcionamiento, lo que hice fue intentar probar con un sólo campo lo del ChooseFromList… Honestamente no manejo mucho lo de la matrix, el datatable y el CFL y este código fue generado por una IA, pero probablemente el código no respeta o no sigue algún orden cronológico que el SAP requiera o reconozca para que pueda funcionar correctamente el evento.

Si se fijan, la columna de código aparece en blanco pero no me deja escribir nada y tampoco aparece el CFL.

Como dije, sería muy útil si pudieran iluminarme y proveerme la estructura base o los criterios que debo considerar para que lo que necesito hacer funcione, que es básicamente crear una matrix dentro de un form nuevo (sea con datatable o userdatasource) y asociar el CFL para la columna de código y descripción (simulando una grilla nativa de SAP para cargar los productos)… Ojalá me haya explicado correctamente para que puedan brindarme su ayuda…

Desde ya, gracias totales.

Tienes muy buena estructurar nada mas que creo te estas saltando algunos detallitos.

Comentarios
Asegúrate de que en tu archivo .b1f las columnas con UniqueID:

  • "col_Cod" (para código),
  • "col_Desc" (para descripción),
  • "col_Uni" (para unidad de medida)

estén bien definidas y tengan habilitado el Editable al menos para "col_Cod".

Adicional te adjunto el archivo con algunas modificaciones que podrias probar a ver si te funciona.

using System;
using SAPbouiCOM;
using SAPbouiCOM.Framework;
using System.Windows.Forms;

namespace PlanifProdAddon
{
    [FormAttribute("PlanifProdAddon.Form1", "Form1.b1f")]
    class Form1 : UserFormBase
    {
        private Timer _cflTimer;
        private bool cflActivado = false;
        private SAPbouiCOM.Matrix Matrix0;

        public Form1() { }

        public override void OnInitializeComponent()
        {
            this.Matrix0 = ((SAPbouiCOM.Matrix)(this.GetItem("mtx_Planif").Specific));
            this.UIAPIRawForm.Mode = BoFormMode.fm_ADD_MODE;
            this.OnCustomInitialize();
        }

        public override void OnInitializeFormEvents()
        {
            SAPbouiCOM.Framework.Application.SBO_Application.ItemEvent += SBO_Application_ItemEvent;
        }

        private void OnCustomInitialize()
        {
            this.UIAPIRawForm.Mode = BoFormMode.fm_ADD_MODE;

            // Crear el DataTable si no existe
            bool existeDT = false;
            for (int i = 0; i < this.UIAPIRawForm.DataSources.DataTables.Count; i++)
            {
                if (this.UIAPIRawForm.DataSources.DataTables.Item(i).UniqueID == "dtPlanif")
                {
                    existeDT = true;
                    break;
                }
            }

            if (!existeDT)
            {
                this.UIAPIRawForm.DataSources.DataTables.Add("dtPlanif");
            }

            SAPbouiCOM.DataTable dt = this.UIAPIRawForm.DataSources.DataTables.Item("dtPlanif");

            // Crear columnas necesarias
            if (!dt.Columns.Contains("codigo"))
                dt.Columns.Add("codigo", BoFieldsType.ft_AlphaNumeric, 50);

            if (!dt.Columns.Contains("descripcion"))
                dt.Columns.Add("descripcion", BoFieldsType.ft_AlphaNumeric, 100);

            if (!dt.Columns.Contains("unidad"))
                dt.Columns.Add("unidad", BoFieldsType.ft_AlphaNumeric, 30);

            // Crear ChooseFromList si no existe
            if (!CFL_Existe("CFL_Prod"))
            {
                ChooseFromListCreationParams cflParams = 
                    (ChooseFromListCreationParams)SAPbouiCOM.Framework.Application.SBO_Application.CreateObject(
                        BoCreatableObjectType.cot_ChooseFromListCreationParams);

                cflParams.MultiSelection = false;
                cflParams.ObjectType = "4"; // OITM
                cflParams.UniqueID = "CFL_Prod";
                this.UIAPIRawForm.ChooseFromLists.Add(cflParams);
            }

            // Asociar columnas visuales
            Matrix0.Columns.Item("col_Cod").DataBind.Bind("dtPlanif", "codigo");
            Matrix0.Columns.Item("col_Desc").DataBind.Bind("dtPlanif", "descripcion");
            Matrix0.Columns.Item("col_Uni").DataBind.Bind("dtPlanif", "unidad");

            // Asignar CFL a la columna CODIGO
            EditTextColumn colCod = Matrix0.Columns.Item("col_Cod") as EditTextColumn;
            if (colCod != null)
            {
                colCod.ChooseFromListUID = "CFL_Prod";
                colCod.ChooseFromListAlias = "ItemCode";
                colCod.Editable = true;
            }

            // Agregar fila inicial si está vacío
            if (dt.Rows.Count == 0)
            {
                dt.Rows.Add();
            }

            Matrix0.LoadFromDataSource();

            // Activar CFL automáticamente al abrir
            _cflTimer = new Timer();
            _cflTimer.Interval = 500;
            _cflTimer.Tick += (s, e) =>
            {
                try
                {
                    Matrix0.Columns.Item("col_Cod").Cells.Item(1).Click(BoCellClickType.ct_Regular);
                }
                catch (Exception ex)
                {
                    SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText(
                        "Error al activar CFL automáticamente: " + ex.Message,
                        BoMessageTime.bmt_Short,
                        BoStatusBarMessageType.smt_Error);
                }
                finally
                {
                    _cflTimer.Stop();
                }
            };
            _cflTimer.Start();
        }

        private bool CFL_Existe(string uid)
        {
            try
            {
                var test = this.UIAPIRawForm.ChooseFromLists.Item(uid);
                return test != null;
            }
            catch
            {
                return false;
            }
        }

        private void SBO_Application_ItemEvent(string FormUID, ref ItemEvent pVal, out bool BubbleEvent)
        {
            BubbleEvent = true;

            if (this.UIAPIRawForm == null || pVal == null)
                return;

            if (FormUID == this.UIAPIRawForm.UniqueID &&
                pVal.EventType == BoEventTypes.et_FORM_ACTIVATE &&
                !pVal.BeforeAction &&
                !cflActivado)
            {
                try
                {
                    Matrix0.Columns.Item("col_Cod").Cells.Item(Matrix0.RowCount).Click(BoCellClickType.ct_Regular);
                    cflActivado = true;
                }
                catch (Exception ex)
                {
                    SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText(
                        "Error al activar CFL automáticamente: " + ex.Message,
                        BoMessageTime.bmt_Short,
                        BoStatusBarMessageType.smt_Error);
                }
            }

            // Captura de valores desde el CFL
            if (FormUID == this.UIAPIRawForm.UniqueID &&
                pVal.EventType == BoEventTypes.et_CHOOSE_FROM_LIST &&
                !pVal.BeforeAction)
            {
                try
                {
                    IChooseFromListEvent cfl = (IChooseFromListEvent)pVal;
                    DataTable oDataTable = cfl.SelectedObjects;

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

                    string itemCode = oDataTable.GetValue("ItemCode", 0).ToString();
                    string itemName = oDataTable.GetValue("ItemName", 0).ToString();
                    string uom = oDataTable.GetValue("SalUnitMsr", 0).ToString();

                    int row = pVal.Row - 1;

                    if (row >= 0 && row < Matrix0.RowCount)
                    {
                        SAPbouiCOM.DataTable dt = this.UIAPIRawForm.DataSources.DataTables.Item("dtPlanif");

                        dt.SetValue("codigo", row, itemCode);
                        dt.SetValue("descripcion", row, itemName);
                        dt.SetValue("unidad", row, uom);

                        Matrix0.LoadFromDataSource();

                        // Agregar nueva fila automáticamente si se está en la última
                        if (row == dt.Rows.Count - 1)
                        {
                            dt.Rows.Add();
                            Matrix0.LoadFromDataSource();
                        }
                    }
                }
                catch (Exception ex)
                {
                    SAPbouiCOM.Framework.Application.SBO_Application.StatusBar.SetText(
                        "Error al capturar valores del CFL: " + ex.Message,
                        BoMessageTime.bmt_Short,
                        BoStatusBarMessageType.smt_Error);
                }
            }
        }
    }
}

Saludos

1 me gusta

Muchas gracias colega. Iré realizando la prueba yem en cualquier caso, si consigo llegar a una solución, también iré compartiendo todo por acá por si a alguien más le sirva la info. Una vez más, gracias por tu tiempo y estamos a las órdenes… Buen fin de semana.