Programmatically Resize Name Manager Dialog Box

Greg_M

New Member
Joined
Jan 28, 2017
Messages
15
Hi All,

I use Excel's built-in Name Manager a LOT!

Each time Excel is started, the dimensions, column widths etc. of the Name Manager dialog box are reset to default values which (I suppose!) are stored somewhere. The problem is that these default values are unsuitable for me, and I would like to be able to resize the dialog box programmatically.

I've performed a quick check of registry value changes but have been unable to identify where these settings might be stored.

So, does anyone know how to programmatically resize the Name Manager dialog box using e.g. API calls, registry values, other methods???

Regards,

Greg M

Please note that this item was originally posted on Excel Forum at: Programmatically Resize Name Manager Dialog Box
 
Last edited by a moderator:
@Tragic Shadow

bosa_sdm_XL9 is the class name of the Name Manager Dialog. Many excel windows carry the same class name.

As you can see from the code (in the HookProc window procedure), I added the two custom buttons to the name manager dialog using the CreateWindowEx API function. These 2 added custom buttons have the class name of Button. They are predefined control classes

Notice that I set both custom buttons as child (WS_CHILD) of the name manager dlialog window and I set their style to BS_OWNERDRAW so that the now parent dialog becomes responsible for drawing the custom buttons as well as for handling their click events. Obviously, this is only possible after subclassing (SetWindowLong + GWL_WNDPROC) the name manager dialog in order to be able to handle the corresponding window messages.

Hope that helps.
 
Upvote 0

Excel Facts

Ambidextrous Undo
Undo last command with Ctrl+Z or Alt+Backspace. If you use the Undo icon in the QAT, open the drop-down arrow to undo up to 100 steps.
@Tragic Shadow

bosa_sdm_XL9 is the class name of the Name Manager Dialog. Many excel windows carry the same class name.

As you can see from the code (in the HookProc window procedure), I added the two custom buttons to the name manager dialog using the CreateWindowEx API function. These 2 added custom buttons have the class name of Button. They are predefined control classes

Notice that I set both custom buttons as child (WS_CHILD) of the name manager dlialog window and I set their style to BS_OWNERDRAW so that the now parent dialog becomes responsible for drawing the custom buttons as well as for handling their click events. Obviously, this is only possible after subclassing (SetWindowLong + GWL_WNDPROC) the name manager dialog in order to be able to handle the corresponding window messages.

Hope that helps.
Yes Jaafar.
I understand all that and have followed as far as the subclassing the Name Manager.
I have already successfully sent lvm messages to search and select certain listitems-Names.

But my problem is those three Button-like-things, namely: New, Edit & Delete.
They are not of Button class. No clue what they are.
Because Spy++ shows nothing about them.
There are only 4 Children there with hWnds in the Name Manager Dialog.

I am searching for a way to interact with them (apart from sendkeys).
I think those 3 buttons are Accessible Children of Name Manager just like "Paste All" and "Clear All" from "Collect and Paste 2.0".
However, I still can't get them via the AccessibleChildren function via AccessibleObjectFromWindow Lib "oleacc".
But I just kept crashing Excel.
I am flying blind here because it's hard to find documentations or examples on this bosa_sdm_XL9 or IAccessble, apart from the Clipboard clearing VBA code examples.
I am still reading stuff on IAccessible thing.

Anyway, thanks so much again for your kind and prompt reply, Sir. I really appreciate it.
 
Upvote 0
Yes Jaafar.
I understand all that and have followed as far as the subclassing the Name Manager.
I have already successfully sent lvm messages to search and select certain listitems-Names.

But my problem is those three Button-like-things, namely: New, Edit & Delete.
They are not of Button class. No clue what they are.
Because Spy++ shows nothing about them.
There are only 4 Children there with hWnds in the Name Manager Dialog.

I am searching for a way to interact with them (apart from sendkeys).
I think those 3 buttons are Accessible Children of Name Manager just like "Paste All" and "Clear All" from "Collect and Paste 2.0".
However, I still can't get them via the AccessibleChildren function via AccessibleObjectFromWindow Lib "oleacc".
But I just kept crashing Excel.
I am flying blind here because it's hard to find documentations or examples on this bosa_sdm_XL9 or IAccessble, apart from the Clipboard clearing VBA code examples.
I am still reading stuff on IAccessible thing.

Anyway, thanks so much again for your kind and prompt reply, Sir. I really appreciate it.
I just found that I can press the Delete via code like
VBA Code:
accButton.objIA.accDoDefaultAction accButton.lngChild
.
It is an AccessibleChild of Role_PushButton.
But strangely no success so far with the other two: New... and Edit... may be because their names are either too long or may be the ellipsis, hinting that they will open new window, is messing with the find AccessibleChild by name code.
Since the Delete is working, I am now sure that these are accessible things from IAccessible stuff.
Still testing, researching and reading further...will update if and when I got a break through.
 
Upvote 0
@Tragic Shadow

Yes. Those built-in buttons ( New, Edit & Delete) are windowless controls thus they cannot be interacted with using the windows api. This is the case with most controls in excel built-in dialogs.

AFAIK, Active Accessibilty and UI Automation are the only means by which one can reliably interact with them programmatically.

If all you want is to be ble to click on them via code (apart from sendkeys) then yes, AccessibleChildren is the way to go.

I agree that locating accessible elements and interacting with them via Active Accessibilty and UI Automation can sometimes be a real pain.

Edit:
Are you using the name manager dialog as a generic example for learning purposes on how to access and interact with windwoless excel dialog controls via accessibility or is this part of a specific project ?
 
Last edited:
Upvote 0
@Tragic Shadow

Yes. Those built-in buttons ( New, Edit & Delete) are windowless controls thus they cannot be interacted with using the windows api. This is the case with most controls in excel built-in dialogs.

AFAIK, Active Accessibilty and UI Automation are the only means by which one can reliably interact with them programmatically.

If all you want is to be ble to click on them via code (apart from sendkeys) then yes, AccessibleChildren is the way to go.

I agree that locating accessible elements and interacting with them via Active Accessibilty and UI Automation can sometimes be a real pain.

Edit:
Are you using the name manager dialog as a generic example for learning purposes on how to access and interact with windwoless excel dialog controls via accessibility or is this part of a specific project ?
Thank you, Jaafar, for your kindest reply.
To tell you the truth, I've read about windowless controls here and there but never really had to think about them.
This is the first time I had to deal with them, so, it's not learning per se but more like learning by crashing Excel. :D
Accessibility controls are also way above my grade, never met them before, so learning that one too.

Before this, I tried to create a msgbox which would accept text formatting of specific parts of a string (like string formatting with % in other languages) by replacing the static control with a RichEdit control (also based on your msgbox code somewhere, I believe) but then I got stuck repainting the background and put it aside, until I found your code on adding buttons to Name Manager here, which by the way is a gem to me.
Now, you gave me the tools and knowledge on how to add new buttons on dialogboxes and how to repaint the background etc., so, I think I can restart the fire on that project.

Right now, I am in the middle of a specific project, part of which requires me to be able to programmatically press buttons on Name Manager after programmatically searching and selecting a specific name on the syslistview32 control, to help the user easily edit that name. Yes I know I can do it in other ways but for the next project, I needed knowledge on this.
I had to start a separate project for messing with Name Manager because I knew from experience that subclassing will crash a lot and can corrupt the .xlsm file.
So, it is also true that the current Name Manager project is for learning how to harness the built-in dialogs.

And your Name Manager code helped me with my dream project which is still in formulating stage in my brain, I needed that for making the edit? control of the Evaluate formula dialog (set to thickframe?) to become bigger(stretchable) to help people(myself included) who write pretty long formulae. I shall work on it after I finished this one.
Plus you've also helped me with DispCallFunc, in the past, in my other project on working with input.dll.

All of my projects are meant for ease of use by other people. I am not paid for any of those projects. I am doing all these out of my passion for VBA and coding in general.
I am not selling myself here but just want to let you know that the knowledge you shared helped me grow my skills enormously, and I am learning from your code not to get myself paid but to help others.

I'm sorry for the long response but I don't want to give you a wrong impression that I am asking you questions in the interest of a project for monetary gain. Many thanks again.
 
Upvote 0
I'm sorry for the long response but I don't want to give you a wrong impression that I am asking you questions in the interest of a project for monetary gain. Many thanks again.
No not at all. It's perfectly okay . I was just asking so I would be more precise in case I was to post an answer. You are most welcome to ask whenever you have any questions.
Regards.
 
Upvote 0
No not at all. It's perfectly okay . I was just asking so I would be more precise in case I was to post an answer. You are most welcome to ask whenever you have any questions.
Regards.
Hi Jaafar, many thanks for your reply.
I am now stuck in trying to press the Edit button and kept crashing if I choose "Edit..." button while "Delete" button is working fine.
Let me outline the steps I took to get this far.
  1. CBTHooked, then called Application.Dialogs(xlDialogNameManager).Show
  2. In CBTProc, CallNextHook then,
    1. get hWnd to Name Manager and UnHooked CBT immediately after confirmation via checking against classname
    2. from that, get hWnd to sysListView32
    3. search and select a particular Name entry with lvm messages
    4. search for the Edit button via IAccessible interface and press it with accButton.objIA.accDoDefaultAction accButton.lngChild
  3. returned CBTProc=0
The code works.
Because I can press Delete button.
I have also check with a simple debug.print in place of accDoDefaultAction part and it shows that it is actually found as "Edit...".

What I believe causing the crashes:
  • pressing Delete button shows only a confirmation dialogbox/msgbox and the Name Manager is still there
  • but pressing "New..." or "Edit..." causes the Name Manager dialog to be destroyed and recreated after the Edit Name dialogbox was OKed away
    • I checked this with Spy++ by comparing the 2 hWnds before and after manually pressing Edit outside of code by using mouse clicks and they are different
What I tried:
  • Replacing sendKeys %e and accDoDefaultAction with each other, it crashes like H3LL (I know I shouldn't but I am at my wits' end)
  • sendKeys %e with or without Application.OnTime, still no go

I now believe that it is impossible for me to press the Edit button via code because:
  • CBTProc has still not yet returned even though I unhooked it nearly immediately and pressing Edit via code causes the Name Manager dialog to be destroyed while CBTProc is still running even though I never actually subclassed the Name Manager dialog itself. (or did I?)
  • All the code (including IAccessible parts) actually worked because it was put into the CBTProc. Otherwise, the modal-ness would prevent it from running, so I can't put it anywhere else.
That's just what I believe happened. I hope that I am wrong.

It is quite hard to debug because of the breaking nature of the Name Manager's modal-ness in addition to the hooking.

I'd really appreciate your expert insight on this issue.
Right now, I am pulling my hairs out...so please help me...
Thanks in advance.
 
Last edited:
Upvote 0
Hi Tragic Shadow,

Let me see if if I am understanding this correctly ? Essentially, You want to have a macro that will open the name manager dialog and automatically carry out the following :

1-Click the New... button (this will display the New Name sub-dialog) OR/AND
2- Select a Name (if there are any Names) from the list of Names and click the Edit... button (This will display the Edit Name sub-dialog)

When the user is done with either of the the sub-Name Dialogs, the macro should close the main manager dialog.

Is my understanding correct ?

Bear in mind that if my above assumptions are correct, you should be able to achieve your goal quite easily.

For #1, New.. button you can simply use this:
Application.Dialogs(xlDialogNewName).Show

For #2, Edit... button I couldn't find its corresponding xldialog name (probably it doesn' exist due to not knowing which name to edit beforehand) , but I guess we can come up with some workaround wthout the need to use sendkeys.
 
Upvote 0
Hi Tragic Shadow,

Let me see if if I am understanding this correctly ? Essentially, You want to have a macro that will open the name manager dialog and automatically carry out the following :

1-Click the New... button (this will display the New Name sub-dialog) OR/AND
2- Select a Name (if there are any Names) from the list of Names and click the Edit... button (This will display the Edit Name sub-dialog)

When the user is done with either of the the sub-Name Dialogs, the macro should close the main manager dialog.

Is my understanding correct ?

Bear in mind that if my above assumptions are correct, you should be able to achieve your goal quite easily.

For #1, New.. button you can simply use this:
Application.Dialogs(xlDialogNewName).Show

For #2, Edit... button I couldn't find its corresponding xldialog name (probably it doesn' exist due to not knowing which name to edit beforehand) , but I guess we can come up with some workaround wthout the need to use sendkeys.
Hi Jaafar,
I apologize for the confusion. It's my bad explanation.

What I want to do, programmatically, is:
1.Open the Name Manager then search and select a particular name,
(Some should already be added via code or manually. So, no need to use the New... button.)
2.Click the Edit... button. And the Sub shall end here.
From this point on, the user can do whatever they want with the selected name(and value).
Application.Dialogs(NameManager).Show will return true/false and I can also check what they did from Names.

I got as far as #2 above. Delete button is working beautifully, using the IAccessible.
I run the code with "Delete" as selected button and the Delete confirmation dialog appears when the code finished running.
But with either "New..." or "Edit...", Excel crashes really hard. I've seen a enough crashes but this one is really bad in that it takes very long to reopen and I can't kill Excel too.

Applications.Dialogs has no Edit... option which is the reason, I had to mess with IAccessible stuff.

I thought this should be easy and straight forward. But the modal-ness is messing with me, or so I think.
I am using ThisWorkbook.Names to hold a couple of variables that the user can set so that the code can check and work accordingly.
I could share the code with you but it is rather long-ish (with all IAccessible stuff) to be posted here. Plus, it's in a jumbled state for now because I am testing various options too.

Thanks for your kind interest. I really appreciate it.
 
Upvote 0
@Tragic Shadow

Ok. Let's see if this works for you : Unlike what I did before, this time I am not setting up a windows hook for the Name manager dialog .Neither am I doing any subclassing. In fact, this is not needed for what you want .

Instead, I am simply using a one-time-use windows timer that is immediately killed when the dialog is about to be displayed. This approach is much simpler.

So, basically, you now have this easy boolean function ShowEditNameDialog(ByVal NameToEdit As String) which dispalys the Edit Name dialog ready for the user to work with. The function expects a Name (String) as its argument and returns False if no Name was found.

The helper functions use Active Accessibilty to enable selecting the required name from the list and for clicking the Edit button.

I have commented the code for easy following.

Workbook Demo

In a Standard Module:
VBA Code:
Option Explicit

Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

#If VBA7 Then
    Private Declare PtrSafe Function IIDFromString Lib "ole32.dll" (ByVal lpsz As LongPtr, ByVal lpiid As LongPtr) As LongPtr
    Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "OLEACC.DLL" (ByVal hWnd As LongPtr, ByVal dwId As Long, ByVal riid As LongPtr, ppvObject As Any) As Long
    Private Declare PtrSafe Function AccessibleChildren Lib "Oleacc" (ByVal paccContainer As Office.IAccessible, ByVal iChildStart As Long, ByVal cChildren As Long, ByRef rgvarChildren As Any, ByRef pcObtained As Long) As Long
    Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
    Private Declare PtrSafe Function lstrlen Lib "kernel32" Alias "lstrlenW" (ByVal lpString As LongPtr) As Long
    Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
    Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
    Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
    Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
    Private Declare Function IIDFromString Lib "ole32.dll" (ByVal lpsz As Long, ByVal lpiid As Long) As Long
    Private Declare Function AccessibleObjectFromWindow Lib "OLEACC.DLL" (ByVal hWnd As Long, ByVal dwId As Long, ByVal riid As Long, ppvObject As Any) As Long
    Private Declare Function AccessibleChildren Lib "Oleacc" (ByVal paccContainer As Office.IAccessible, ByVal iChildStart As Long, ByVal cChildren As Long, ByRef rgvarChildren As Any, ByRef pcObtained As Long) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" (ByVal lpString As Long) As Long
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
    Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
#End If


Function ShowEditNameDialog(ByVal NameToEdit As String) As Boolean
    If NameExists(NameToEdit) Then
        Call SetTimer(Application.hWnd, StrPtr(NameToEdit), 0, AddressOf TimerProc)
        Application.Dialogs(xlDialogNameManager).Show
        ShowEditNameDialog = True
    End If
End Function


' HELPER ROUTINES ...
#If Win64 Then
    Private Sub TimerProc(ByVal hWnd As LongLong, ByVal uMsg As Long, ByVal nIDEvent As LongLong, ByVal dwTime As Long)
        Dim hTemp As LongLong
#Else
    Private Sub TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal nIDEvent As Long, ByVal dwTime As Long)
        Dim hTemp As Long
#End If

    Const SELFLAG_TAKESELECTION = &H2
    
    Dim vAccChildren As Variant
    Dim oAcc As IAccessible
    Dim sName As String, i As Long

    'Timer no longer needed so kill it now.
    Call KillTimer(Application.hWnd, nIDEvent)
    
    'Retrieve the Accessibility interface of the Names listview control.
    hTemp = FindWindowEx(FindWindow("bosa_sdm_XL9", vbNullString), 0, "XLLVP", vbNullString)
    hTemp = FindWindowEx(hTemp, 0, "SysListView32", vbNullString)
    Set oAcc = GetAccFromHnwd(hTemp)
    
    'Recover the Name string from its pointer.
    sName = GetStringFromPointer(nIDEvent)
    
    'Look for the Name string in the listview and select it.
    Do
        i = i + 1
        Call AccessibleChildren(oAcc, 0&, 1&, vAccChildren, 0&)
        If LCase(oAcc.accName(i)) = LCase(sName) Then
            oAcc.accSelect SELFLAG_TAKESELECTION, i
            Exit Do
        End If
    Loop Until i >= oAcc.accChildCount
    
    'Retrieve the Accessibility interface of the main Names dialog and click the Edit button.
    Set oAcc = GetAccFromHnwd(FindWindow("bosa_sdm_XL9", vbNullString))
    oAcc.accDoDefaultAction (2&)

End Sub


#If Win64 Then
    Private Function GetAccFromHnwd(ByVal hWnd As LongLong) As IAccessible
#Else
    Private Function GetAccFromHnwd(ByVal hWnd As Long) As IAccessible
#End If

    Const ID_ACCESSIBLE As String = "{618736E0-3C3D-11CF-810C-00AA00389B71}"
    Const OBJID_CLIENT = &HFFFFFFFC
    Const S_OK = &H0
    Dim tGUID(0 To 3) As Long
    Dim oIAc As IAccessible
    
    If IIDFromString(StrPtr(ID_ACCESSIBLE), VarPtr(tGUID(0))) = S_OK Then
        If AccessibleObjectFromWindow(hWnd, OBJID_CLIENT, VarPtr(tGUID(0)), oIAc) = S_OK Then
           Set GetAccFromHnwd = oIAc
        End If
    End If

End Function

#If Win64 Then
    Private Function GetStringFromPointer(ByVal lpString As LongLong) As String
#Else
    Private Function GetStringFromPointer(ByVal lpString As Long) As String
#End If
   Dim lLength As Long, sBuffer As String
   lLength = lstrlen(lpString)
   sBuffer = Space$(lLength)
   Call CopyMemory(ByVal StrPtr(sBuffer), ByVal lpString, lLength * 2)
   GetStringFromPointer = sBuffer
End Function

Private Function NameExists(ByVal sName As String) As Boolean
    Dim oName As Name
    On Error Resume Next
        Set oName = Names(sName)
    On Error GoTo 0
    NameExists = Not (oName Is Nothing)
End Function



USAGE EXAMPLE:
VBA Code:
Sub Test()

    'Existing Names:- Name1,Name2,Name3 and Name4.
    If ShowEditNameDialog("Name1") Then
        Debug.Print "success"
    Else
        MsgBox "No such name exists!"
    End If

End Sub

I hope this helps.

EDIT: Note that this code is language sensitive as it relies on the language of the button captions in the name manager dialog.
 
Upvote 0

Forum statistics

Threads
1,223,929
Messages
6,175,449
Members
452,642
Latest member
acarrigan

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top