Hi,
I've created a .ctl-File:
#FireFly_Custom_Control#
[Control]
controlname = WsTextBox
description = WsTextBox (B4B-Textbox)
author = Christian Weilguny
copyright = WeSoft (c) 2010
version = 1.00
dll_filename = WsControls.dll
dll_required = 1
source_filename = WsTextBox.inc
toolbox_bitmap = Ws_Text.bmp
toolbox_cursor = Ws_Text.cur
toolbox_tooltip = WsTextBox (B4B-Textbox)
delimiter = |
uniqueid = {0B5515F6-A7A9-4279-92F4-76CA1CD2F5E6}
use_loadlibrary = 1
use_initialize_action = 0
initialize_action_designer =
initialize_action_code =
create_action = WsControls.dll|WSTEXTBOX|WSTEXTBOX|BYVAL LONG //CTRL_PARENT//|BYVAL LONG //CTRL_ID//|BYVAL LONG //CTRL_LEFT//|BYVAL LONG //CTRL_TOP//|BYVAL LONG //CTRL_WIDTH//|BYVAL LONG //CTRL_HEIGHT//|BYVAL LONG //CTRL_STYLE//|BYVAL LONG //CTRL_EXSTYLE//
[Property]
name = name|(Name)
curvalue = WsTextBox
itemtype = Edit|1
[Property]
name = windowstyles|(WindowStyles)
curvalue =
itemtype = Styles
[Property]
name = controlindex|ControlIndex
curvalue = 0
itemtype = Edit|1
[Property]
name = locked|Locked
curvalue = False
itemtype = Combo
cmbitems = False|True
equates = False|True
[Property]
name = resizerules|ResizeRules
curvalue =
itemtype = ResizeRules
[Property]
name = tag|Tag
curvalue =
itemtype = Edit|1
[Property]
name = tag2|Tag2
curvalue =
itemtype = Edit|1
[Property]
name = text|Text
curvalue = WsTextBox
itemtype = Edit|1
[Message]
name = WSTEXTBOX_MSG_CHANGE
declare = (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idTextControl As Long) As Long
call = Fly_nResult = //MESSAGE// (Fly_ControlIndex, hWndForm, @Fly_pNotify.hwndFrom, @Fly_pNotify.idFrom)
notification = Notify_Notification
default = True
[Message]
name = WSTEXTBOX_MSG_SETFOCUS
declare = (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idTextControl As Long) As Long
call = Fly_nResult = //MESSAGE// (Fly_ControlIndex, hWndForm, @Fly_pNotify.hwndFrom, @Fly_pNotify.idFrom)
notification = Notify_Notification
[Message]
name = WSTEXTBOX_MSG_KEYPRESS
declare = (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idTextControl As Long, pKeyPressNotify As Dword) As Long
call = Fly_nResult = //MESSAGE// (Fly_ControlIndex, hWndForm, @Fly_pNotify.hwndFrom, @Fly_pNotify.idFrom, Fly_pNotify)
notification = Notify_Notification
[Message]
name = WSTEXTBOX_MSG_KILLFOCUS
declare = (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idTextControl As Long) As Long
call = Fly_nResult = //MESSAGE// (Fly_ControlIndex, hWndForm, @Fly_pNotify.hwndFrom, @Fly_pNotify.idFrom)
notification = Notify_Notification
[Message]
name = WSTEXTBOX_MSG_CLICKED
declare = (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idTextControl As Long) As Long
call = Fly_nResult = //MESSAGE// (Fly_ControlIndex, hWndForm, @Fly_pNotify.hwndFrom, @Fly_pNotify.idFrom)
notification = Notify_Notification
[Message]
name = CUSTOM
I've created the .dll with the 'WsTextBox'-Function.
I've placed all in the 'Custom Control'-Folder of FF.
Now, when I try to create the control (the tool for it is ok), I get an Error-Message: 'Error creating the custom control. Parameters: ......'
If I make a direct call to the 'CreateWindowEx' in the .ctl-File it works.
What do I wrong?
Christian
Quote
If I make a direct call to the 'CreateWindowEx' in the .ctl-File it works.
I assume that the function that creates your textbox is called "WSTEXTBOX" in your include file. In that function you are calling CreateWindowEx, right? That function is "exported", right?
By looking at the ctl file I can not see anything there that looks wrong. Maybe you can email me the project so I can trace it through as it trys to create an instance of the control in the FF3 designer?
Hi Paul,
of course, in a few minutes you will have the project with the dll, the include-file and the .ctl-File.
Thanks
Christian
Hi Christian,
I got your email. I'll send you back the modified files shortly.
For those reading this post, there were a few problems with the .ctl file:
(1) The ALIAS in the create_action property was uppercase but Christian specifically used a non-uppercase Alias when he defined the exported function from the DLL.
(2) No need for use_loadlibrary = 1. Changed that to 0.
(3) The ctl was missing properties for Top, Left, Width, Height (and I added Font as well because his textbox would benefit from it).
(4) He defined notification messages but the WsTextBox.inc did not have any of the equates defined. eg. %WSTEXTBOX_MSG_CHANGE = %WM_USER + 1 so I added those.
Hi Paul,
thanks very much.
I have not made properties for the font and the colors, because this values come from the registry (that,s why I do this). The top, left, width and height must be there, that's ok.
At one try I killed all message-definitions from the .ctl-File, but it doesn't work (because there where other bugs, too -:)
I didn't know, that aliase in declarations are case-sensitive. My SDK-programming level is to low -:)
In which case I need the use_loadlibrary ?
May be there is a way to put the dll-File for more then one custom-controls at a central place? Or have the dll to be in the same folder with the .ctl?
Christian
Hi,
in the meantime I have figured out, the "FF_Control_SetColor" - Function does not work with my custom control.
There is no reaction at all.
I have tried it in the dll and in the mainprog, but there are the syscolors every time.
I have put the "Background Color" - property in the .ctl.
When I change the property in the designer I see the changed color (e.g. yellow). When I run the program, it is white.
What may be wrong?
Christian
Quote from: Christian Weilguny on September 07, 2010, 08:33:10 PM
...the "FF_Control_SetColor" - Function does not work with my custom control.
There is no reaction at all.
This could be a problem for you. The way that I coded FF3 is to disable color processing for all external controls. That way FF3 does not interfere with any processing that a custom control would do. But, this means that the control itself will need to handle the coloring. this is what I do with FireTextBox, FireLink, etc...
As you can see from the generated code, the 6th parameter is FALSE (nProcessColor). Therefore, using FF_Control_SetColor will have no effect in the generated code because the settings will be ignored by FLY_SetControlData.
ff = FLY_SetControlData( hWndControl, %TRUE, %TRUE, _
"Tahoma,-13,0,0,0,400,0,0,0,0,3,2,1,34", 0, %FALSE, _
%FALSE, %FALSE, %FALSE, _
%FALSE, -1, CodePtr(FORM1_CODEPROCEDURE), "" )
The theory is that the custom control should handle color itself. In your case, the best approach is to have a custom message to set the back color and handle it from within the control. You would need to have a parent window in your DLL and use that as the parent window for your TextBox. You would need to save the color/brush using a Prop (GetProp/SetProp) so each instance of the control could have it's own color. In the Form's CUSTOM message handler you would handle the WM_CTLCOLOREDIT message to return a brush for the background color. This is not trivial stuff especially if you don't have a lot of api experience. Sorry.
Quote from: Christian Weilguny on September 07, 2010, 12:46:32 PM
In which case I need the use_loadlibrary ?
Some controls need to use LoadLibrary api call to create an instance of the DLL at runtime. This is somewhat rare these days especially in the PB world so i wouldn't worry about it.
Quote
May be there is a way to put the dll-File for more then one custom-controls at a central place? Or have the dll to be in the same folder with the .ctl?
The dll needs to be in the same folder as the ctl. That's where FF3 expects to find the dll.
Hi Paul,
it's a pity that the colors do not work.
I want only one time (at creation) to set the color from a value in the registry.
And you're right, it's the best to do this in the control-dll, I've first tried to do so. But till now I've no 'generic' way found to do it.
I will have an intensive look at your 'FireTextBox', perhaps I see, what I need.
What meaning has the 'use_initialize_action' in the .ctl-file? I thought, this can be a solution for me.
Christian
Hi Christian,
Hopefully I will have some time today where I can try to create a little demo for you to see if we can make this work. I will be in touch.
Hi Christian,
Here is my first attempt at implementing the type of control that you need. Hopefully it will work okay for you.
Function WsTextBox Alias "WsTextBox"( ByVal hWndFrm As Long, _
ByVal hID As Long, _
ByVal lLeft As Long, _
ByVal lTop As Long, _
ByVal lWidth As Long, _
ByVal lHeight As Long, _
ByVal lStyle As Long, _
ByVal lExStyle As Long _
) Export As Dword
Dim hWndParent As Dword
Dim hWndControl As Dword
Dim IsVisible As Long
Dim hInst As Long
Dim wc As WNDCLASSEX
Dim szClassName As Asciiz * 20
hInst = GetWindowLong(hWndFrm, %GWL_HINSTANCE)
lStyle = lStyle Or %WS_CHILD Or %WS_VISIBLE
' Create a parent window that will hold the textbox control. We use that window to
' handle the color notification messages and to reflect any messages sent to it
' down to the child textBox control.
szClassName = "WSTEXTBOX"
'if not already registered
If GetClassInfoEx(GetModuleHandle(ByVal %Null), szClassName, wc) = 0 Then
wc.cbSize = SizeOf(wc)
wc.Style = %CS_HREDRAW Or %CS_VREDRAW
wc.lpfnWndProc = CodePtr(WsTextBoxProc)
wc.cbClsExtra = 0
wc.cbWndExtra = 4
wc.hInstance = hInst
wc.hIcon = %Null
wc.hCursor = %Null
wc.hbrBackground = %Null
wc.lpszMenuName = %Null
wc.lpszClassName = VarPtr(szClassName)
If RegisterClassEx(wc) = 0 Then
Exit Function
End If
End If
hWndParent = CreateWindowEx( 0, szClassName, "", lStyle, _
lLeft, lTop, lWidth, lHeight, _
hWndFrm, hId, hInst, ByVal 0)
Function = hWndParent
hWndControl = CreateWindowEx( lExStyle, "Edit", "", _
lStyle, 0, 0, lWidth, lHeight, _
hWndParent, 100, hInst, ByVal %Null )
' Set the default font for the control.
SendMessage hWndControl, %WM_SETFONT, GetStockObject(%DEFAULT_GUI_FONT), %TRUE
' Save the background color and background brush values to properties
' of the parent window.
SetProp hWndParent, "BACKCOLOR", %Yellow
SetProp hWndParent, "BACKBRUSH", CreateSolidBrush(%Yellow)
Function = hWndParent
End Function
'------------------------------------------------------------------------------
Function WsTextBoxProc( ByVal hWnd As Dword, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long _
) As Long
Local hWndEdit As Dword
If wMsg <> %WM_CREATE Then
hWndEdit = GetDlgItem(hWnd, 100)
End If
Select Case wMsg
Case %WM_SIZE
' Make sure that the child TextBox resizes to fill the entire
' width/height of the parent window.
SetWindowPos hWndEdit, %HWND_TOP, 0, 0, LoWrd(lParam), HiWrd(lParam), 0
Case %WM_SETFOCUS
'set the focus to the child edit control
SetFocus hWndEdit
Function = 0: Exit Function
Case %WM_NOTIFY 'forward to parent
SendMessage GetParent(hWnd), wMsg, wParam, lParam
' Forward all edit control messages to the embedded edit control
Case %WM_SETTEXT, %EM_GETSEL To %EM_GETIMESTATUS
If IsWindow(hWndEdit) Then
SendMessage hWndEdit, wMsg, wParam, lParam
End If
Case %WM_GETTEXTLENGTH
' Get the text length from the edit control.
If IsWindow(hWndEdit) Then
Function = SendMessage( hWndEdit, %WM_GETTEXTLENGTH, wParam, lParam)
Exit Function
End If
Case %WM_GETTEXT
' Get the text from the edit control.
If IsWindow(hWndEdit) Then
Function = SendMessage( hWndEdit, %WM_GETTEXT, wParam, lParam)
Exit Function
End If
Case %WM_CTLCOLOREDIT
' return the background brush
Dim hBackColor As Long
Dim hBackBrush As Dword
hBackColor = GetProp(hWnd, "BACKCOLOR")
hBackBrush = GetProp(hWnd, "BACKBRUSH")
SetBkColor wParam, hBackColor
Function = hBackBrush
Exit Function
Case %WM_ENABLE
'If the state of the window is changing then we must repaint the
'edit control in order for the text color to be correctly shown.
EnableWindow hWndEdit, wParam
InvalidateRect hWnd, ByVal %Null, %TRUE: UpdateWindow hWnd
Function = 0: Exit Function
Case %WM_DESTROY
' make sure that we destroy the back brush handle before
' the control is destroyed in order to avoid a GDI leak.
DeleteObject GetProp(hWnd, "BACKBRUSH")
RemoveProp hWnd, "BACKCOLOR"
RemoveProp hWnd, "BACKBRUSH"
End Select
Function = DefWindowProc(hWnd, wMsg, wParam, lParam)
End Function
Hi Paul,
Thanks very much, I had a similar solution elaborated (with some bugs :)
It's possible to set the colors directly like this?
Case %WM_CTLCOLOREDIT
' return the background brush
SetBkColor wParam, FF_GetRegistryDword(...)
Function = FF_GetRegistryDword(...)
Exit Function
Christian
Quote from: Christian Weilguny on September 09, 2010, 04:46:51 PM
It's possible to set the colors directly like this?
Almost... you need to return a
brush handle from WM_CTLCOLOREDIT, not the color value itself. You will notice in my example that I retrieve the brush that I created when the control was created. I create the brush at that time rather than having to delete an existing brush, retrieve the color value, create the new brush and return it for every WM_CTLCOLOREDIT message. You need to handle that brush carefully otherwise you will end up with GDI resource leaks.
Hi Paul,
now it works!
But I had to set the width and height for the hWndEdit initially to the correct values (in CreateWindowEx), because otherwise the control was not visible (only the container-window).
Quote... you need to return a brush handle from WM_CTLCOLOREDIT
This is my coloredit-message:
Case %WM_CTLCOLOREDIT
'wParam hold the hDC
SetTextColor wParam, FF_GetRegistryDWord( %HKEY_CURRENT_USER, $USR_REG_PATH, "TxtForeColor", %RGB_BLACK)
SetBkColor wParam, FF_GetRegistryDWord( %HKEY_CURRENT_USER, $USR_REG_PATH, "TxtBkColor", %RGB_LIGHTCYAN)
Function = CreateSolidBrush(FF_GetRegistryDWord( %HKEY_CURRENT_USER, $USR_REG_PATH, "TxtBkColor", %RGB_LIGHTCYAN))
Exit Function
In this case the brush is only return value. I think not, I have to destroy it. I hope, I'm right?
Christian
You can not do this:
Function = CreateSolidBrush(FF_GetRegistryDWord( %HKEY_CURRENT_USER, $USR_REG_PATH, "TxtBkColor", %RGB_LIGHTCYAN))
Every time the message is processed you will leak a GDI resource because you are creating a new brush on EVERY call. That's why you need to create the brush once when the control is created and then use that brush over and over and finally destroy it in WM_DESTROY. Check out the sample code that I posted earlier - it does exactly that.
Ok, thanks Paul, I've created the props on the frame-window.
Now, there is a little thing, I can't pass text from the property-window in FF.
If I set the text in code, it will appear, if I set it in the property-window, it doesn't.
I've seen, the FF_SetText uses the "SetWindowText", may be, this is different to the setting from property-window?
Christian
Unfortunately, at this time you will need to set the text for your control in the WM_CREATE message handler for the Form that your control is on. FF3 does not code generate the "Text" property for External controls other than the ones that ship with FF3 itself (ie. I have hardcoded the code generation for certain properties of those FF3 shipped controls).
Ok, for the textbox I have no solution, but the label works with the "Tag2"-property.
Assigned in the "CTLCOLORSTATIC"-message :).
Christian