I've been working hard on building a Scroll Frame which (a) looks quite good and (b) doesn't require too much work to create. I have eventually completed it, and I thought I'd post it here to save the time of other people who are new to Scroll Frames. I've omitted fluff that isn't directly relevant to save space.
The following list details what frames are required to build the Scroll Frame (in short, 3 standard frames, and 1 frame which inherits a template):
(a) First create a standard frame and make it the size you wish your 'window' to be. This frame serves to do nothing other than hold all of the other elements in place and will be the 'master parent' of all of the frames related to this scroll frame.
(b) Create another frame, this time arg1 is "ScrollFrame" and arg 4 is "UIPanelScrollFrameTemplate" in the CreateFrame func. Parent it to the frame above.
(c) Create another frame, standard frame. Don't do anything with it yet, and don't parent it to anything yet. This will be the scrollchild (see code below).
(d) Create one more standard frame once the Scroll Frame is complete (this is the frame which will hold your option widgets etc).
-- create the frame that will hold all other frames/objects:
local self = frameHolder or CreateFrame("Frame", nil, UIParent); -- re-size this to whatever size you wish your ScrollFrame to be, at this point
-- now create the template Scroll Frame (this frame must be given a name so that it can be looked up via the _G function (you'll see why later on in the code)
self.scrollframe = self.scrollframe or CreateFrame("ScrollFrame", "ANewScrollFrame", self, "UIPanelScrollFrameTemplate");
-- create the standard frame which will eventually become the Scroll Frame's scrollchild
-- importantly, each Scroll Frame can have only ONE scrollchild
self.scrollchild = self.scrollchild or CreateFrame("Frame"); -- not sure what happens if you do, but to be safe, don't parent this yet (or do anything with it)
-- define the scrollframe's objects/elements:
local scrollbarName = self.scrollframe:GetName()
self.scrollbar = _G[scrollbarName.."ScrollBar"];
self.scrollupbutton = _G[scrollbarName.."ScrollBarScrollUpButton"];
self.scrolldownbutton = _G[scrollbarName.."ScrollBarScrollDownButton"];
-- all of these objects will need to be re-anchored (if not, they appear outside the frame and about 30 pixels too high)
self.scrollupbutton:SetPoint("TOPRIGHT", self.scrollframe, "TOPRIGHT", -2, -2);
self.scrolldownbutton:SetPoint("BOTTOMRIGHT", self.scrollframe, "BOTTOMRIGHT", -2, 2);
self.scrollbar:SetPoint("TOP", self.scrollupbutton, "BOTTOM", 0, -2);
self.scrollbar:SetPoint("BOTTOM", self.scrolldownbutton, "TOP", 0, 2);
-- now officially set the scrollchild as your Scroll Frame's scrollchild (this also parents self.scrollchild to self.scrollframe)
-- IT IS IMPORTANT TO ENSURE THAT YOU SET THE SCROLLCHILD'S SIZE AFTER REGISTERING IT AS A SCROLLCHILD:
-- set self.scrollframe points to the first frame that you created (in this case, self)
-- now that SetScrollChild has been defined, you are safe to define your scrollchild's size. Would make sense to make it's height > scrollframe's height,
-- otherwise there's no point having a scrollframe!
-- note: you may need to define your scrollchild's height later on by calculating the combined height of the content that the scrollchild's child holds.
-- (see the bit below about showing content).
self.scrollchild:SetSize(self.scrollframe:GetWidth(), ( self.scrollframe:GetHeight() * 2 ));
-- THE SCROLLFRAME IS COMPLETE AT THIS POINT. THE CODE BELOW DEMONSTRATES HOW TO SHOW DATA ON IT.
-- you need yet another frame which will be used to parent your widgets etc to. This is the frame which will actually be seen within the Scroll Frame
-- It is parented to the scrollchild. I like to think of scrollchild as a sort of 'pin-board' that you can 'pin' a piece of paper to (or take it back off)
self.moduleoptions = self.moduleoptions or CreateFrame("Frame", nil, self.scrollchild);
-- a good way to immediately demonstrate the new scrollframe in action is to do the following...
-- create a fontstring or a texture or something like that, then place it at the bottom of the frame that holds your info (in this case self.moduleoptions)
self.moduleoptions.fontstring:SetText("This is a test.");
self.moduleoptions.fontstring:SetPoint("BOTTOMLEFT", self.moduleoptions, "BOTTOMLEFT", 20, 60);
-- you should now need to scroll down to see the text "This is a test."
Some things I've discovered:
(a) I've seen them created in other ways and without templates, but doing it this way ensures that functions are already written for the appropriate handlers. Decent functionality is achieved without the requirement to write any functions for the ScrollFrame.
If this has had anyone thinking "this bloke is trying to teach me how to suck eggs", I'm sorry. At the same time though, why are you still reading this lol?
In seriousness though, I hope this helps someone, as the post itself took half an hour!