A form has been created. It can be seen, but 'Load' event doesn't fire. This is the simplest case:
type Program() as this =
let form_ = new Form(Visible=true, Text="Some Caption", Width=1024, Height=768)
do
form_.Load.Add(this.OnFormLoad)
form_.Show()
member x.OnFormLoad(e) =
Trace.WriteLine("OnFormLoad() entering...")
member x.form = form_
#if COMPILED
[<STAThread()>]
let program = new Program()
Application.Run(program.form)
#endif
Where am I wrong in this code?
Try removing Visible=true from the call that constructs the form.
I think that when you set Visible to true, the form gets immediately created and loaded, so the Load event is triggered during the form construction (before you setup the handler). I would also remove the call form_.Show(). I think the form will be opened by Application.Run.
Related
Being new to F#, I'm trying to understand how to make graphic updates in a Form triggered by timer events.
My expectation was that the below simple routine should continue drawing new "random" lines every second.
Calling line() outside the timer event works seemingly without any problems, but I cannot get my head around why nothing gets displayed on the screen when the very same function is invoked via the timer event.
open System
open System.Drawing
open System.Windows.Forms
let form = new Form(Text="Simple Animation", Size=Size(400,500))
let pen = new Pen(Color.Red, 4.0f)
let random = new Random()
let line x =
let flexRight = random.Next(29,300)
form.Paint.Add (fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, flexRight))
let timer=new Timer(Interval=1000, Enabled=true)
timer.Tick.Add(fun time -> line())
form.Show()
Application.Run(form)
Any help very much appreciated, Thanks.
The major problem with you code is that on each timer tick just another brand new Paint event handler is added to your form instead of invoking a single registered OnPaint callback that would perform the drawing.
You may get rid of your line function definition and register instead a single Paint callback as
form.Paint.Add(fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, random.Next(29,300)))
Then on each timer tick Paint event may be fired, for example, by invalidating the form. This may be achieved by changing timer's callback code to
timer.Tick.Add(fun _ -> form.Invalidate())
The entire behaving as expected snippet is listed below:
#r "System.Windows.Forms"
open System
open System.Drawing
open System.Windows.Forms
let form = new Form(Text="Simple Animation", Size=Size(400,500))
let pen = new Pen(Color.Red, 4.0f)
let random = new Random()
form.Paint.Add(fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, random.Next(29,300)))
let timer=new System.Windows.Forms.Timer(Interval=1000, Enabled=true)
timer.Tick.Add(fun _ -> form.Invalidate())
form.Show()
UPDATE: as it came out the original intent was showing on the form the superposition of all subsequent drawn lines, I provide one of possible ways to accommodate such behavior with the help of GraphicsPath. Utilizing it would require the following changes to the snippet above:
before the line adding the form Paint event handler add the line creating the instance of GraphicsPath
let gp = new System.Drawing.Drawing2D.GraphicsPath()
change the Paint event handler to
form.Paint.Add(fun e -> gp.AddLine(30,30,350,random.Next(29,300))
e.Graphics.DrawPath(pen, gp))
I am creating an item selector with two boxes that move things back and forth within an extjs application. On the right box, I am creating buttons that serve to move items up and down. Essentially I am swapping the item with one above or below it. So, my code is straight forward in that regard
MoveUp: function(button, event, eOpts){
var theChosen = Ext.getStore('storeId').getRootNode().findChild('text', 'Chosen folder');
var chosenPanel = Ext.ComponenetQuery.query('#chosenTreePanel')[0];
var selected = chosenPanel.getSelectionModel().getSelection();
for( var i = 1; i < theChosen.childNodes.length; i++){
if(Ext.Array.contains(selected, theChosen.childNodes[i]) && (!Ext.Array.contains(selected, theChosen.childNodes[i-1]){
var temp = theChosen.childNodes[i];
theChosen.childNodes[i] = theChosen.childNodes[i-1];
theChosen.childNodes[i-1] = temp;
}
}
}
All of this code seems to work fine, because after clicking my button, and checking the DOM in firebug, I can see that the selected nodes have moved in the array correctly, however, this effect is never shown within my treepanel. ???How do I make the treepanel update when it's elements change. ???
TreePanel heirarchy looks like this just to clarify
Root Node
'Chosen Folder Node'
Array of items I am moving up and down within the 'folder'
I am USING VERSION 4.0.7
Attempting to use replaceChild() to fire an event to rerender does not behave as I expected
Changing:
var temp = theChosen.childNodes[i];
theChosen.childNodes[i] = theChosen.childNodes[i-1];
theChosen.childNodes[i-1] = temp;
To:
var temp = theChosen.childNodes[i];
theChosen.replaceChild(theChosen.childNodes[i-1], theChosen.childNodes[i]);
theChosen.replaceChild(temp, theChosen.childNodes[i-1]);
Results in odd behavior in which some nodes go missing. Certaintly not what I was looking for. Where am I going wrong here?
Tried the following code using the datachanged and (undocumented)refresh event:
Ext.getStore('storeId').fireEvent('datachanged', Ext.getStore('chosen') );
Ext.getStore('storeId').fireEvent('datachanged', Ext.getStore('chosen') );
This does not reload anything...
SOLUTION:
Use the insertChild method of nodeInterface....
I have noticed something strange in how insertChild works in that I need to change my index more based on moving up or down will explain with code below.
To move Up:
theChosen.insertChild( (i-1), theChosen.childNodes[i]);
To move down:
theChosen.insertChild( (i+2), theChosen.childNodes[i]);
Although the -1 vs +2 they both effectively move the item by one in the appropriate direction.
If you want to update the view of the nodes, I recommend using yourTree.getView().refresh();
But you can avoid that by using parentNode.insertChild(index, childNode); where index is where you want the node to show up and parentNode is the parent to the nodes you are reordering. ChildNode can be a config for a new node or any other nodeinterface that already exists. If the node does already exist and you use the insertChild method to insert it, it will automatically remove that node from whereever else it is.
So as you provided in your question in response to my answer, your code will work with something like (this is probably how I'd do it, but this is untested):
MoveUp: function(button, event, eOpts){
var chosenPanel = Ext.ComponenetQuery.query('#chosenTreePanel')[0];
var selectedNodes = chosenPanel.getSelectionModel().getSelection();
for( var i = 0; i < selectedNodes.length; i++){
var currentNode = selectedNodes[i];
if(!currentNode.isFirst())
{
var parentNode = currentNode.parentNode;
var newIndex = parentNode.indexOf(currentNode) - 1;
parentNode.insertChild(newIndex, currentNode);
}
}
}
Edit:
Back to the the responsible event question...
You need to fire the
'datachanged'
'refresh'
Events on the store with the store as only param. That should cause a UI update. But please note that I just had a glimpse into the sourcecode and I am sure this can all be done much smarter. At least a DD solution for this exists already.
If I find some time I my look into this again, but I guess you should be fine with these events for the first.
You never see anything cause you just do it without the appropriate methods that then fire the responsible events that cause rerender. Take a look at
appendChild()
removeChild()
replaceChild()
There may also be more methods that can help on the Ext.data.NodeInterface. I recommend you to use these instead of doing it under hood without any responsible event fired.
In addition to my second comment (just a wild guess without knowing if that is exactly what you want):
MoveUp: function(button, event, eOpts){
var target = Ext.getStore('storeId').getRootNode().findChild('text', 'Chosen folder');
var selected = chosenPanel.getSelectionModel().getSelection();
target.appendChild(selected);
}
I want to execute a data control operation (CreateInsert and Delete) from a buttons ActionListener. I am aware a data control button can be inserted from the Data Controls menu, but for various reasons I need to do it this way, a prominent one being I need to perform extra runtime checks.
I found the following code:
OperationBinding operation = bindings.getOperationBinding("operation_name");
operation.getParamsMap().put("parameter_name", parameterValue);
operation.execute();
But don't know which variables to use for myself. First of all, I don't know which binding I should use. Then, the operation name should, as far as I know, be CreateInsert, and for the next button, CreateInsert1. Thats whats used for UIBinding now (which I will remove).
The Data control I want to use the operation of is 'ARNG1'.
So in short, I need to know how to manually invoke this Data control's CreateInsert operation.
Thanks in advance.
See if this will help you:
https://blogs.oracle.com/shay/entry/doing_two_declarative_operatio
the code you want to execute an operation behind a actionlistener:
public BindingContainer getBindings() {
if (this.bindings == null) {
FacesContext fc = FacesContext.getCurrentInstance();
this.bindings = (BindingContainer)fc.getApplication().
evaluateExpressionGet(fc, "#{bindings}", BindingContainer.class);
}
return this.bindings;
}
BindingContainer bindings = getBindings();
OperationBinding operationBinding =
bindings.getOperationBinding("doQueryResultReset");
operationBinding.execute();
Similar to Joe's answer but does not use EL Expression evaluator and uses direct access instead to get the BindingContainer
//Get binding container
BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
// get an Action or MethodAction
OperationBinding method = bindings.getOperationBinding("methodAction");
method.execute();
List errors = method.getErrors();
I'm trying to implement drag and drop in Silverlight using F# and asynchronous workflows.
I'm simply trying to drag around a rectangle on the canvas, using two loops for the the two states (waiting and dragging), an idea I got from Tomas Petricek's book "Real-world Functional Programming", but I ran into a problem:
Unlike WPF or WinForms, Silverlight's MouseEventArgs do not carry information about the button state, so I can't return from the drag-loop by checking if the left mouse button is no longer pressed. I only managed to solve this by introducing a mutable flag.
Would anyone have a solution for this, that does not involve mutable state?
Here's the relevant code part (please excuse the sloppy dragging code, which snaps the rectangle to the mouse pointer):
type MainPage() as this =
inherit UserControl()
do
Application.LoadComponent(this, new System.Uri("/SilverlightApplication1;component/Page.xaml", System.UriKind.Relative))
let layoutRoot : Canvas = downcast this.FindName("LayoutRoot")
let rectangle1 : Rectangle = downcast this.FindName("Rectangle1")
let mutable isDragged = false
do
rectangle1.MouseLeftButtonUp.Add(fun _ -> isDragged <- false)
let rec drag() = async {
let! args = layoutRoot.MouseMove |> Async.AwaitEvent
if (isDragged) then
Canvas.SetLeft(rectangle1, args.GetPosition(layoutRoot).X)
Canvas.SetTop(rectangle1, args.GetPosition(layoutRoot).Y)
return! drag()
else
return()
}
let wait() = async {
while true do
let! args = Async.AwaitEvent rectangle1.MouseLeftButtonDown
isDragged <- true
do! drag()
}
Async.StartImmediate(wait())
()
Thank you very much for your time!
The way to solve this issue is to use an overloaded AwaitEvent that allows you to wait for two events. Instead of just waiting for MouseMove, you can also wait for the MouseUp event - in the first case, you can continue moving and in the second case, you can return from the loop and stop drag&drop (this is actually discussed later in the book in section 16.4.5).
Here is the code - it actually uses AwaitObservable variant of the method (see below), which is a better choice in general, because it works with Observable.map and similar combinators (in case you wanted to use these).
let! args = Async.AwaitObservable(layoutRoot.MouseMove, layoutRoot.MouseUp)
match args with
| Choice1Of2(args) ->
// Handle the 'MouseMove' event with 'args' here
Canvas.SetLeft(rectangle1, args.GetPosition(layoutRoot).X)
Canvas.SetTop(rectangle1, args.GetPosition(layoutRoot).Y)
return! drag()
| Choice2Of2(_) ->
// Handle the 'MouseUp' event here
return()
As far as I know, the overloaded AwaitObservable method is not available in the F# libraries (yet), but you can get it from the book's web site, or you can use the following code:
// Adds 'AwaitObservable' that takes two observables and returns
// Choice<'a, 'b> containing either Choice1Of2 or Choice2Of2 depending
// on which of the observables occurred first
type Microsoft.FSharp.Control.Async with
static member AwaitObservable(ev1:IObservable<'a>, ev2:IObservable<'b>) =
Async.FromContinuations((fun (cont,econt,ccont) ->
let rec callback1 = (fun value ->
remover1.Dispose()
remover2.Dispose()
cont(Choice1Of2(value)) )
and callback2 = (fun value ->
remover1.Dispose()
remover2.Dispose()
cont(Choice2Of2(value)) )
// Attach handlers to both observables
and remover1 : IDisposable = ev1.Subscribe(callback1)
and remover2 : IDisposable = ev2.Subscribe(callback2)
() ))
I have the following method that is executing twice every time it is called:
public static void ChangeToRepository(RepositoryTextBox textBox, int repositoryNumber)
{
MessageBox.Show("you");
int indexOfLastRepository = (textBox.RepositoryCollection.Count - 1);
if (repositoryNumber > indexOfLastRepository)
{
AddTextRepositoriesThrough(textBox, repositoryNumber, indexOfLastRepository);
}
textBox.RepositoryCollection[textBox.CurrentRepositoryNumber].CurrentText = textBox.Text;
textBox.PreviousRepositoryNumber = textBox.CurrentRepositoryNumber;
textBox.CurrentRepositoryNumber = repositoryNumber;
textBox.Text = textBox.RepositoryCollection[textBox.CurrentRepositoryNumber].CurrentText;
}
The first time that the method executes, it executes all of the code except for its last line:
textBox.Text = textBox.RepositoryCollection[textBox.CurrentRepositoryNumber].CurrentText;
The second time, it executes all of the code. What's up?
When you assign to CurrentRepositoryNumber on the text box, it probably triggers an event handler that calls back to this function again. This seems likely because the property name suggests that it controls the current repository, which this method then is responsible for displaying somehow.
You might want to temporary delist, assign to the property and then re-enlist that event handler. Or maybe you need more of a redesign to get the responsibilities clear - often with GUI frameworks that is hard to do, and the simplest option is to just delist, assign, re-enlist, with this kind of pattern:
textBox.TextChange -= YourHandler;
textBox.Text = newValue;
textBox.TextChange += YourHandler;