Themed Controls Project Issues

Topics: General
Coordinator
Jul 2, 2008 at 6:11 PM
Edited Jul 2, 2008 at 6:23 PM
Hi Emerson.

I've been working with the Themed Controls project over the past week and love it. Great job! Here are some minor issues I've run into:

  • ThemedTitleContainer: when the container is resized, all controls but the line at the bottom are resized. I added this to the ChangeTheme method to fix this:

    *** DH 06/28/2008: adjust the width of the line as well.
    This.linTitle.Width = This.Width - 2
    This.linTitle.Anchor = 0
    This.linTitle.Anchor = 11
    *** DH 06/28/2008: end of new code
  • I added this code to the start of the Init method of ThemedTitleContainer so I don't have to create a ThemesManager object in other code (this allows me to create a standalone form that doesn't require a calling PRG to create the ThemesManager):

    *** DH 06/30/2008: create a ThemesManager object so we don't depend on someone
    *** else doing it.

     

     

    if type('_screen.ThemesManager') <> 'O'
      _screen.NewObject('ThemesManager', 'ThemesManager', This.ClassLibrary)
    endif
    *** DH 06/30/2008: end of new code

     

     

  • ThemedTitlePage: if you change the size of the pageframe, you have to manually change the size of the TitleContainer object on every page. This code I added to the Init method does it automatically at runtime:

    *** DH 06/28/2008: adjust the size of the container to fill the pageframe

     

     

    local lnAnchor
    with This.TitleContainer
      lnAnchor = .
    Anchor
     
    .Anchor = 0
      .
    Width = This.Parent.Width
      .Height = This.Parent.Height
     
    .Anchor = lnAnchor
    endwith
    dodefault
    ()
    *** DH 06/28/2008: end of new code

  • Panel and OverflowPanel: I changed the NewObject statement in the AddButton method of both of these classes to use This.ClassLibrary rather than hard-coding the VCX name so the path to the VCX isn't a problem (as above, this allows me to create a standalone form that doesn't require a calling PRG that sets the path to the location of the VCX):

    *** DH 06/30/2008: use our own VCX so the path is included
    * .Newobject("Button"+Alltrim(Str(.ControlCount + 1)),;

     

     

    * "PanelButton","OutlookNavBar",,;
    * lcCaption,lcPicture)
    .Newobject("Button"+Alltrim(Str(.ControlCount + 1)),;
      "PanelButton",
    This.ClassLibrary,,;
      lcCaption,lcPicture)

  • OutlookNavBar: I wanted the Outlook bar to resize when I resize the form so I changed Anchor to 15 on an instance I dropped on a form. However, after clicking the shrink button to shrink and then expand the control, resizing stopped working. That's because the Anchor value is hard-coded in the ButtonClicked method of the ShrinkButton object. I added a new nAnchor property to OutlookNavBar and changed the code in ShrinkButton.ButtonClicked to save and restore the Anchor property rather than hard-coding it at 7 when the control is expanded:

      If llShrunk
    *** DH 06/28/2008: save current Anchor
       
    .nAnchor = .Anchor
    *** DH 06/28/2008: end of new code
       
    .Anchor = 0
       
    This.Tag = Alltrim(Str(.Width))
        .
    Width = 33
        .
    Anchor = 7
        .Panes.
    SetAll("Visible",.F.)
        .Panes.
    Visible = .F.
     
    Else
       
    .Anchor = 0
        .
    Width = Int(Val(This.Tag))
    *** DH 06/28/2008: restore saved Anchor
    *   .Anchor = 7
       
    .Anchor = .nAnchor
    *** DH 06/28/2008: end of new code
I made these changes in my local copy, not the one on CodePlex. I hope you can implement these changes in the next version. Thanks again for some extremely cool controls!

Doug
Developer
Jul 3, 2008 at 5:45 PM
Hi Doug.
I've implemented your suggestions.
I'm working on a new version with a lot of changes to try to improve the performance.
ASAP I'll post a test version.

Emerson
Coordinator
Jul 3, 2008 at 9:57 PM
Awesome work guys. This is exactly the kind of interaction I'm hoping for on all VFPX projects some day.
Coordinator
Jul 9, 2008 at 11:20 PM
Edited Jul 9, 2008 at 11:21 PM
Hi Emerson.

A couple of other things:

  • I added the same code to PanelButton.Init to create a ThemesManager object that I mentioned above for ThemedTitleContainer.Init.
  • I added code to the start of OutlookNavBar.SelectedButton_Assign to prevent an error if an invalid value is specified:

    Lparameters vNewVal
    *** DH 07/08/2008: handle illegal value passed in
    if not between(m.vNewVal, 1, This.Panel.ControlCount)
     
    return
    endif
    *** DH 07/08/2008: end of new code

Doug

Coordinator
Jul 10, 2008 at 7:51 PM
Hi Emerson.

One other thing. I'm implementing "go back" behavior in a form using the Outlook bar, so I need to save the currently selected button when the user clicks a different button. OutlookNavBar.ButtonClicked is too late because SelectedButton has already been changed. The logical place to do this in a subclass or instance of OutlookNavBar is at the start of SelectedButton_Assign. However, that method is hidden. So, I suggest changing that method to public. I've done this in my copy and now when the user clicks on various buttons, they can click a Go Back button to move back to the previous selection.

Doug
Developer
Jul 11, 2008 at 1:04 PM
Hi Doug.
I've implemented the code to handle an illegal value for SelectedButton property.
It is little different than your suggestion but will work also.
Thanks a lot for your help!


DougHennig wrote:
Hi Emerson.

A couple of other things:

  • I added the same code to PanelButton.Init to create a ThemesManager object that I mentioned above for ThemedTitleContainer.Init.
  • I added code to the start of OutlookNavBar.SelectedButton_Assign to prevent an error if an invalid value is specified:

    Lparameters vNewVal
    *** DH 07/08/2008: handle illegal value passed in
    if not between(m.vNewVal, 1, This.Panel.ControlCount)
     
    return
    endif
    *** DH 07/08/2008: end of new code

Doug




Developer
Jul 11, 2008 at 1:15 PM
Hi Doug!
What do you think is better?
1) Make SelectedButton_Assign method public;
2) Create a hook method named BeforeChangeSelectedButton(lnOldButton,lnNewButton) and call it in SelectedButton_Assign.


DougHennig wrote:
Hi Emerson.

One other thing. I'm implementing "go back" behavior in a form using the Outlook bar, so I need to save the currently selected button when the user clicks a different button. OutlookNavBar.ButtonClicked is too late because SelectedButton has already been changed. The logical place to do this in a subclass or instance of OutlookNavBar is at the start of SelectedButton_Assign. However, that method is hidden. So, I suggest changing that method to public. I've done this in my copy and now when the user clicks on various buttons, they can click a Go Back button to move back to the previous selection.

Doug


Coordinator
Jul 11, 2008 at 4:13 PM
Hi Emerson.

The BeforeChangeSelectedButton method is probably a cleaner design. Thanks.

Doug
Coordinator
Jul 11, 2008 at 8:08 PM
Edited Jul 11, 2008 at 8:09 PM
Hi Emerson.

I hope I'm not drowning you in requests, but here are a couple more:

  • ThemedTitleContainer: set imgTitle.Stretch to 1 so someone can use a different image size (for example, a 48x48) and have it scale properly. The same is also true for OverflowPanelButton.imgTitle.
  • Add a FontName property with an Assign method to the Title and ThemedTitleContainer classes with this code in the Assign method: 

    *** DH 07/11/2008: added FontName property and Assign method

     

     

    lparameters tuNewValue
    store tuNewValue to This.FontName, This.lblCaption.FontName

    This allows me to do something like This.SetAll('FontName', 'Segoe UI') because I like to use that font when the user is running in Vista. Because those two classes are Control, they hide the lblCaption member and SetAll can't change the font.

Thanks again!

Doug

Coordinator
Jul 12, 2008 at 1:52 PM
If anyone is interested, I've created a short video (about 9.5 minutes) showing an example of how fantastic Emerson's Themed Controls project can make your forms look.

More material on Themed Controls: I'm doing a session on explorer interfaces at Southwest Fox (and possibly the German DevCon) that uses the controls in this and several other VFPX projects, Rick Schummer is also doing a session at Southwest Fox (and possibly the German DevCon) on VFPX controls, and I believe he has an article in an upcoming issue of FoxRockX on how to use the Outlook bar control.

Great job, Emerson!

Doug
Developer
Jul 29, 2008 at 12:49 PM
Hi Doug!
A new ThemedControls version including a help file, bug fixes and a lot of changes is available.
So, I think that you'll need to make little changes in your video.
Sorry for this.

Thanks for you, Rick and Craig for the support!
Cheers.
Coordinator
Aug 18, 2008 at 9:23 PM
Hi Emerson.

I just have a few more requests:

  • Make ThemesManager.Themes public. That allows me to create my own menu or form to allow the user to select the theme. I know Rick Schummer has suggested adding properties used by ShowPanel to allow specifying your own form, and this request doesn't take away from that, but making Themes public also allows it to be used in a menu rather than just a form. For example, I added the themes back to the Outlook menu (the items you removed from that menu) in a subclass.
  • If you add the following code to ThemesManager.LoadThemes, a developer doesn't have to SET PATH TO the folder containing ThemedControls.VCX. In that case, a copy of Themes.XML should be in the VCX folder as well.

*** DH 07/29/2008: look for file in VCX folder if not found
if not file(m.lcXML)
 lcXML = forcepath(lcXML, justpath(This.ClassLibrary))
endif not file(m.lcXML)
*** DH 07/29/2008: end of new code
If File(m.lcXML)

  • The following change in the InitThemedControl method of ThemedContainer and ThemedForm means I don't have to SET PATH either:

*** DH 07/29/2008: use This.ClassLibrary so path included
* _Screen.Newobject("ThemesManager","ThemesManager","ThemedControls.vcx")
 _Screen.Newobject("ThemesManager","ThemesManager",This.ClassLibrary)

  • This change in the InitThemedControl method of OutlookNavBar, OverflowPanel, OverflowPanelButton, Panel, PanelButton, PanelVertical, ShrinkButton, SplitterHorizontal, and Title also prevents the need to SET PATH:

*** DH 07/29/2008: use This.ClassLibrary so path included
* _Screen.Newobject("ThemesManager","ThemesManager","ThemedControls.vcx")
 _Screen.Newobject("ThemesManager","ThemesManager",forcepath('ThemedControls.vcx', justpath(This.ClassLibrary)))

  • OverFlowPanelButton.ChangedSelectedButton: move the code defining the menu to a new FillMenu method and call that method. This allows the menu to be modified or replaced in a subclass or instance.

*!*       lnOption = 1
      Define Popup ShortcutMenu From lnRow, lnCol Relative Shortcut
 This.FillMenu()
*!*       Define Bar (lnOption) Of ShortcutMenu Prompt (loBar.mnuShowMoreText) Skip For loBar.Showedbuttons>=Min(lnButtonCount,loBar.MaxShowedbuttons)
*!*       On Selection Bar (lnOption) Of ShortcutMenu loBar.ShowMore()
*!*       lnOption = lnOption + 1
*!*       Define Bar (lnOption) Of ShortcutMenu Prompt (loBar.mnuShowLessText) Skip For loBar.Showedbuttons=0
*!*       On Selection Bar (lnOption) Of ShortcutMenu loBar.ShowLess()
*!*       lnOption = lnOption + 1
*!*       Define Bar (lnOption) Of ShortcutMenu Prompt "\-"

*!*       Local laMenu[lnButtonCount, 2]
*!*       laMenu=""
*!*       Local loButton
*!*       For Each loButton In This.Parent.Controls
*!*          With loButton
*!*             If Upper(.Class) == Upper("OverflowPanelButton") And .Name <> This.Name
*!*                laMenu[.Order, 1] = .imgPicture.ToolTipText
*!*                laMenu[.Order, 2] = .imgPicture.Picture
*!*             Endif
*!*          Endwith
*!*       Endfor
*!*       loButton = Null
*!*       Local lnMenuItem
*!*       For lnMenuItem=1 To Alen(laMenu,1)
*!*          Define Bar (lnOption+lnMenuItem) Of ShortcutMenu Prompt (laMenu[lnMenuItem, 1]) ;
*!*             Style (Iif(lnMenuItem=loBar.Selectedbutton,"B","")) ;
*!*             Picture (laMenu[lnMenuItem, 2])
*!*          On Selection Bar (lnOption+lnMenuItem) Of ShortcutMenu loBar.Selectedbutton=(Bar()-lnOption)
*!*       Endfor

      Activate Popup ShortcutMenu

Here's the code for FillMenu:

Local lnButtonCount
lnButtonCount = This.Parent.ControlCount - 1
Local loBar, lnOption
loBar = This.Parent.Parent
lnOption = 1
Define Bar (lnOption) Of ShortcutMenu Prompt (loBar.mnuShowMoreText) Skip For loBar.Showedbuttons>=Min(lnButtonCount,loBar.MaxShowedbuttons)
On Selection Bar (lnOption) Of ShortcutMenu loBar.ShowMore()
lnOption = lnOption + 1
Define Bar (lnOption) Of ShortcutMenu Prompt (loBar.mnuShowLessText) Skip For loBar.Showedbuttons=0
On Selection Bar (lnOption) Of ShortcutMenu loBar.ShowLess()
lnOption = lnOption + 1
Define Bar (lnOption) Of ShortcutMenu Prompt "\-"

Local laMenu[lnButtonCount, 2]
laMenu=""
Local loButton
For Each loButton In This.Parent.Controls
   With loButton
      If Upper(.Class) == Upper("OverflowPanelButton") And .Name <> This.Name
         laMenu[.Order, 1] = .imgPicture.ToolTipText
         laMenu[.Order, 2] = .imgPicture.Picture
      Endif
   Endwith
Endfor
loButton = Null
Local lnMenuItem
*** DH 07/29/2008: added LOCAL
local lcBar
For lnMenuItem=1 To Alen(laMenu,1)
   Define Bar (lnOption+lnMenuItem) Of ShortcutMenu Prompt (laMenu[lnMenuItem, 1]) ;
      Style (Iif(lnMenuItem=loBar.Selectedbutton,"B","")) ;
      Picture (laMenu[lnMenuItem, 2])
*** DH 07/29/2008: assigned value to lcBar and used it instead of local variable
*   On Selection Bar (lnOption+lnMenuItem) Of ShortcutMenu loBar.Selectedbutton=(Bar()-lnOption)
 lcBar = transform(lnOption)
   On Selection Bar (lnOption+lnMenuItem) Of ShortcutMenu loBar.Selectedbutton=Bar()-&lcBar
Endfor
return lnOption+lnMenuItem

Thanks.

Doug

Developer
Aug 21, 2008 at 8:17 PM
Hi Doug!
I sent a private mail for you with a test version including all enhancements, fixes and changes that you asked.
Please, let me know if everything is working as expected.

Thanks!

Emerson