How does the WinForms MouseWheel event work in F#? - winforms

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.

Related

OxyPlot performance issue on larg data in WPF on InvalidatePlot

I'm using OxyPlot in my wpf application as line recorder. It's like the LiveDemo example.
On a larg visible data set, I get some UI performance issues and may the whole application could freez. It seems to be PlotModel.InvalidatePlot which is called with to many points to often, but I didn't found a better way.
In deep:
Using OxyPlot 2.0.0
I code all in the PlotModel. The Xaml PlotView is only binding to the PlotModel.
I cyclical collect data in a thread an put them in a DataSource (List of List which are ItemSoure for the LineSeries)
I have a class which calculates cyclical in a thread the presentation for x and y axis and a bit more. After all this stuff, it calls PlotModel.InvalidatePlot.
If I
have more than 100 k points on the display (no matter if in multiple LineSeries or not)
and add 1 DataPoint per LineSeries every 500 ms
and call PlotModel.InvalidatePlot every 200 ms
not only the PlotView has performance issues, also the window is very slow in reaction, even if I call PlotModel.InvalidatePlot (false).
My goal
My goal would be that the Windo / Application is working normally. It should not hang up because of a line recorder. The best would be if it has no performance issues, but I'm skeptical.
What I have found or tested
OxyPlot has Performance guidelines. I'm using ItemsSource with DataPoints. I have also tried adding them directly to the LineSeris.Points, but then the Plot doesn’t refresh anyway (even with an ObservableCollection), so I have to call PlotModel.InvalidatePlot, what results in the same effect. I cannot bind to a defined LineSeries in Xaml because I don’t know how much Lines will be there. Maybe I missed something on adding the points directly?
I have also found a Github issue 1286 which is describing a related problem, but this workaround is slower in my tests.
I have also checked the time which is elapsed on the call of PlotModel.InvalidatePlot, but the count of points does not affect it.
I have checked the UI thread and it seems it have trouble to handle this large set of points
If I zoom in to the plot and display under 20 k Points it looks so
Question:
Is there a way to handle this better, except to call PlotModel.InvalidatePlot much less?
Restrictions:
I also must Update Axis and Annotations. So, I think I will not come around to call PlotModel.InvalidatePlot.
I have found that using the OxyPlot Windows Forms implementation and then displaying it using Windows Form integration in WPF gives much better performance.
e.g.
var plotView = new OxyPlot.WindowsForms.PlotView();
plotView.Model = Plot;
var host = new System.Windows.Forms.Integration.WindowsFormsHost();
host.Child = plotView;
PlotContainer = host;
Where 'Plot' is the PlotModel you call InvalidatePlot() on.
And then in your XAML:
<ContentControl Content="{Binding PlotContainer}"/>
Or however else you want to use your WindowsFormsHost.
I have a similar problem and found that you can use a Decimator in LineSeries. It is documented in the examples: LineSeriesExamples.cs
The usage is like this:
public static PlotModel WithXDecimator()
{
var model = new PlotModel { Title = "LineSeries with X Decimator" };
var s1 = CreateSeriesSuitableForDecimation();
s1.Decimator = Decimator.Decimate;
model.Series.Add(s1);
return model;
}
This may solve the problem on my side, and I hope it helps others too. Unfortunately it is not documented in the documentation
For the moment I ended up with calculating the time for calling InvalidatePlot for the next time. I calculate it with the method given in this answer, wich returns the number of visible points. This rededuce the performance issue, but dosent fix the block on the UI Thread on calling InvalidatePlot.

Implementing "tab completion" in RichEdit Winapi

This is a feature you see in a lot of IRC clients. Basically, if you type a string "Ad" and then hit tab the client will fill in the first matching nick (in the case of an IRC client) mathcing 'Ad' - so let's say it fills in Adam. But, like bash, if you keep hitting tab it should cycle through all the names containing "Ad" as a prefix.
I'm not quite sure how to implement this in the Wndproc for a RichEdit though. Specifically, when a user hits tab I need to get the current 'token', save it, and get all the prefixes and fill in the first. If he hits tab again I need to get the next prefix, and so on, but I need to empty the prefix list once I get a WM_CHAR that's not tab -- I think?
I'm wondering if there's some easier, less hacky way though, or if anybody has seen code that does this?
Thanks.
Useful though Remy's comments are, it seems to me that this question is more about what the logic should be to implement kind-of-bash-style auto-completion than anything else. On that basis, and based on what you posted, which I found slightly confusing, I think it should be something like this (pseudo-code);
int autocomplete_index = 0;
string autocomplete_prefix;
on_tab:
if (autocomplete_prefix == "")
{
autocomplete_prefix = current_contents_of_edit_field ();
autocomplete_index = 0;
}
auto autocomplete_result = get_autocomplete_string (autocomplete_prefix, autocomplete_index++);
if (autocomplete_result != "")
replace_contents_of_edit_field_and_move_caret_to_end (autocomplete_result);
else
beep (); // or cycle round
done;
on_any_other_char:
autocomplete_prefix = "";
If the rich edit control is embedded in a dialog, you also need to ensure that the dialog manager does not speak in and snaffle VK_TAB before you do. That normally doesn't happen for rich edit controls (although it does for regular edit controls - go figure) but if it does you can handle WM_GETDLGCODE appropriately in your WndProc (details on request).
And 'hacky'? Why? I don't think so. Sounds like a good idea to me.

F# and winform Controls Question

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).

Floating child window in WPF

I want to create floating child window in .NET 3.0 WPF application.
What I'm doing is:
sideWindow = new SideWindow(this);
sideWindow.Left = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width - sideWindow.Width;
sideWindow.Top = 125;
sideWindow.WindowStartupLocation = WindowStartupLocation.Manual;
sideWindow.Owner = this;
sideWindow.Show();
This is running fine except one customer. According to trace messages the window is created, but the client doesn't see it!
Any idea about similar problems?
Thank you very much.
Have you tried using the WPF native:
System.Windows.SystemParameters.PrimaryScreenWidth
I usually use SystemParameters.VirtualScreenWidth which works better with dual monitors.
I'm surprised this works at all: sideWindow.Width should be NaN (not a number) when you check it, because the layout passes haven't been performed at that point.
You need to call Measure/Arrange methods on sideWindow.

Using TransformToVisual in Silverlight to position HTML elements

I'm in the process of developing a Silverlight custom control that "hosts" a Flash instance. The way that you do this, of course, is to position the HTML element in question over your Silverlight instance, as described, say, here. The problem I'm running into is that when I use the GeneralTransform.Transform() method to get the absolute coordinates of my control, so that I can position the HTML element correctly, the Point object that's returned always has .X=0 and .Y=0.
public void InitControl(string id)
{
GeneralTransform gt = this.TransformToVisual(Application.Current.RootVisual);
Point localPos = gt.Transform(new Point(_htmlControlLeft, _htmlControlTop));
// Create the containing DIV tag.
HtmlDocument doc = HtmlPage.Document;
divHost = doc.CreateElement("div");
divHost.SetAttribute("id", System.Guid.NewGuid().ToString());
divHost.SetStyleAttribute("position", "absolute");
divHost.SetStyleAttribute("left", localPos.X.ToString() + "px"); // always 0
divHost.SetStyleAttribute("top", localPos.Y.ToString() + "px"); // always 0
Debug.WriteLine("x,y=" + localPos.X.ToString() + "," + localPos.Y.ToString());
divHost.SetStyleAttribute("width", Width.ToString() + "px");
divHost.SetStyleAttribute("height", Height.ToString() + "px");
divHost.SetStyleAttribute("z-index", _htmlZIndex.ToString());
}
I haven't been able to find great documentation on the GeneralTransform.Transform() method, but it seems like I'm using it correctly. Any thoughts on what I'm doing wrong?
Edit 4/28/09: I still haven't found an answer, but I AM using the Transform() method properly. The problem only shows up if I call the InitControl() method during the Page.Loaded event. If I wait a few seconds, then call it (say) from a Button_Click event, the same code works fine. According to the SL docs, everything should be laid out appropriately by the time the Page.Loaded event fires, but clearly that's not the case.
I should also note that every once in a while, the code above works perfectly fine, even when it's called from the Page.Loaded event. Huh.
My workaround so far, for what it's worth, is hide the control for a couple seconds after the form loads, then show it. It's an ugly hack, but unless anyone has any better ideas...?
This is due to RootVisual having a 0,0 x/y axis. Try using the next element within the tree, typically it's layoutRoot by default (assuming you haven't changed its name etc). That or utilise FindName() to ensure you have a direct reference to the said layer you are wanting to position the iFrame over.
Scott Barnes / Rich Platforms Product Manager / Microsoft.

Resources