I am very new to F#, and I thought I would develop a simple winform calculator to strengthen my skills with F# and .NET. I ran into two problems which probably have very simple answers, however, I could not find any assistance from my resources.
First, I want to be able to change my textbox type to integer, so that when I press my "add" button the event will add the integers and not concatenate them. For example:
btnPlus.Click.Add(fun _ -> tb1.Text <- ("2") + ("2")) gives me 22 and not 4
How do I specify integer type for a textbox or integer type for values entered into a textbox?
My next question has to do with syntax. I was just wondering how to add multiple commands to an event. That is, in the above example, let say I wanted to add the two integers, plus open another form, and run a messagebox, after clicking the button. Do I just include commas between each command, such as: btnPlus.Click.Add(fun _ -> add integers, open form, messagebox) or is there something else involved?
I apologize for the sophomoric questions. There are not enough resources on F# and winforms as there are on F# and output (i.e. print, cmd, etc..)
Thank you,
DFM
I think the textbox text is always a string, but you can do e.g.
(2 + 2).ToString()
(not quite sure what you're after).
As for multiple statements inside the body of the lambda, F# is expression-based, but uses ';' or newlines for sequencing, so e.g.
(fun _ -> doFoo(); doBar())
or
(fun _ ->
doFoo()
doBar()
)
You simply have to parse the textbox contents as an integer. You can do this via Convert.ToInt32.
open System
let string1 = "254"
let string2 = "4525"
let myNum = Convert.ToInt32(string1) + Convert.ToInt32(string2)
Edit: You could also use Int32.TryParse(string, int32) to do it.
Edit: In F#, you can just use "int", e.g. (int string1).
Related
I need to understand how I can relate the array of controls I dim in VBA code with the related form and the syntax etc. needed in the code to manipulate the array.
I'm wanting to populate and present an array of textboxes with strings that have been constructed as a result of processing data in a series of tables.
As an initial test, I've tried the following code. I've not yet thought of any way I might attempt to create an array on the surface of a form.
I have several text books on VBA but none of them seem to have anything to say on this.
Can anyone throw any light on this or recommend a more advanced text book?
Dim mytext(20) As TextBox
Dim x As Long
For x = 0 To 19
mytext(x).Value = str(x)
Next x
This results in an error at line 4:
Object variable or With block variable not set
How are you populating your array of Textboxes?
Since Textboxes are objects, you'll need to use Set, e.g.:
Dim mytext(20) As TextBox
Set mytext(0) = Text0
Set mytext(1) = Text2
Set mytext(2) = Text4
Set mytext(3) = Text6
...
Aside, Str is a built-in function in VBA, it should not be used as the name of a
variable.
I want to figure out how the mouseWheel event works in the winforms library.
Though there is never any good examples to learn when it comes to F#
I hope one of you guys can tell me.
i was thinking something like this:
onMouseWheelEvent =
if MouseWheel event = scroll forward then
printfn "zooming in"
else
printfn "zooming out"
I know it is some silly code i am posting but i literally cant find anything on the winforms mousewheelevent other than it exists. How it works remains a mystery.
thanks in advance.
Try running this in f# interactive (courtesy of a similar example on fssnip from Tomas Petricek):
open System.Windows.Forms
// Create form, label and add label to form
let form = new Form(Text = "Scroller Test")
let label = new Label()
form.Controls.Add(label)
// register mousewheel event handler and capture scrolling
form.MouseWheel.Add(fun e ->
match e.Delta with
| n when n > 0 -> label.Text <- "Scrolled up"
| n when n < 0 -> label.Text <- "Scrolled down"
| _ -> ()) // making compiler happy even though e.Delta cannot be 0
// Show the form (in F# Interactive)
form.Show()
If you are using F# interactive, the simplest thing you can write to test how the MouseWheel event behaves is something like this:
open System.Windows.Forms
let frm = new Form(Visible=true)
frm.MouseWheel.Add(fun e ->
printfn "%A" e.Delta
)
When you select the code and send it to F# Interactive (Alt+Enter in most F# editors), you should see a form appear. When you then scroll, you'll see that Delta is negative or postivie number indicating how much up or down you scrolled.
If you want to run this as a standalone application, you'll need to display the numbers elsewhere (using a label, I guess) and you'll need to add Application.Run(frm) to the end to start the application.
I'd agree with Anton that WinForms is perhaps not what most people use for developing user interface applications these days, but I think that's not a problem - you can use WinForms to build perfectly fine applications and learn F#. The F# book I wrote some time ago has a bunch of WinForms examples.
So I'm trying to set up a VB Windows Form that reads data from an array and displays it in a label (and sometimes textboxes so it could be edited). The array contains strings and I know they are stored properly because I can make them appear properly when using MsgBox.
For example
MsgBox(ArrayName(0,0))
works, but when I use
textboxname.Text = ArrayName(0,0)
I get a build error. I thought adding ".ToString" at the end might solve this, and the build error is gone, but then the textbox says "System.Char[]" instead of the value that I put inside the array. The MsgBox shows this as well, if I put ".ToString" at the end.
Thank you in advance for your help!
Edit: The array is declared via the code:
Module ModuleName
Public ArrayData(20, 6) As Array
End Module
so that I can edit it outisde of any specific subroutines.
The array only contains the characters (delimiter is comma): 0,1,2,3,4,5,6 in the first row and q,w,e,r,t,y in the second row.
I am working on a very old visual basic project. I have to rewrite the load and save function of the project. Therefore I wanted to create an array of controls which have all relevant textboxes and checkboxes included. I want to iterate through that array to be able to save the data into a textfile or load from it.
I have looked on the internet of how you can define those arrays, but it doesn't seem to work for me. Maybe I am doing something wrong, cause I am not an expert in Visual Basic.
I have tried to make it work this way:
Dim tbList As TextBox = { Form1.Text1, Form1.Text3, _
Form1.Text10, Form1.Text11, Form1.Text12, Form1.Text13, _
Form2.Text1, Form2.Text3, Form2.Text4, Form2.Text5, _
Form2.Text10, Form2.Text11, Form2.Text12, Form2.Text13, _
Form3.Text1, Form3.Text3, Form3.Text4, Form3.Text5, _
Form3.Text10, Form3.Text11, Form3.Text12, Form3.Text13, _
Form3.Text17, Form3.Text18, Form3.Text19, Form3.Text20, _
Form4.Text1, _
Form5.Text1, Form5.Text2, Form5.Text3, _
Form6.Text2, _
Form7.Text2}
Or with a list:
Dim tbList As New List(Of Controls)
The thing is Visual Basic always tells me there are some kind of compiling issues. There is no real explantation for this issue in VB, so I am asking here.
Your code isn't compiling because it is vb.net code. It should go without saying (but I'll say it anyway) that vb6 and vb.net are not the same thing.
If you want to use an array, you will have to dimension the array with a number that is one less than your number of textboxes (if I counted correctly there are 32 in your example):
'// array is zero based so 0 to 31 = 32 items
Dim tbList(31) As TextBox
tbList(0) = Form1.Text1
tbList(1) = Form1.Text3
'//...I'll leave the rest as an exercise for the programmer
tbList(31) = Form7.Text2
Dim i As Integer
Dim tb As TextBox
'// To loop and work with each textbox
For i = 0 To UBound(tbList)
Set tb = tbList(i)
'// do something with tb
Next
An easier way to do it, however, is to work with a collection:
Dim tbList As New Collection
tbList.Add Form1.Text1
tbList.Add Form1.Text3
'//...I'll leave the rest as an exercise for the programmer
tbList.Add Form7.Text2
Dim tb As TextBox
'// To loop and work with each textbox
For Each tb In tbList
'// do something with tb
Next
Yes, you can use a collection if you want to go to the trouble. But an even easier way to work with it is to use VB6's (now obsolete) control array implementation. Most of us were disappointed when we found it was no longer available in .Net!
All you have to do to get control arrays in VB6 is create a bunch of controls with the same name. (They do have to be the same type of control; you can't make arrays of, say, text boxes and combo boxes.) Start with one text box, name it what you want, and copy it. You will be asked if you want to create a control array. Say yes, copy as many as you want. You will notice that the Index property is no longer blank, starting with 0 and incrementing from there. You will also notice that all of the event handlers have an "Index As Integer" argument to them. This way, you can use the same event handler for all of them, evaluating the Index argument to find out which member of your array is firing the event.
Here is the old doc for it. Microsoft makes it hard to find. :)
I am interested if it is possible to make variable name in PowerBuilder using a loop and a string. For example:
long ll_go
string lst_new
for ll_go = 1 to 8
lst_new = "text" + ll_go
lst_new.tag = 5500
next
So, it should give me variables text1, text2..,.,text8 and I would be able to assign values for them. Let me know if anybody succeeded, thanks in advance
Your description is lacking some term precision.
If you actually want to dynamically create new variables as "variable in a powerscript subroutine or function" this is simply not possible.
If instead you want to create dynamically some new controls statictext or textedit objects in a window or visual userobject this is possible:
use a local variable of the type of the new object you need to create, e.g. static text
make it a live object (instantiate) with create
set the object properties to whatever you need
"attach" the new object to its parent (either a window or a visual userobject - though any graphicobject is possible with using the win32api SetParent function) with the OpenUserObject() method. Note that you cannot simply add it directly to the parent's Control[] array.
you can also keep the object in your own array for later convenience access to the created objects instead of looping on the Control[] array
once the object is attached it its parent, you can reuse the local variable to create another one
Here is an example:
//put this in a button clicked() event on a window
//i_myedits is declared in instances variables as
//SingleLineEdit i_myedits[]
SingleLineEdit sle
int i
for i = 1 to 8
sle = create singlelineedit
sle.text = string(i)
sle.tag = "text_" + string(i)
sle.height = pixelstounits(20, ypixelstounits!)
sle.width = pixelstounits(100, xpixelstounits!)
parent.openuserobject(sle, pixelstounits(10, xpixelstounits!), pixelstounits(22 * i, ypixelstounits!))
i_myedits[i] = sle //keep our own reference
next
An exemple of values access:
//put that in another button clicked() event
SingleLineEdit sle
int i
string s_msg
for i = 1 to upperbound(i_myedits[])
sle = i_myedits[i]
if i > 1 then s_msg += "~r~n"
s_msg += "edit #" + string(i) + " (" + sle.tag + ") says '" + sle.text + "'"
next
messagebox("Edits values", s_msg)
As you can see, one practicality problem is that you cannot refer to these controls by constructing the control's name like "text"+2, instead you must access the my edits[] array or loop through the controls and test their .tag property if you set it to something specific.
I do not think that it is possible. Workaround could be an array maybe.
Br. Gábor
I'd see two ways to do this, but they aren't as easy as it seems that you were hoping:
1. Control Array
First method would be to go through the control arrays (on windows, tabs and user objects). I'd create a function that took the control name as a string, then another that overloaded the same function and took control name and an array of windowobject. The string-only method would just call the string/array method, passing the string through and adding the window.Control as the second parameter. The string/array method would go through the array, and for each element, get the ClassDefinition. Pull the name off of it, and parse it apart the way you want it to match the string parameter (e.g. for w_test`tab_first`tabpage_first`cb_here, do you want cb_here to match, or tab_first`tabpage_first`cb_here?). Deal with matches as appropriate. When you find a control of type tab or user object, call the string/array function again with the Control array from that object; deal with success/fail returns as appropriate.
2. DataWindow
What you're describing works extremely well with DataWindows, and their Describe() and Modify() functions. Since you pass these functions only a string, you can build not only the control names, but the values they're set to as you would build any string. In fact, you can build multiple Modify() strings together (delimited by a space) and make a single call to Modify(); this is not only faster, but reduces window flicker and visible activity.
Don't fall into the trap of thinking that, since your data isn't from a database, you can't use a DataWindow. Create an external DataWindow, and simply use it with one row inserted during the Constructor event.
As you might guess, I'd strongly favour the DataWindow approach. Not only is it going to perform better, but it's going to provide a lot more flexibility when you want to move on and tag more control types than just static text. (You'll have to do some type casting even with one control type, but if you want to get into multiples, you'll need to start a CHOOSE CASE to handle all your types.)
Good luck,
Terry
You can't create a variable name in a script because the variables have to be declared before you can use them. With PBNI it's possible to generate a name the way you describe and then get a reference to a variable of that name that already exists but I don't think that's what you want. If you want to keep track of additional properties for your controls, just inherit a new user object from whatever it is (sle, mle, etc.) and add the properties you want. Then you can place your user object on a window and use the properties. Another approach is to use the control's Tag property. It holds a string that you can put whatever you want in. PFC uses this technique. Terry's DataWindow solution is a good approach for storing arbitrary data.
Yes, and there are more than one way to skin a cat.
Sounds like you have several properties so I'd use an array of custom non visual user objects, or an array of structures. Otherwise you could probably use something from the .NET framework like a dictionary object or something like that, or a datawidnow using an external datasource, where you can refer to column names as col + ll_index.ToString().
SIMPLE Example:
Make custom NVO with following instance variables, plus getter/setter functions for each, name it n_single_field
// add the properties and recommend getter and setter functions
public string myTag
public string myText
public int myTabOrder
...
// To USE the NVO define an unbounded array
n_single_field fields[]
// to process the populated fields
integer li_x, li_max_fields
// loop through field 1 through max using array index for field number
li_max_fields = upperbound(fields)
for li_x = 1 to li_max_fields
fields[li_x].myTag = 'abc'
fields[li_x].myText = 'text for field number ' + li_x.ToString()
fields[li_x].myTabOrder = li_x * 10
next
Maybe I'm oversimplifying if so let me know, if there is a will there is always a way. ;)