How to edit GUI elements on a specific frame in VB6 - vb6

How I can get access to the nested controls of a control?
I have serveral frames on my user interface and every frame contains serveral other controls (like labels, buttons, ...). I have to iterate over the frames and change content of the children of a specific frame (e.g. set another text in a label).
So far I iterate over all controls of my frame and I check if the control in the loop control variable is the frame where the changes should be.
Dim cntrl As Control
For Each cntrl In Controls
'Debug.Print cntrl.Name // here I get all controls on the form
If cntrl.Name = "Frame_Name" Then
If cntrl.Index = index Then
Debug.Print "true" ' here the caption of nested components should be changed
End If
End If
Next
Now I have the frame in the control variable but the problem is that I get no access to the nested label to change the label's caption. What can I do?

You need to look at the Container property of each control. The following code should give you the idea:
Dim cntrl As Control
For Each cntrl In Controls
If cntrl.Container.Name = "Frame_Name" Then
Debug.Print cntrl.Name & " is nested in the specified frame"
End If
Next

Related

How to replace text in Powerpoint with activeX textbox value?

I'm sorry if I can't explain this very well. Language barrier and all that. Might explain why I wasn't able to google a solution... Anyway.
I'm trying to make an interactive Powerpoint where in the first slide the user writes their name in an ActiveX textbox and the name then repeats in several other slides. Because the name would be in the middle of different texts my solution was to add a placeholder word that would then be replaced with the textbox value. I was able to Frankenstein a code that replaces one name with another that works well enough but I can't get the textbox to work with it. Best I can do is replace the placeholder name with some variation of TextBox1.Value and while I know what causes that issue I can't work out a solution.
Not sure if it makes a difference but the plan is to add some sort of "OK" button next to the active textbox that runs the macro.
Sub Findandreplace()
Dim sld As Slide
Set sld = ActivePresentation.Slides(1)
Dim shp As Shape
For Each sld In ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then
shp.TextFrame.TextRange.Text = Replace(shp.TextFrame.TextRange.Text, "word1", "word2")
End If
End If
Next shp
Next sld
End Sub
ActiveX objects have a different set of properties/methods than regular PPT shapes. Assuming you have a reference to the AX textbox in a variable oSh,
osh.OLEFormat.Object.Text
will give you the text in the ActiveX textbox

How could I use VBA to change a button's picture to one of the embedded default images?

I'm trying to get a button to change from the "Arrow Left" to "Arrow Right" picture when I click it, but I'm not sure how to assign the images through VBA. I tried Me!btnCollapseUnscheduled.Picture = "Arrow Left", but I get an error message this way.
In Access 2010 or later, you can store images in the MSysResources system table. Then choose Shared for your command button's Picture Type property, and select one of those shared images.
Afterward, it's easy to toggle between the shared images from the command buttons's click event. I named my command button "cmdArrow" and the shared images "Arrow Left" and "Arrow Right" ...
Option Compare Database
Option Explicit
Private Sub cmdArrow_Click()
Dim strDirection As String
If Me!cmdArrow.Picture Like "*Left" Then
strDirection = "Right"
Else
strDirection = "Left"
End If
Me!cmdArrow.Picture = "Arrow " & strDirection
Me!txtImage.Value = Me!cmdArrow.Picture
End Sub
Private Sub Form_Load()
Me!txtImage.Value = Me!cmdArrow.Picture
End Sub
Screenshots:
The biggest challenge was loading the images into MSysResources. I created a throwaway form and added a command button. Next I chose Embedded as the button's Picture Type property and selected Arrow Left from the available choices. Then I changed Picture Type from Embedded to Shared, which added a row to MSysResources with the image data in an attachment field and my command button's name in the Name field. So I opened the table, changed Name to "Arrow Left" and closed the table. Note MSysResources will not be visible in the Navigation pane unless you enable "Show System Objects" in the Navigation Options.
I repeated those steps for "Arrow Right". It may sound fiddly, but it's not really a huge hurdle.
The trick is to use the full file path of your icon.
MS-Access might be using some icon map for its native icons, so you might not have much luck getting a path to one of those.
If you definitely can't find the path to the native MS-Access icons, you could always just do a google image search for arrow icons (specify exact size of 16 x 16 pixels).
You can then just save these icons to a folder of your choosing and then use those paths in the following illustrations.
If you just want the button to change its image on the first click and stay that way thereafter:
Make sure the button does not have a picture attached in Design View.
Assign the default image using its full path to your command button using the form's Open event:
Private Sub Form_Open(Cancel As Integer)
Me.cmdButton.Picture = "C:\myFolder\myDefaultIcon.png"
End Sub
Then in the button's click event, assign the .picture property to the icon path you want to change it to:
Private Sub cmdButton_Click()
Me.cmdButton.Picture = "C:\myFolder\myChangedIcon.png"
End Sub
If you just want the button to toggle between 2 images on each click:
Use a toggle button control instead of a command button control
Make sure the button does not have a picture attached in Design View.
Write a sub that will handle the different on/off states of your toggle button:
Public Sub togImg()
If _
Me.togButton = -1 _
Then
Me.togButton.Picture = "C:\myFolder\myChangedIcon.png"
Else
Me.togButton.Picture = "C:\myFolder\myDefaultIcon.png"
End If
End Sub
Call your sub from both the form's Open event and the toggle button's Click event:
Private Sub Form_Open(Cancel As Integer)
togImg
End Sub
Private Sub togButton_Click()
togImg
End Sub
You could create two buttons where only one is visible at a time, both calling the same code.
When one is clicked, unhide the other, set focus to this, then hide the first button. And vice-versa.

How to Save Embedded Images in Powerpoint 2013 To Local Disk Through VBA?

I have lot of pictures embedded in the Microsoft Powerpoint 2013 presentation. In Powerpoint 2013, I can right click on a picture object and then use "Save As Picture" to save the image to local disk. But this is manual way. I want to know if there is any way of automating the same through VBA? I am able to loop through the Shapes collection and then determine the type of the object. But I am not able to figure out which properties or methods are relevant for storing the picture to the local disk? Following is code snippet of iterating through the Shapes collection and determining if the Shape is of type msoPicture
For currentSlideIndex = 1 To totalSlidesCount
Set currentSlide = ActivePresentation.Slides(currentSlideIndex)
For Each mediaShape In currentSlide.Shapes
mediaCounter = mediaCounter + 1 'for each shape in the slide
If currentSlideIndex = 3 Then
If mediaShape.Type = msoPicture Then
MsgBox ("Shape Type Picture is " + CStr(mediaShape.Type) )
End If
End If
Next mediaShape
mediaCounter = 0 'Reset to 0 for the new slide
Next
You can export the current shape to a picture using the shape's .Export method.
This is normally hidden, so you won't get intellisense help on it by default, so:
Go into the IDE
Press F2 to open the object browser
Rightclick in the pane to the right of Classes, where you see "Members of ..." at the top.
Put a check next to Show Hidden Members
Now you'll get intellisense guidance when you type .Export on a slide shape.
Note also that if a shape is Type 14 (placeholder), it can contain a picture, so you may want to add something like:
If mediaShape.Type = msoPlaceholder Then
If mediaShape.PlaceholderFormat.ContainedType = msoPicture Then
' it's also a picture
End If
End if
And to add further entertainment value you might also have .Type = msoLinkedPicture, and even in some cases msoEmbeddedOLEObject that happens to be a picture.

vb6 changing colors of SSTab at runtime using label in the background

I am trying to update an old VB6 application. I am struggling with changing the backcolor of the SSTab control at runtime.
I know that I cannot just set the Backcolor to the required colour.
At designtime I have created a label on each tab and then resized the label to the sstab dimensions. Then I set the label backcolor. This works fine.
However, what I really want to do is to do this at runtime so that if a new tab control is added on a form in the future I dont need to manually do this at designtime each time.
I would be grateful if someone can point me in the right direction to create and display a label on every tab of a SSTab control at runtime?
I dont have frames on all the tabs so I dont want to use the frame container to hold the label. I would rather just have the label placed on the tab container at runtime.
Thanks and I look forward to your replies.
As svinto says, you can load new controls to a Control Array, but there's a bit more to it than that.
Firstly the new controls are not Visible, so you must set .Visible=True
You also need to set the current tab before creating the control to get it sited on that tab.
Assuming you already have the first label on the first tab:
For i = 1 To Me.SSTab1.Tabs - 1
Me.SSTab1.Tab = i
Load lblOne(i)
lblOne(i).Visible = True
Next
Also, this doesn't work in the Form_Load event, but does in Form_Activate
There is another method to create the Labels from scratch rather than using a Control Array, which might be better as you don't need to add the first Label to the form:
Private Sub Form_Activate()
Dim lbl() As Label
Dim i As Integer
Dim name As String
ReDim lbl(SSTab1.Tabs - 1)
For i = 0 To Me.SSTab1.Tabs - 1
Me.SSTab1.Tab = i
name = "MyLabel" & CStr(i)
Set lbl(i) = Form1.Controls.Add("VB.Label", name)
Set lbl(i).Container = Me.SSTab1
lbl(i).Visible = True
lbl(i).Caption = name
lbl(i).Move 40, 345
lbl(i).BackColor = vbGreen
Next
End Sub
Create one label at design-time, and set it's index to 0. You can later on do Load labelname(newindex) to create more instances of the label. Position them once created, set the background color and you're all done.

Allow vertical scrolling in listbox, when it is disabled (VB6)

I need to allow the vertical scrollbar in a multiselect listbox (VB6) however, when the control is disabled, I can't scroll.
I would think there is an API to allow this, but my favorite VB6 site (MVPS VB.NET) does not have a way.
I toyed with pretending it was disabled, and ignore the clicks... but to do that with VB6 code is really ugly... so if this is a solution, I need an API to ignore the clicks.
Thanks for your help.
I came up with the following code, which hides all of the gnarly details behind a class. Basically, I implemented greg's idea of using overlaying another scrollbar on top of the disabled list box's scrollbar. In my code, I dynamically create another ListBox control (resized so that only its scrollbar is visible), and use its scrollbar to scroll the actual ListBox. I also specifically avoided using the Windows API (except for the call to GetSystemMetrics that I used to figure how how wide a scroll bar is on the system). The nice thing about using another ListBox's scrollbar is that it will be themed properly (a ListBox uses the OS's theme when it displays it's scrollbar, but a VB.Scrollbar doesn't, so it would look out-of-place). Another advantage of using a second ListBox to scroll the first list box is that it's really easy to implement the scrolling logic (just set the first ListBox's TopIndex property to the second ListBox's TopIndex property whenever the second one is scrolled).
I also set it up to be as low-impact as possible (you only have to call a single function in your Form_Load event to make it work).
Usage
Add CustomScrollingSupport.cls and ListBoxExtras.bas to your project.
In your form's Form_Load event, add the following line:
AddCustomListBoxScrolling Me
This will make every VB.ListBox on the form support scrolling even while they are disabled. If you only want to add this functionality to a select number of ListBox's, you can call AddCustomScrollingSupport instead, passing in a specific ListBox control.
Interesting Note
In an older version of this code, I wasn't calling the ZOrder method on the second listbox (the one that provides the scrollbar) to make sure it would appear on top of the first listbox. This meant the second listbox was actually behind the first listbox; the interesting thing is that the scrolling on the second ListBox still worked when the first ListBox was disabled! Apparently, when the first ListBox is disabled, any mouse and keyboard events that would have gone to that ListBox "bleed through" to the second ListBox, so scrolling support still does work. I'm not sure if this is a bug or by design (I mean, you could argue that it makes sense that controls behind a disabled control would be able to receive events...). However, I found the scrolling to be slightly jerky at times, so I decided to add .ZOrder 0 to make the second listbox render on top of the first one. This has the drawback that you see the frame border for the second listbox (to the left of the scroll bar), which you wouldn't see if it was hidden behind the first listbox, but the scrolling is smoother.
CustomScrollingSupport.cls
This class wraps up the logic necessary to add "custom scrolling support" (for lack of a better name) to a VB.ListBox control. It should not be used directly, instead use the one of the Add* methods in the ListBoxExtras.bas module (I'll provide the code for that module later in the post).
Option Explicit
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Const SM_CXVSCROLL = 2
Private Const SM_CXFRAME = 32
Private m_runningScrollers As Collection
Private WithEvents m_list As VB.listbox
Private WithEvents m_listScroller As VB.listbox
'--------------------------------------------------------------'
' Bind '
' '
' Adds custom scrolling support to a ListBox control. '
' Specifically, it allows the ListBox to be '
' scrolled even when it is disabled. '
' '
' Parameters: '
' '
' + list '
' the ListBox control to add custom scrolling support to '
' '
' + runningScrollers '
' a Collection of CustomScrollingSupport objects. Passed '
' in so that this object can remove itself from the list '
' when it is terminated. '
' '
'--------------------------------------------------------------'
Public Sub Bind(ByVal list As VB.listbox, runningScrollers As Collection)
Set m_list = list
Set m_runningScrollers = runningScrollers
'Create another ListBox loaded with the same number of entries as the real listbox'
Set m_listScroller = m_list.Container.Controls.Add("VB.ListBox", list.Name & "_scroller")
LoadScrollerList
Dim nScrollbarWidth As Long
nScrollbarWidth = GetSystemMetricScaled(SM_CXVSCROLL, m_list) + _
GetSystemMetricScaled(SM_CXFRAME, m_list)
'Display the other listbox (the "scroller"), just wide enough so that only its scrollbar is visible'
'and place it over the real listboxs scroll bar'
With m_listScroller
.Left = m_list.Left + m_list.Width - nScrollbarWidth
.Top = m_list.Top
.Height = m_list.Height
.Width = nScrollbarWidth
.Enabled = True
.Visible = True
.ZOrder 0
End With
End Sub
Private Sub m_listScroller_Scroll()
'If the master list has changed, need to reload scrollers list'
'(not ideal, but there is no ItemAdded event that we could use to keep the lists in sync)'
If m_list.ListCount <> m_listScroller.ListCount Then
LoadScrollerList
End If
'Make any scrolling done on the scroller listbox occur in the real listbox'
m_list.TopIndex = m_listScroller.TopIndex
End Sub
Private Sub Class_Terminate()
Dim scroller As CustomScrollingSupport
Dim nCurrIndex As Long
If m_runningScrollers Is Nothing Then
Exit Sub
End If
'Remove ourselves from the list of running scrollers'
For Each scroller In m_runningScrollers
nCurrIndex = nCurrIndex + 1
If scroller Is Me Then
m_runningScrollers.Remove nCurrIndex
Debug.Print m_runningScrollers.Count & " scrollers are running"
Exit Sub
End If
Next
End Sub
Private Sub LoadScrollerList()
Dim i As Long
m_listScroller.Clear
For i = 1 To m_list.ListCount
m_listScroller.AddItem ""
Next
End Sub
Private Function GetSystemMetricScaled(ByVal nIndex As Long, ByVal ctrl As Control)
GetSystemMetricScaled = ctrl.Container.ScaleX(GetSystemMetrics(nIndex), vbPixels, ctrl.Container.ScaleMode)
End Function
ListBoxExtras.bas
This module contains two utility methods:
AddCustomScrollingSupport adds custom scrolling functionality
to an individual VB.ListBox
control
AddCustomListBoxScrolling adds custom scrolling
functionality to every VB.ListBox
control on a given Form
Option Explicit
Public Sub AddCustomScrollingSupport(ByVal list As VB.listbox)
Static runningScrollers As New Collection
Dim newScroller As CustomScrollingSupport
Set newScroller = New CustomScrollingSupport
runningScrollers.Add newScroller
newScroller.Bind list, runningScrollers
End Sub
Public Sub AddCustomListBoxScrolling(ByVal frm As Form)
Dim ctrl As Control
For Each ctrl In frm.Controls
If TypeOf ctrl Is VB.listbox Then
AddCustomScrollingSupport ctrl
End If
Next
End Sub
Rather than looking for the API to ignore clicks, can't you just ignore the events? (i.e. just don't do when the user clicks/selects something).
And, I think there is a SelectionMode property to disable multiselect and make it single select.
If you don't want the user to be able to select anything at all, you can try hooking into the SelectionIndexChanged event and set the SelectionIndex to -1.
My VB6 is a bit rustly, so sorry if the event/property name don't match exactly.
Speaking of hacks, what if you enable the scrollbar when the mouse is moving over the scroll bar?
Or maybe ... place another scroll bar over the ListBox's SB, and use APIs to scoll the disabled LB.
Enabling just the scrollbar on a disabled listbox is possible (I think), but you'd have to dig into the Windows API and do SendMessage and some other grisly stuff.
I just checked it out, and when a listbox is disabled you can still scroll it up and down programatically by changing the control's ListIndex property. So you could do something like what greg suggests, and "float" an enabled vertical scroll bar over the one on the list box, and use this scrollbar's Value_Changed event (I think that's what it's called) to change the listbox's ListIndex property.
This is a total VB hack, but I think you can leave the listbox enabled, and then drag a transparent label (with blank text) over all of the listbox except the scrollbar. The label will intercept any mouse clicks (although this won't affect keystrokes).
This will only work if the label is transparent like I remember (it may be the image control - without a loaded image - that works like this in VB).

Resources