Thanks to everybody who has helped over the past few months trying to help me get my silverlight / f# prototype up and running (started in the RC version of VS - Ugh). The last problem we are trying to solve is an RPC issue.
We need to have the ability to paginate RPC calls, such that the first page is requested and bound to the grid and displayed, while the otehr pages are prefilled in the background and concatenated together. I guess psuedo code would look like this:
let pageNo = 1
let page1Data = JsonRpc.getSomeData(pageNo)
let grid.datasource <- page1Data
let grid.suspendFiltering <- true
// run the remainder in background
let allData : list ref = page1Data ref
for pageNo in [2..totalPages]
allData := allData # JsonRpc.getSomeData(pageNo)
let grid.datasource <- allData
let grid.suspendFiltering <- true
I appologize for the code above, I tried to make it as F# like as possible (writing in this text window); another flaw is the need to use call backs to bind the data to grids etc.
The question approaches might be used to solve this problem and what is the most approriate?
hmm... something like this? (typing in browser so may contain errors):
module Loader
open System
open System.Threading
let totalPages = 20
// emulation of long-running data loading routine
let private loadPageData (page : int) =
async {
do! Async.Sleep(1000)
return List.replicate 5 page
}
// loader - notifies UI about new data via callback
let loadAsync (callback : System.Action<_>) =
let syncContext = SynchronizationContext.Current
let doLoad = async {
// load first page and immediately feed it to callback
let! page1Data = loadPageData 1
do! Async.SwitchToContext syncContext
callback.Invoke(ResizeArray<_>(page1Data))
// load remaining data in the background
do! Async.SwitchToThreadPool()
let allData = ResizeArray<_>(page1Data)
for page in 2..totalPages do
let! pageData = loadPageData page
allData.AddRange(pageData)
do! Async.SwitchToContext syncContext
callback.Invoke(allData)
}
Async.Start doLoad
On UI side it will appear like this (i.e. data - ListBox or some other control)
Loader.loadDataAsync(list => data.ItemSource = list)
Related
I'm trying to draw some boxes in Rascal and trying to give each box its own callback function. On entering the box with the mouse the corresponding string should get displayed in the text element (so hovering box1 should display box1 etc.).
However, at the moment the text does pop up but just displays "box3" for each of the 3 boxes.
Any ideas?
strings = ["box1", "box2", "box3"];
boxes = [ box(
size(100, 100),
onMouseEnter(void() {
output = s;
})
) | s <- strings];
render(hcat([
vcat(boxes),
text(str () {return output;})
]));
Good question, classical problem. The essence of the problem is that Rascal uses "non-capturing closures": this means that functions that are returned from another function share the same context. In your case this is the variable s introduced by s <- strings. This nearly always happens when you create function values in a loop (as you do here). The solution is to wrap another function layer around the returned function.
Here is a simple example:
list[int()] makeClosures()
= [ int() {return i;} | i <- [0,1,2]];
void wrong(){
lst = makeClosures();
println(lst[0]());
println(lst[1]());
println(lst[2]());
}
which will print surprisingly the values 2,2and2`. The solution is, as said, to introduce another function level:
int() makeClosure(int i)
= int() { return i;};
list[int()] makeClosuresOK()
= [ makeClosure(i) | i <- [0,1,2]];
void right(){
lst = makeClosuresOK();
println(lst[0]());
println(lst[1]());
println(lst[2]());
}
now calling right() will print 1, 2, and 3 as expected.
I leave it as an exercise how this is done in your example, but I am prepared to give a solution when you ask for it. Good luck!
I'm trying to turn on AutoFilter for the users who will consume the data.
open Microsoft.Office.Interop.Excel
let xl = ApplicationClass()
xl.Workbooks.OpenText(fileName...)
let wb = xl.Workbooks.Item(1)
let ws = wb.ActiveSheet :?> Worksheet
let rows = string ws.UsedRange.Rows.Count
// AutoFilter method of Range class failed.
ws.Range("A7:I" + rows).AutoFilter() |> ignore
Thanks for any help you can offer.
According to the documentation, you need to pass 5 parameters to AutoFilter.
Unspecified parameters can be filled by System.Reflection.Missing.Value.
Something like
ws.Range("A7:I" + rows).AutoFilter(1, System.Reflection.Missing.Value,
Excel.XlAutoFilterOperator.xlAnd,
System.Reflection.Missing.Value, true)
|> ignore
should work.
Learning something new is always following something what already exists. So I reconfigure this example (http://www.websharper.com/samples/KendoChart) which works fine. (only graph on line 12. I saved series into some variable and it looks like this.
So line 12 should be.
let Chart chartType stack =
let data = [|
chart.SeriesConfiguration (
Name = "World",
Data = [|15.7 ; 16.7 ; 20. ; 23.5; 26.6|]
)
chart.SeriesConfiguration (
Name = "United States",
Data = [|67.96 ; 68.93 ; 75. ; 74. ; 78.|]
)
|]
Where data is on line 36 Series = data... and that works fine.
But what if I want my data to be ...
let Chart chartType stack =
let techs = StoneMiner.Charts.technologies()
let data = seq { for i in techs do
yield chart.SeriesConfiguration (
Name = fst i ,
Data =[|snd i|]//; 16.7 ; 20. ; 23.5; 26.6|]
) }
|> Seq.toArray
where let techs = StoneMiner.Charts.technologies() is technologies is in another Namespace and module...
I get some strange error...
Error 1 Failed to translate a method call: technologies(..) [StoneMiner.Charts]. Because it is in another modul.
It seems your function StomeMiner.Charts.technologies doesn't have a [<JavaScript>] annotation. This prevents it from being translated into JavaScript and thus used on client-side.
As a rule of thumb, anything you use from the client-side needs to be annotated with one of the attributes from IntelliFactory.WebSharper.Core.Attributes (which are accessible directly when you open IntelliFactory.WebSharper). Depending on the situation, it can be [<JavaScript>] (for translation to JS), [<Inline "...">] (to inline some JS code), [<Remote>] (for Ajax RPC calls to the server) or less common ones like [<Stub>] or [<Direct>].
This function is fine, but it doesn't do what I would like it to, but I hav eused it to make sure the use of objects is OK:
let getStreamData_ok (uri:string) =
let request = WebRequest.Create uri
use response = request.GetResponse()
use stream = response.GetResponseStream()
use reader = new StreamReader(stream)
while not reader.EndOfStream do
ignore <| reader.ReadLine()
I would like to connect to a stream and pull the file down one line at a time, on demand. This function doesn't work, I have tried shifting various lines in and out of the sequence expression without any success:
let getStreamData_OnDemand (uri:string) =
let request = WebRequest.Create uri
use response = request.GetResponse()
seq {
use stream = response.GetResponseStream()
use reader = new StreamReader(stream)
while not reader.EndOfStream do
yield reader.ReadLine()
}
Usage code:
let lines = getStreamData_OnDemand("http://stackoverflow.com/")
for line in lines do
ignore line
Thank you
This should work:
let getStreamData_OnDemand (uri:string) = seq {
let request = WebRequest.Create uri
use response = request.GetResponse()
use stream = response.GetResponseStream()
use reader = new StreamReader(stream)
while not reader.EndOfStream do
yield reader.ReadLine() }
The key difference compared to your second code snippet (the one that uses sequence expressions) is that everything is done inside a sequence expression. Most importantly, the use response = ... line is also enclosed in the sequence expression.
This is essential, because use in this case means that the response will be disposed only after the iteration over the sequence is completed. In your second code snippet, you would dispose response before anything is read from the returned sequence.
In your original snippet, it is disposed after getStreamData_OnDemand returns, but that's before you even started iterating over the sequence - so when you start iterating over the sequence, it is already disposed!
I would like to display a directory structure using Gtk# widgets through F#, but I'm having a hard time figuring out how to translate TreeViews into F#. Say I had a directory structure that looks like this:
Directory1
SubDirectory1
SubDirectory2
SubSubDirectory1
SubDirectory3
Directory2
How would I show this tree structure with Gtk# widgets using F#?
EDIT:
gradbot's was the answer I was hoping for with a couple of exceptions. If you use ListStore, you loose the ability to expand levels, if you instead use :
let musicListStore = new Gtk.TreeStore([|typeof<String>; typeof<String>|])
you get a layout with expandable levels. Doing this, however, breaks the calls to AppendValues so you have to add some clues for the compiler to figure out which overloaded method to use:
musicListStore.AppendValues (iter, [|"Fannypack" ; "Nu Nu (Yeah Yeah) (double j and haze radio edit)"|])
Note that the columns are explicitly passed as an array.
Finally, you can nest levels even further by using the ListIter returned by Append Values
let iter = musicListStore.AppendValues ("Dance")
let subiter = musicListStore.AppendValues (iter, [|"Fannypack" ; "Nu Nu (Yeah Yeah) (double j and haze radio edit)"|])
musicListStore.AppendValues (subiter, [|"Some Dude"; "Some Song"|]) |> ignore
I'm not exactly sure what you're looking for but here is a translated example from their tutorials. It may help you get started. Image taken from tutorial site.
I think the key to a multi-level tree view is to append values to values, iter in this line musicListStore.AppendValues (iter, "Fannypack", "Nu Nu (Yeah Yeah) (double j and haze radio edit)") |> ignore
// you will need to add these references gtk-sharp, gtk-sharp, glib-sharp
// and set the projects running directory to
// C:\Program Files (x86)\GtkSharp\2.12\bin\
module SOQuestion
open Gtk
open System
let main() =
Gtk.Application.Init()
// Create a Window
let window = new Gtk.Window("TreeView Example")
window.SetSizeRequest(500, 200)
// Create our TreeView
let tree = new Gtk.TreeView()
// Add our tree to the window
window.Add(tree)
// Create a column for the artist name
let artistColumn = new Gtk.TreeViewColumn()
artistColumn.Title <- "Artist"
// Create the text cell that will display the artist name
let artistNameCell = new Gtk.CellRendererText()
// Add the cell to the column
artistColumn.PackStart(artistNameCell, true)
// Create a column for the song title
let songColumn = new Gtk.TreeViewColumn()
songColumn.Title <- "Song Title"
// Do the same for the song title column
let songTitleCell = new Gtk.CellRendererText()
songColumn.PackStart(songTitleCell, true)
// Add the columns to the TreeView
tree.AppendColumn(artistColumn) |> ignore
tree.AppendColumn(songColumn) |> ignore
// Tell the Cell Renderers which items in the model to display
artistColumn.AddAttribute(artistNameCell, "text", 0)
songColumn.AddAttribute(songTitleCell, "text", 1)
let musicListStore = new Gtk.ListStore([|typeof<String>; typeof<String>|])
let iter = musicListStore.AppendValues ("Dance")
musicListStore.AppendValues (iter, "Fannypack", "Nu Nu (Yeah Yeah) (double j and haze radio edit)") |> ignore
let iter = musicListStore.AppendValues ("Hip-hop")
musicListStore.AppendValues (iter, "Nelly", "Country Grammer") |> ignore
// Assign the model to the TreeView
tree.Model <- musicListStore
// Show the window and everything on it
window.ShowAll()
// add event handler so Gtk will exit
window.DeleteEvent.Add(fun _ -> Gtk.Application.Quit())
Gtk.Application.Run()
[<STAThread>]
main()