This Widget-Tutorial will show, how you can put your gathered knowledge with the Drawing-Methods
(behind the usually named CC As cCairoContext) - to good use, when writing your own Cairo-based Controls.
A self-implemented "UserControl" (with the helper-classes from RC6, using cairo-drawing) - is called "a Widget".
A Widget is like a WindowLess-UserControl in VB6 - but not implemented in a *.ctl - but a normal *.cls.
Due to that (everything sitting in a "normal Class"), I recommend to use the same ClassName-prefixing as you find in the Demos:
- cSomeNormalClass (a normal Class is prefixed with a small c)
- cfSomeRC6Form (an RC6-Form-Class is prefixed with cf)
- cwSomeWidget (an RC6-Widget-Class is prefixed with cw)
This will allow one, to keep the different type of Classes apart (the cf-prefixed ones are like *.frm, and the cw-ones like *.ctl).
Ok, on the inside of a cwSomeWidget-Class, you will see basically the same principle, as when implementing something in a MyCtl.ctl:
- an internal Helper-Variable, which provides you with all kind of Events inside of this "Control-Class"
- in VB6 this Helper-Variable is named UserControl (automatically provided by the IDE, you don't have to define it)
- and for RC6-WidgetClasses the same thing is usually named W, but you have to define it yourself:
Private WithEvents W As cWidgetBase
Other than that, the implementation is similar to what you do in a VB6-UserControl-Module:
- you get Resize, Focus, Keyboard, Mouse- and Paint-Events from your internal UserControl-Variable
- and in the same way, you get the same Events (plus a few more) in your Widget-Class from the W-Variable
The largest difference to VB6-Usercontrols is this:
- We now have a fully Alpha-Capable Vector-engine behind it (which supports even nested Alpha)
- And the Widget is "by default" always "fully transparent"
The latter part meaning:
If you don't render anything in the W_Paint()-Event via explicite Cairo-methods, you will "not see anything"
(the Container-Background of the Parent-Widget or Parent-Form will "remain as it is" below the area, your Widget occupies).
Please note, that the only way to "render a given Widget properly", is via the W_Paint()-Event.
If you change state on a given Widget (e.g. a Caption-Prop or an ImageKey-Property), you will have to ensure:
- either a W.Refresh (if you are on the inside of your own implementing WidgetClass)
- or a MyCtl.Widget.Refresh call on the outside of your WidgetClass-implementation
to finally trigger the W_Paint-Event on the given Widget, which then dutifully re-renders its contents, according to the new "state".
Ok, the Tutorial here has the same "Folder-structured approach" (from simple to more difficult),
as the Cairo-Drawing-Tutorial which came before it...
Folder-Contents:
- 0 Hello World in VBScript
- 1 Hello Widgets (simple)
- 2 Widgets (Moveable-Prop and Zooming)
- 3 Widgets (Events and Nesting)
- 4 Widgets (MouseCursors and ImageKeys)
- 5 SliderWidget (demonstrating visual inheritance)
Here a ScreenShot of the "Folder #4 Demo" (Drag-Drop + MouseCursor-Handling)
Here a ScreenShot of "Folder #5" (a Slider Widget + Visual Inheritance)
Dear Olaf,
The RC5/6 Widget is so powerful that I'm able to use it to simulate a little graph canvas to create a group of objects as a desired mixed object, my question is how to save the single object and whole canvas , especially like microsoft excel's insert shapes and grouping them and save them. Thanks.
RC5/6 Widgets are based on a "State-Machine"...
(based on a bunch of Widget-Props, which hold that State,...
and "all that State" is finally influencing the rendering in the internal W_Paint-Event, when triggered by the .Refresh-Method).
So, if you want to save Widgets (a Widget-Hierarchy, starting from some "Root-, or Parent-Widget"), you will have to:
- Enumerate all Widgets recursively (starting from a Root-Widget)
- build (serialize) "a XML- or JSON-Node"-string out of the "internal State" of the currently enumerated Widget
adding that "String-Result-Node", to a StringBuilder-Object, you pass along in that recursive Enumeration
And as for the serialization itself, you basically have to do that (in a single, given Widget) -
for all the Private Vars within that Widget, starting with the one which is "always there" -> W As cWidgetBase.
1) serialize all "Default-Props" of 'W' (Left, Top, Width, Height, FontName, FontSize, ForeColor,BackColor etc.)
2) and finally serialize all "selfdefined Private Vars" which you used, to hold additional state
-----------------------------------------
That said... the RC5/6 already contains an "Auto-Serializer", based on XML -
which can be triggered behind any .Widgets-Prop via e.g.: strXML = MyRC6Form.Widgets.SerializeToXML(..., ...)
The only requirement for that to work properly, is that all of your own Widget-Classes are contained Public in an AX-Dll
(and not as Private Classes in your Main-Project).
If that requirement is a problem for you, then you can always "roll your own" serialization,
as outlined at the beginning of this post.
Desr Olaf, Thank you very much, As per your guide above, I define sth my self in every obj class, so far the object can be moved, zoomimg , coped and saved as png ,also with the help of imagemagicK command line it can be copy as tranparent bg object similar to MS excel insert graph. It works fine。
My another3 questions are:
1)how to lock any one object but allow other objects to move and zoom in the same panel?
2) Is it possible to group some objects together like excel insert graph?
3) when I make one object rotate as your post example in vbforums, it can work at first time, then memory problem occured ,how can I do ?
As for your other questions:
1) Move-Locks could be done by simply setting MyShape.Widget.Moveable = False
2) Grouping (using the Widget-Engine) one can do, by placing the grouped Widgets within their own (transparent) Parent-Widget
3) A link to some code (or your own uploaded Zip-File) would be good
Hi all, I can't believe I missed this tutorial(?) somehow. As I'm reading it currently, just wondering, since Olaf has updated Cairo so much, it appears that some of the programing is no longer requires a lot of the setouts of the old stuff. Is this same situtation for Widgets too? Like for example, one doesn't need to type this in anymore to make Cairo work. (Great) "Public New_c As New cConstructor, Cairo As cCairo"
I'd like learn more if possible, but I feel like I'm reading something that seems out of date. Does anyone have any updated Widget samples/examples they could share with us plebs?
Have a Cairo-Random-Auto-Generated Facemaker Maiden for your trouble.
Any sample code to tell me how using RC6 I can do the following:
--
1. Read the contents of a password protected zip file, in memory? In other words, I should not have the need to unzip the contents anywhere first into the system in order to read the contents. I should be able to do in-memory reading.
2. On a similar note, how to do in-memory writing? i.e. read the contents of a password-protected in memory, make changes in it and write it back to the zip file. All this to be done without extracting the contents of the zip file anywhere into the system.
--
Note-1: I will be creating the password-protected zip file using 7-zip, just in case this info is significant
Note-2: I searched the forum on 'olaf rc6 zip file', 'olaf rc6 cryptography', etc., etc., but I could not get the right thread where I could ask the above question. Hence, I decided to ask the above question here, in this thread. I will be mighty thankful to get to know one major thread (like the WebView2 thread) where I can ask all NON-webview2 questions (sqlite, cairo, zip/unzip, hash, encyrpt, etc.) in future. Thanks in advance.
I take this opportunity to once again thank you like anything, Olaf, for all your contributions and wholehearted support at all times.
Any sample code to tell me how using RC6 I can do the following:
--
1. Read the contents of a password protected zip file, in memory? In other words, I should not have the need to unzip the contents anywhere first into the system in order to read the contents. I should be able to do in-memory reading.
2. On a similar note, how to do in-memory writing? i.e. read the contents of a password-protected in memory, make changes in it and write it back to the zip file. All this to be done without extracting the contents of the zip file anywhere into the system.
--
Note-1: I will be creating the password-protected zip file using 7-zip, just in case this info is significant
The RC6-Zip support works in conjunction with the SQLite-Zip-module -
(where Zip-ByteArrays or Zip-Files can be loaded into, as a "virtual table", which is then "SQL-queryable").
Though "password-protected Zip-Files" are not supported by this module (but I think wqwetos Zip-class has support for it).
What you can do though is, to encrypt a "normal Zip file" via your own Encryption-routine -
(treating it like a "any other unencrypted ByteBuffer") - though then "only your App" will then be able to open an archive, encrypted in this way.
Crypto- and Hash-support is all contained in the New_c.Crypt-class (e.g. AES and ArcFour Methods are available behind it).
Originally Posted by softv
Note-2: I searched the forum on 'olaf rc6 zip file', 'olaf rc6 cryptography', etc., etc., but I could not get the right thread where I could ask the above question. Hence, I decided to ask the above question here, in this thread.
where I can ask all NON-webview2 questions (sqlite, cairo, zip/unzip, hash, encyrpt, etc.) in future. Thanks in advance.
As for searches - I use google, along with the "site-modifier"-addition, as e.g.:
[ site:vbForums.com New_c olaf zip file ]
Note, that I used New_c instead of the terms RC5 or RC6 - to "catch all code-examples across both versions".
RC5/RC6 is a "commonly used, free available ActiveX-helper" in the meantime -
so "questions about it" should be treated like questions about "other larger ActiveX-helpers" like e.g. FlexGrid, ADO, DAO etc. -
and are therefore welcome in the normal (big) VB6-Forum.
This is fantastic, Olaf. Thank you!
My goal is to replace controls one at a time as time permits on my existing standard forms and VBCCR apps.
Would you recommend using the ucPanel.ctl to draw widget replacements as I go?
Cheers
Last edited by taishan; Apr 26th, 2024 at 06:27 PM.
This is fantastic, Olaf. Thank you!
My goal is to replace controls one at a time as time permits on my existing standard forms and VBCCR apps.
Would you recommend using the ucPanel.ctl to draw widget replacements as I go?
ucPanel.ctl (as a VB6-Control) integrates (as a Widget-Host) best with the VB6-Tab-and-Focus-Handling.
Though - once you have replaced *all* VB6-Controls on a given VB6-Form with Widgets -
then you don't need to host them on an "intermediate ucPanel" anymore -
and can finally replace the VB6-Form with a cWidgetForm-Class
(which contains a Public WithEvents Form As cWidgetForm - which contains all Widgets).
However, there is one problem which is extremely time taking:
VB6 crashes when the IDE is being stopped.
I have attached a minimalistic sample project.
Once the timer in the attached sample project tries to divide by zero, and if I press Stop, VB6 freezes, and I need to kill it using the taskmanager.
Pressing the Stop button is something that I can not avoid, it is an essential feature of an IDE to me.
Is there a way to fix this?
Thank you!
Last edited by tmighty2; Sep 29th, 2024 at 11:19 AM.
The old ucPanel.ctl based approach is not as "debug-stable" -
(due to relying on the somewhat "messed up" VBIDE-control-unloading mechanisms) -
compared to using a cWidgetForm-based Panel (or TopLevel-Form).
Here is code, which shows how to use the more modern and crash-resistant "Panel As cWidgetForm" approach:
(also using a cTimer instead of a VB-TimerControl - to keep the hosting VB-Form free of any old Controls).
Code:
Option Explicit
Private m_lX&
Private m_lY&
Private m_dblButtonWidth As Double
Private m_dblButtonHeight As Double
Private m_lBorder As Double
Private WithEvents btnIdle As cwSimpleButton
Private WithEvents btnWaitingToShrink As cwSimpleButton
Private WithEvents btnTravel As cwSimpleButton
Private WithEvents btnShrinking As cwSimpleButton
Private WithEvents btnClicked As cwSimpleButton
Private WithEvents btnUnableToShrinkHere As cwSimpleButton
Private WithEvents btnEnlargingAfterClick As cwSimpleButton
Private WithEvents Panel As cWidgetForm
Private WithEvents Timer As cTimer
Private Sub Form_Load()
Set Panel = Cairo.WidgetForms.CreateChild(hWnd)
Set Timer = New_c.Timer(2000, True)
pCreateControls
End Sub
Private Sub Form_Resize()
Panel.Move 0, 0, ScaleWidth, ScaleHeight 'let the panel cover the entire Form-area
End Sub
Private Sub pCreateControls()
m_lBorder = 4
m_dblButtonHeight = 80
m_dblButtonWidth = 80
Set btnIdle = pAddButton("btnIdle", "Toggle " & Quote("Idle"))
Set btnWaitingToShrink = pAddButton("btnWaitingToShrink", "Toggle " & Quote("WaitingToShrink"))
Set btnTravel = pAddButton("btnTravel", "Toggle " & Quote("Travel"))
Set btnShrinking = pAddButton("btnShrinking", "Toggle " & Quote("Shrinking"))
Set btnClicked = pAddButton("btnClicked", "Toggle " & Quote("Clicked"))
Set btnUnableToShrinkHere = pAddButton("btnUnableToShrinkHere", "Toggle " & Quote("UnableToShrinkHere"))
Set btnEnlargingAfterClick = pAddButton("btnEnlargingAfterClick", "Toggle " & Quote("EnlargingAfterClick"))
Panel.WidgetRoot.LockRefresh = False
End Sub
Private Function pAddButton(ByVal uName As String, ByVal uText As String) As cwSimpleButton
Set pAddButton = New cwSimpleButton
pAddButton.Caption = uText
Panel.Widgets.Add pAddButton, uName, m_lX, m_lY, m_dblButtonWidth, m_dblButtonHeight
m_lX = m_lX + m_dblButtonWidth + m_lBorder
End Function
Public Function Quote(ByVal u As String) As String
Quote = ChrW(34) & u & ChrW(34)
End Function
Private Sub Timer_Timer()
Dim d As Double
Dim d2 As Double
d2 = d / 0
Debug.Print d2
End Sub
The blue Lines above, show the changes to your former code...
Thank you.
I am in the progress of making a PWA version of my VB6 application. I have now (apart from using this new Panel) completed the transition to RC6 graphics only.
My app consist of a surface that has different "cells" which can be tapped.
I am going to use this Panel as cWidgetForm for that, right?
Is there any additional recommendation that you can share in this where somebody wants to use RC6 for a PWA?
I have an anchor class that handles the form resizing.
It is not absolutely necessary to have this property, but since all controls so far had a .Name property, I would use "Name" for debugging purposes in the class.
Also, I was referring to objecfs to be anchored as "Control". Here is a link: https://www.vbforums.com/showthread....=1#post5654044
AnchorEx v1.95.zip
Now that I have switched from usercontrol to Widgetform Panel, I noticed that a WidgetForm does not have a name, and I have to treat it as Object in this anchor class.
Most times when I notice that RC6 is different than I thought, I later discover that there is a different concept behind that.
If you find the time to reply:
Is there more to it than just the .Name property not being there?
If I have to guess why there is no .Name property then I would say that - being runtime creatures - they would not have names as names are something to clearly identify something, and since the usual development-time checks like double names are not easily available at runtime, you did not implement this property. But that is just a guess.