19 de enero de 2021

Aplicaciones de terceros desde nuestros formularios

Artículo original: 3rd party apps from within our forms
https://sandstorm36.blogspot.com/2014/05/3rd-party-apps-from-within-our-forms.html
Autor: Jun Tangunan
Traducido por: Luis María Guayán


Ahora es la 1:30 AM y todavía no puedo volver a dormir, así que hagamos que mi tiempo sea un poco útil. Inspirado por lo que Bernard Bout ha mostrado AQUI, hoy decidí ver de que forma se puede haceruna instancia de Excel. Por favor, lea en enlace anterior antes de proceder a continuar como voy a tratar de separar las cosas para nuestra mejor comprensión.

Las herramientas más básicas del oficio

  • un Shape en nuestro formulario
  • SetParent
  • WinExec
  • FindWindow
  • SetWindowPos

Bernard nos dió un gran punto de partida porque los ingredientes básicos ya están ahí. Y estoy de acuerdo y aprecio que Bernard no haya mostrado todos los trucos porque si los ha hecho, no trataré de entender algunos de ellos y simplemente usaré el truco a ciegas; y no los comprenderé mejor.

Comencemos la deconstrucción y reconstrucción:

¿WinExec es la única forma?

No. Está ya que es uno de los comandos más simples para abrir una aplicación de terceros. Pero hay alternativas como RUN, ShellExecute(), Scripting y algunas más.

Me he dado cuenta de que el objetivo principal y el primer paso es abrir el archivo o la aplicación de cualquiera de las formas posibles y, dado que mi objetivo aquí es Excel, me gusta la automatización cuando se trata de Excel, entonces es la automatización.

¿Donde está la ventana?

El segundo paso después de abrir la aplicación de terceros es controlar la ventana de esa aplicación tratando de encontrarla. Y eso se puede hacer a través de Winapi con FindWindow() de esta manera:

nHwnd = FindWindow(NULL, "Untitled - Notepad")

Donde nHwnd es el identificador de ventana de la aplicación de terceros que queremos. Lo anterior dice, en términos sencillos, busque una instancia de Bloc de Notas recién abierta y sin guardar entre las ventanas abiertas y obtenga el identificador de su ventana para que podamos trabajar más en ella. Ese título es lo que verá como título cuando abra un Bloc de Notas solo.

Si VFP no puede encontrar el identificador de la ventana, nHwnd devolverá 0.

Intente con un formato de archivo xlsx

Como estaba haciendo la automatización, el primer intento se realiza en un archivo xlsx:

Local loExcel As excel.Application, lcFile
loExcel = Createobject('excel.application')
lcFile = Getfile('xls,xlsx')
If !Empty(m.lcFile)
      loExcel.Workbooks.Open(m.lcFile)
      * Get a handle on its Window
      nHwnd = FindWindow('XLMain',Alltrim(Justfname(m.lcFile))+' - Microsoft Excel')
Endif

¡Y falla, no pasó nada! Tarde me doy cuenta de que a pesar de lo que se muestra en la pantalla en la barra de título de Excel 2007, internamente todavía lo está haciendo de la manera anterior como esta:

nHwnd = FindWindow('XLMain','Microsoft Excel - '+Alltrim(Justfname(m.lcFile)))

No hace falta decir que lo anterior funciona. ¡Hasta aquí todo bien!

Intente con un formato de archivo xls

Luego intenté abrir un formato de archivo xls y ¡vuelve a fallar! ¡¡¡Maldito!!! Recordé que cuando abres un archivo xls en Excel 2007, se agregará un título adicional de [Modo de compatibilidad]. Esa es una forma de recordarnos visualmente que actualmente estamos trabajando en un formato de archivo antiguo. Hmmm ... pedazo de alcornoque, lo haré así entonces:

* Attempt to open without that compatibility mode caption
nHwnd = FindWindow(Null, "Microsoft Excel - "+Alltrim(Justfname(m.lcFile)))
If m.nHwnd = 0
      * Failed, so attempt to open with that added compatibility mode caption
      nHwnd = FindWindow('XLMain', 'Microsoft Excel - +Alltrim(Justfname(m.lcFile))+' [Compatibility Mode]')
Endif

Y no se abre correctamente. Quiero decir, se abre pero se abre fuera de mi aplicación por sí solo. ¡¡¡Maldito sea !!!

Perdí mi tiempo tratando de encontrar la combinación correcta en el título interno de la barra de título de Excel aplicando los casos reales de nombre de archivo usando FSO ... todavía no tuve suerte (me di cuenta al final a través de repetidas pruebas, aunque ese caso de caracteres como adecuado, superior e inferior no lo afecte), mezclando y reubicando las palabras en el pie de foto ... de nuevo no tuve suerte ... y estaba a punto de rendirme porque ya he perdido casi 2 horas solo para ese estúpido título interno (hey yo estaba frustrado, ¡LOL!) y estba a punto de archivar todo el proyecto cuando se me ocurrió una idea. ¡¡¡¡Diablos!!!! Estoy haciendo automatización, entonces, ¿qué me impide hacer esto?

nHwnd = FindWindow('XLMain', loExcel.Caption)

Y listo !!!! ¡Una forma muy flexible de asegurarse de encontrar el título correcto de un archivo de Excel abierto sin importar si está en modo de compatibilidad o no, o en cualquier versión de Excel que esté usando! ¡Excelente! Por supuesto, dicho enfoque no se limita a Excel.

Shape de mi corazón

Ahora vamos a la otra parte. Lo que también he notado es que el truco usa un Shape. Y cuando leí y vi el truco por primera vez, mi presunción es que Excel o cualquier aplicación de terceros aparece mágicamente en dicho Shape transformando dicho Shape en esa aplicación de terceros. Pero como ahora estoy tratando de separar las cosas, me doy cuenta de que con o sin ese Shape, podemos abrir esas aplicaciones de terceros dentro de nuestra aplicación.

Entonces, ¿para qué es ese Shape? Dicho Shape invisible/visible (su elección) en el formulario existe solo por una razón. Eso no es para mostrar la aplicación de terceros, sino para servir como una manera fácil de establecer las coordenadas de esas aplicaciones de terceros desde nuestro formulario, ya que es más fácil cambiar el tamaño de un Shape e indicar a dicha aplicación de terceros que "siga" las coordenadas de ese Shape, que hacerlo por código, de forma repetida y a prueba y error.

Cree un Shape, dimensione y colóquelo en el formulario a su gusto, escóndelo si lo desea e indique a la aplicación de terceros que siga sus coordenadas. Muy ingenioso por aquellos que originalmente pensaron en la idea.

¡Excel no se puede hacer clic, no se puede editar y está muy loco!

Para simplificar las cosas, me doy cuenta de que con VFP, aunque "nosotros" podemos ver los objetos, internamente no puede ser visto por VFP. Al igual que cuando agregamos mediante códigos algunos objetos en un Grid, tenemos que hacerlo Visible, de lo contrario, puede verlos pero no puede funcionar correctamente en él. Entonces:

loExcel.Visible = .T.

Entonces, si desea que esto se destaque dentro de nuestro formulario solo con fines de visualización pura, establezca la propiedad Visible en .F.

loExcel.Visible = .F.

Y eso es todo. Todo lo que el usuario puede hacer es desplazarse hacia abajo y hacia arriba para ver su contenido. :)

¿Que hay en el menu?

Pero una vez que haya hecho visible Excel, entonces todo el archivo de Excel estará dentro de nuestro formulario con todo su esplendor como cinta, barra de fórmulas, pestañas de la hoja de trabajo, etc. Bueno, algunos de ustedes pueden quererlo de esa manera, pero yo no. Así que tengo que esconderlos. Simplemente verifique los códigos más adelante para saber cómo hacerlo.

¡Dimensióname!

No es sorprendente que el cambio de tamaño del formulario deje la aplicación de terceros en sus coordenadas originales. Pero esto es fácil, revisa los códigos.

Hacer zoom, guardar, detectar y algo más

Solo revisa los códigos ... Estoy empezando a tener sueño, finalmente...

Resumen:

Hay dos o más cosas que estoy buscando pero que todavía no he podido encontrar una solución y que tampoco me siento cómodo dejando esos asuntos sin resolver. Y lo básico de esos deseos son:

  • Ocultar la barra de título de Excel
  • No permitir arrastrar y soltar

Incluso jugar con uFlags de SetWindowPos no me dio los resultados esperados. Sin embargo, pude utilizar una buen Flag cuando intentamos abrir un nuevo archivo de Excel para que Excel no destruya lentamente sus objetos frente a nuestros ojos cuando lo cerramos.

Seguiré jugando con esto y si encuentro formas, actualizaré esto. O dado que el plan, como de costumbre, es publicar este foro interno de Foxite, que se encuentra entre los foros de desarrolladores más amigables que he visto, con suerte alguien que haya jugado con esto antes que yo (lo siento, siempre llego tarde) pueda compartir con nosotros cómo solucionar esos problemas; luego editaré esta publicación para incluir sus códigos y al colaborador.

El día siguiente:

¡Encontré el eslabón perdido, LOL! El truco para ocultar la barra de título de Excel es, en lugar de buscar propiedades ocultas de Excel para ocultarlas, manipularlo directamente usando estas dos WinAPI, es decir, GetWindowLong y SetWindowLong. Esos son posibles porque ya tenemos el Handle de la ventana. ¡Compruebe en los códigos a continuación cómo se hace!

Códigos:

loTest = Createobject("Form1")
loTest.Show(1)

Define Class Form1 As Form
      AutoCenter= .T.
      Height = 493
      Width = 955
      Caption = 'Excel within our Form'
      _nHwnd = .F.
      _oExcel = .F.

      Add Object Shape1 As Shape With ;
            Top = 36, Left = 6, Height= 445, Width = 936,;
            BackColor = Rgb(255,255,255), BorderColor = Rgb(0,128,192)

      Add Object label1 As Label With ;
            Top = 15, Left = 6, Caption = 'Preview', FontBold = .T.,;
            FontName = 'Calibri', FontSize = 12, AutoSize = .T.

      Add Object label2 As Label With ;
            Top = 12, Left = 836, Caption = 'Zoom', FontBold = .T.,;
            FontName = 'Calibri', FontSize = 12, AutoSize = .T.,;
            Anchor = 9

      Add Object cmdOpen As CommandButton With ;
            Top = 8, Left = 312, Caption = '\<Open', Width = 84, Height = 24

      Add Object cmdSave As CommandButton With ;
            Top = 8, Left = 399, Caption = '\<Save', Width = 84, Height = 24

      Add Object cmdClose As CommandButton With ;
            Top = 8, Left = 486, Caption = '\<Close', Width = 84, Height = 24

      Add Object chkShowTabs As Checkbox With ;
            Top = 12, Left = 732, Caption = 'Show \<Tabs', AutoSize = .T.,;
            Anchor = 9

      Add Object SpinZoom As Spinner With ;
            Top = 10, Left = 882, KeyboardLowValue = 10, SpinnerLowValue = 10,;
            Value = 100, Anchor = 9, Width = 60

      Procedure Load
            Declare Integer SetParent In user32;
                  INTEGER hWndChild,;
                  INTEGER hWndNewParent

            Declare Integer FindWindow In user32;
                  STRING lpClassName, String lpWindowName

            Declare Integer SetWindowPos In user32;
                  INTEGER HWnd,;
                  INTEGER hWndInsertAfter,;
                  INTEGER x,;
                  INTEGER Y,;
                  INTEGER cx,;
                  INTEGER cy,;
                  INTEGER uFlags

            Declare Integer GetWindowLong In User32;
                  Integer HWnd, Integer nIndex

            Declare Integer SetWindowLong In user32 ;
                  Integer HWnd,;
                  INTEGER nIndex,;
                  Integer dwNewLong
      Endproc

      Procedure Resize
            Thisform._SetCoord()
      Endproc

      Procedure Destroy
            If Type('thisform._oexcel') = 'O'
                  This._Clear()
            Endif
      Endproc

      Procedure cmdOpen.Click
            If Type('thisform._oexcel') = 'O'
                  Thisform._Clear()
            Endif
            Thisform._linkapp()
      Endproc

      Procedure cmdSave.Click
            If Type('thisform._oexcel') = 'O'
                  Thisform._oExcel.activeworkbook.Save()
                  Messagebox('Changes made are saved!',64,'Save')
            Else
                  Messagebox('Nothing to save yet!',64,'Opppppssss!')
            Endif
      Endproc

      Procedure cmdClose.Click
            If Type('thisform._oexcel') = 'O'
                  Thisform._Clear()
            Endif
      Endproc

      Procedure SpinZoom.InteractiveChange
            Thisform._oExcel.ActiveWindow.Zoom=Thisform.SpinZoom.Value
      Endproc

      Procedure chkShowTabs.Click
            Thisform._oExcel.ActiveWindow.DisplayWorkbookTabs=This.Value
      Endproc

      Procedure _Clear
            With This
                  With  .Shape1
                        * Show shape
                        .Visible = .T.
                        * Hide window via uFlags
                        SetWindowPos(This._nHwnd, 1, .Left, .Top, .Width, .Height,0x0080)
                  Endwith

                  With ._oExcel
                        * Restore those we have hidden
                        .DisplayFormulaBar = .T.
                        .DisplayStatusBar = .T.
                        .ActiveWindow.DisplayWorkbookTabs=.T.
                        .ActiveWindow.DisplayHeadings=.T.

                        .activeworkbook.Close()
                        .Visible = .F.
                        .Quit
                  Endwith
                  ._oExcel = .F.
            Endwith
      Endproc

      Procedure _HideRibbon
            Local loRibbon
            loRibbon =This._oExcel.CommandBars.Item("Ribbon")
            If m.loRibbon.Height > 0
                  This._oExcel.ExecuteExcel4Macro('Show.Toolbar("Ribbon",False)')
            Endif
      Endproc

      Procedure _linkapp
            Local loExcel As excel.Application, lcFile
            loExcel = Createobject('excel.application')
            lcFile = Getfile('xls,xlsx')
            If !Empty(m.lcFile)
                  loExcel.Workbooks.Open(m.lcFile)

                  * This is so we can tap into it on other methods/events
                  This._oExcel = loExcel

                  With loExcel
                        .Visible = .T.
                        .DisplayAlerts = .F.
                        .Application.ShowWindowsInTaskbar=.F.

                        .DisplayFormulaBar = .F.
                        .DisplayDocumentActionTaskPane=.F.
                        .DisplayStatusBar = .F.

                        * Ensure scroll bars are shown
                        .ActiveWindow.DisplayVerticalScrollBar=.T.
                        .ActiveWindow.DisplayHorizontalScrollBar=.T.

                        * Hide Workbook Tabs
                        .ActiveWindow.DisplayWorkbookTabs=Thisform.chkShowTabs.Value

                        .ActiveWindow.DisplayHeadings=.F.
                        .ActiveWindow.WindowState = -4137  && xlMaximized
                        .ActiveWindow.Zoom=Thisform.SpinZoom.Value

                        * Get a handle on Window
                        nHwnd = FindWindow('XLMain', .Caption)
                  Endwith

                  * Add this so we can work on other methods
                  This._nHwnd = m.nHwnd

                  * Hide Ribbon
                  Thisform._HideRibbon()


                  * Hide the title bar, disallow drag and drop of the excel window
                  Local lnStyle

                  * Get the current style of the window
                  lnStyle = GetWindowLong(nHwnd, -6)

                  * Set the new style for the window
                  SetWindowLong(nHwnd, -16, Bitxor(lnStyle, 0x00400000))

                  * force it inside our form
                  SetParent(nHwnd,Thisform.HWnd)

                  * Size it
                  Thisform._SetCoord()

                  * Hide shape
                  Thisform.Shape1.Visible = .F.
            Endif
      Endproc

      Procedure _SetCoord
            * size it based on Invisible shape
            With This.Shape1
                  SetWindowPos(This._nHwnd, 0, .Left, .Top, .Width, .Height, 2)
            Endwith
      Endproc

Enddefine

Palabras de despedida:

En primer lugar, muchas gracias a Bernard Bout, cuya publicación me sirve de inspiración para este enfoque. Un agradecimiento especial a Yousfi Benameur quien nos compartió también antes de los códigos para ocultar la cinta de Excel en Office 2007.

Siempre que estoy haciendo este estilo de redacción, estoy apuntando a que los lectores que son nuevos en esto intenten comprender el propósito de cada paso / objeto / código. ¿Cuál es el propósito de WinExec aquí, por qué necesitamos conocer el título, cuál es el propósito de la forma en el formulario, etc.?

Otra es que mis lectores provienen de diferentes partes del mundo donde el inglés no es el idioma nativo y supongo que mi enfoque de redacción es una de las razones por las que mi Blog todavía recibe nuevas páginas vistas de vez en cuando. Aunque espero no aburrirte de esta manera. :)

Hora de dormir....


7 de enero de 2021

Guía de uso del Control Ribbon similar a Microsoft Office 365

Traducción libre de la documentación del Control Ribbon similar a Microsoft Office 365 de Doug Hennig.

El proyecto proporciona un Control Ribbon similar al de Microsoft Office 365 para ser usado en formularios VFP

El código fuente del proyecto y la documentación en inglés se encuentra en: https://github.com/DougHennig/Ribbon

Nota: la traducción se hizo con la documentación al día 06 de enero de 2021.


Usando el Control Ribbon

Para agregar un control ribbon a un formulario, suelte una instancia de la clase SFRibbon de SFRibbon.vcx en el formulario y luego escriba el código en algún método del formulario o quizás en el método Init del objeto del ribbon, para agregar pestañas, secciones y botones; consulte la sección "Componentes" para obtener más detalles.

Para implementar el control ribbon en su aplicación, agregue lo siguiente al proyecto:

  • SFRibbon.vcx
  • SFGDIMeasureString.prg
  • SFRibbonDown.png
  • SFRibbonRight.png
  • RibbonThemes.xml

Además, incluya System.app con los archivos instalados de su aplicación.

Componentes

El control ribbon consta de varios componentes, todos los cuales están definidos en la biblioteca de clases visuales SFRibbon.vcx:

  • Una pestaña (la clase SFRibbonTab) es un elemento del menú en la parte superior, como "Home" y "Send/Receive" de la imagen de arriba. Para agregar una pestaña al ribbon, llame al método AddTab, pasando opcionalmente el nombre de la pestaña (si no se pasa, AddTab asigna un nombre único). Establezca la propiedad "Caption" del objeto Tab devuelto por AddTab.

        loTab = Thisform.oRibbon.AddTab('Home')
        loTab.Caption = 'Home'
    

    Las pestañas tienen una propiedad "Selected" que es .T. para la pestaña seleccionada y .F. para todas las demás pestañas. Establezca esta propiedad en .T. para seleccionar una pestaña mediante programación:

        Thisform.oRibbon.Home.Selected = .T.
    
  • Cada pestaña tiene una barra de herramientas (una instancia de la clase SFRibbonToolbar) debajo. No necesita preocuparse por las barras de herramientas, ya que están allí solo para su comodidad: cuando se selecciona una pestaña, su barra de herramientas se hace visible y las barras de herramientas de las otras pestañas están ocultas.

  • Una sección (la clase SFRibbonToolbarSection) es un conjunto de botones con una barra separadora a la derecha y un título en la parte inferior. Puede haber varias secciones en una barra de herramientas. Para agregar una sección, llame al método AddSection de una pestaña (que en realidad agrega una sección a la barra de herramientas de una pestaña), pasando opcionalmente el nombre de la sección (si no se pasa, AddSection asigna un nombre único). Establezca la propiedad Caption del objeto de sección devuelto por AddSection

        loSection = loTab.AddSection()
        loSection.Caption = 'New'
    
  • Hay dos tipos de botones: normales (la clase SFRibbonToolbarButton) y horizontales (la clase SFRibbonToolbarButtonHorizontal). La diferencia es que los botones normales están dispuestos de izquierda a derecha y tienen una imagen de 32 x 32 con un título debajo, mientras que los botones horizontales se apilan uno encima del otro y tienen una imagen de 16 x 16 con un título a la derecha. Puede haber varios botones en una sección. Al hacer clic en un botón, se ejecuta un comando o se muestra un menú desplegable (que se explica a continuación).

    Para agregar un botón normal, llame al método AddButton de una sección, pasando opcionalmente el nombre del botón (si no se pasa, AddButton asigna un nombre único).

    Para agregar un botón horizontal, llame al método AddHorizontalButton de una sección, pasando opcionalmente el nombre del botón (si no se pasa, AddHorizontalButton asigna un nombre único).

    Establezca las propiedades Caption, Image, Command y EnabledExpression del objeto  botón devuelto por AddButton o AddHorizontalButton. Para utilizar un título de varias líneas, incluya un retorno de carro (carácter CHR(13)) en el texto del título. El código en Command se ejecuta a través de la función EXECSCRIPT(), por lo que puede constar de varias declaraciones separadas por retornos de carro si es necesario. EnabledExpression contiene opcionalmente una expresión (como una cadena) que el método Refresh evalúa para determinar si el botón está habilitado.

          loButton = loSection.AddButton()
          with loButton
              .Caption = 'New' + chr(13) + 'Email'
              .Image   = 'newmail.png'
              .Command = 'Thisform.NewMail()' + chr(13) + 'Thisform.Refresh()'
              .EnabledExpression = 'Thisform.IsButtonEnabled()'
          endwith
          loButton = .AddHorizontalButton()
          with loButton
              .Caption = 'Ignore'
              .Image   = 'ignore.png'
              .Command = 'Thisform.Ignore()'
          endwith
    
  • Un botón puede tener un menú desplegable; si es así, aparece una pequeña flecha hacia abajo en el título del botón. El botón tiene un menú si tiene barras. Para agregar una barra al menú, llame al método AddBar del botón, pasando el título de la barra (use "\<" delante del carácter utilizado como tecla de acceso rápido para la barra tal como lo haría con un menú de VFP) , el comando a ejecutar, opcionalmente una imagen para la barra (16 x 16), y opcionalmente una expresión que determina cuando la barra está habilitada (si no se pasa, la barra siempre está habilitada).

    Para agregar una barra separadora, no pase ningún parámetro al método AddBar.

    Para agregar un submenú a una barra de menú, llame al método AddBar del objeto de barra devuelto por AddBar. Por ejemplo, en el código siguiente, el botón "New Items" tiene un menú con cuatro barras: "E-mail Message", "Appointment", un separador y "E-mail Message Using", el último de los cuales tiene un submenú con "More Stationery...", un separador, y barras de "Plain Text", "Rich Text" y "HTML".

          loButton = .AddButton('NewItems')
          with loButton
              .Caption = 'New' + chr(13) + 'Items'
              .Image   = 'newitems.png'
              .AddBar('E-\<mail Message', 'Thisform.Email()', 'newemailsmall.png')
              .AddBar('\<Appointment', 'Thisform.Appointment()', 'appointmentsmall.png')
              .AddBar()
              loBar = .AddBar('E-mail Message \<Using')
              loBar.AddBar('\<More Stationery...', 'Thisform.MoreStationery()')
              loBar.AddBar()
              loBar.AddBar('\<Plain Text')
              loBar.AddBar('\<Rich Text')
              loBar.AddBar('\<HTML')
          endwith
    
  • El control ribbon también puede tener un menú desplegable, que se muestra al hacer clic con el botón derecho en la parte del ribbon que no está cubierta con botones. Para agregar barras al menú del ribbon, llame al método AddBar del ribbon utilizando los mismos parámetros descritos anteriormente. Los submenús también se admiten de la misma manera que se discutió anteriormente.

          Thisform.oRibbon.AddBar('Change Theme to Colorful', ;
              "Thisform.oRibbon.Theme = 'Colorful'" + chr(13) + ;
              "Thisform.Refresh()", , ;
              "Thisform.oRibbon.Theme <> 'Colorful'")
          Thisform.oRibbon.AddBar('Change Theme to Dark Grey', ;
              "Thisform.oRibbon.Theme = 'Dark Grey'" + chr(13) + ;
              "Thisform.Refresh()", , ;
              "Thisform.oRibbon.Theme <> 'Dark Grey'")
    
  • Puede agregar otros tipos de controles además de botones a una sección: llame a AddControl, pasando el nombre (opcional), la clase y la biblioteca para el control. Asegúrese de llamar al método CalculateWidth para la sección, para que el ancho de la sección se ajuste en consecuencia. Por ejemplo, este código agrega un cuadro de texto:

          loControl = loSection.AddControl('Test', 'textbox', '')
          with loControl
            .FontName = 'Segoe UI'
            .Width    = 200
    		.Height   = 24
    		.Top      = int((loSection.Height - .Height)/2) && center it vertically
    	endwith
    	.CalculateWidth()
    
  • Puede hacer referencia a objetos jerárquicamente por el nombre, comenzando desde el ribbon. Por ejemplo, Thisform.oRibbon.Home.Toolbar.New.NewEmail, puede hacer referencia al botón NewEmail en la sección "New" de la barra de herramientas de la pestaña Home" en el ribbon. Esta es la razón principal para pasar un nombre a los distintos métodos Add*; de lo contrario, tiene que usar los nombres asignados automáticamente al objeto, algo como Thisform.oRibbon.Tab1.Toolbar.Section1.Button1.

Comportamiento

El control ribbon tiene el siguiente comportamiento:

  • La pestaña actualmente seleccionada tiene una barra debajo (una barra azul en la primera captura de pantalla anterior; el color se especifica usando el color "tabbordercolor" en el tema actual; consulte la sección "Temas" a continuación) y su título está en negrita.
  • Al pasar el mouse sobre una pestaña, se resalta la pestaña en el color "tabhighlightcolor" especificado por el tema actual y, si es la pestaña seleccionada, se alarga la barra debajo de la pestaña.
  • Hacer clic en una pestaña la convierte en la seleccionada y muestra la barra de herramientas para esa pestaña.
  • Al pasar el mouse sobre un botón, se resalta el botón en el color "buttonhighlightcolor" especificado por el tema actual.
  • Al hacer clic en un botón se ejecuta el comando para ese botón. Si no se especificó ningún comando y no hay un menú para el botón (consulte el siguiente punto), aparece un cuadro de mensaje con "No implementado".
  • Aparece una flecha hacia abajo en el título de un botón si tiene un menú. Al hacer clic en el botón, se muestra el menú. Al hacer clic en el botón nuevamente, hacer clic fuera del menú o mover el mouse fuera del formulario, se oculta el menú.
  • Al pasar el mouse sobre una barra de menú, se resalta la barra en el color "menuitemhighlightcolor" especificado por el tema actual.
  • Al hacer clic en una barra se ejecuta el comando para esa barra. Si no se especificó ningún comando y no hay un submenú para la barra (ver el siguiente punto), aparece un cuadro de mensaje con "No implementado".
  • Aparece una flecha hacia la derecha en el título de una barra si tiene un submenú. Los submenús se comportan como menús.

Temas

Un tema define un conjunto de colores para el control ribbon. El tema predeterminado es el tema "Colorido" (Colorful)

Aquí está el tema "Gris oscuro" (Darkgrey):

Los temas se definen en el archivo RibbonThemes.xml:

<themes>
	<theme name="Colorful" 
		ribbonbackcolor="243,242,241" 
		ribbonbordercolor="210,208,206" 
		ribbonshadowcolor1="234,234,234"
		ribbonshadowcolor2="238,238,238"
		ribbonshadowcolor3="243,243,243"
		ribbonshadowcolor4="247,247,247"
		ribbonshadowcolor5="251,251,251"
		menuitembackcolor="255,255,255"
		menuitemhighlightcolor="210,208,206"
		menuseparatorcolor="225,223,221"
		buttonhighlightcolor="200,198,196"
		tabhighlightcolor="250,249,248"
		tabbordercolor="16,110,190"
		sectionseparatorcolor="179,176,173"
	/>
	<theme name="Dark Grey" 
		ribbonbackcolor="190,187,184" 
		ribbonbordercolor="68,68,68" 
		ribbonshadowcolor1="75,75,75"
		ribbonshadowcolor2="82,82,82"
		ribbonshadowcolor3="88,88,88"
		ribbonshadowcolor4="95,95,95"
		ribbonshadowcolor5="102,102,102"
		menuitembackcolor="243,242,241"
		menuitemhighlightcolor="200,198,196"
		menuseparatorcolor="210,208,206"
		buttonhighlightcolor="151,149,147"
		tabhighlightcolor="200,198,196"
		tabbordercolor="32,31,30"
		sectionseparatorcolor="121,119,117"
	/>
</themes>

Los valores numéricos son colores RGB.

Los atributos del XML son:

  • ribbonbackcolor: El color de fondo del ribbon.
  • ribbonbordercolor: el color de la línea del borde en la parte inferior del ribbon.
  • ribbonshadowcolor1, ribbonshadowcolor2, ribbonshadowcolor3, ribbonshadowcolor4 y ribbonshadowcolor5: Hay una "sombra" debajo de la línea del borde en la parte inferior del ribbon. Esta sombra en realidad consta de cinco líneas de un píxel de altura. Estos atributos especifican los colores de esas líneas.
  • menuitembackcolor: El color de fondo de una barra en un menú.
  • menuitemhighlightcolor: El color de fondo de una barra cuando se mueve el mouse.
  • menuseparatorcolor: el color de una barra separadora en un menú.
  • buttonhighlightcolor: El color de fondo de un botón cuando el mouse está sobre él.
  • tabhighlightcolor: El color de fondo de una pestaña cuando el mouse está sobre él.
  • tabbordercolor: el color de la línea del borde debajo de una pestaña.
  • sectionseparatorcolor: El color de la línea de separación a la derecha de una sección.

Para establecer el tema del ribbon, establezca la propiedad "Theme" con el nombre del tema deseado (actualmente solo "Colorido" y "Gris oscuro"). La propiedad "Theme" del ribbon contiene una colección de los nombres de temas disponibles. El formulario Sample.scx incluido con este proyecto utiliza un cuadro combinado con Thisform.oRibbon.Themes como RowSource y Thisform.oRibbon.Theme como ControlSource para elegir el tema del ribbon.

Clases

Todas las clases de los componentes del control ribbon están en SFRibbon.vcx.

  • SFRibbonBase: la clase principal para la mayoría de los componentes del ribbon, excepto como se muestra; una subclase de Container.
  • SFRibbon: la clase de ribbon.
  • SFRibbonTab: la clase de Tab.
  • SFRibbonToolbar: la barra de herramientas de una pestaña.
  • SFRibbonToolbarSection: la clase de sección.
  • SFRibbonToolbarButton: una clase de botón normal.
  • SFRibbonToolbarButtonHorizontal: una clase de botón horizontal; una subclase de SFRibbonToolbarButton.
  • SFRibbonMenu: una clase utilizada para definir menús; una subclase de Custom.
  • SFRibbonMenuBar: la clase de la barra de menú; una subclase de SFRibbonToolbarButtonHorizontal.
  • SFRibbonMenuSeparator: la clase de barra separadora de menú.
  • SFRibbonMenuForm: una clase de formulario que proporciona un menú desplegable; una subclase de Form.

5 de enero de 2021

Control Ribbon similar a Microsoft Office 365

Artículo original: Microsoft Office 365-like Ribbon Control
https://doughennig.blogspot.com/2020/12/microsoft-office-365-like-ribbon-control.html
Autor: Doug Hennig
Traducido por: Luis María Guayán


¡Felices Fiestas! Cada año, entre Navidad y Año Nuevo, me gusta trabajar en algún proyecto divertido, como aprender una nueva técnica o escribir alguna utilidad interesante. El año pasado, trabajé en la versión de código abierto de Hacker's Guide to Visual FoxPro. Este año, he creado un control ribbon de código abierto similar a Microsoft Office 365 para formularios VFP: Microsoft Office 365-like ribbon control.

Aquí hay un formulario de ejemplo que contiene el control ribbon similar a Microsoft Outlook. Tiene dos pestañas: "Inicio" y "Enviar / Recibir". Al hacer clic en una pestaña, se muestra la barra de herramientas de dicha pestaña.

Aquí está la pestaña "Enviar / Recibir":

Hasta ahora, he incluido dos temas: "Colorido" (arriba) y "Gris oscuro" (abajo):

Los temas se definen como colores RGB en un archivo XML, por lo que agregar otro tema es muy sencillo.

El control ribbon es 100% código VFP. Pruébelo y avíseme de cualquier mejora que desee, o cualquier problema que encuentre en el repositorio del proyecto en GitHub: Microsoft Office 365-like ribbon control.

Descarga directa del código del control ribbon: https://github.com/DougHennig/Ribbon/archive/master.zip


Consulte la traducción al español de la documentación del Control Ribbon en:

"Guía de uso del Control Ribbon similar a Microsoft Office 365"

https://comunidadvfp.blogspot.com/2021/01/guia-de-uso-del-control-ribbon-similar.html