Visual F# Windows form closing after showing - winforms

Good Day,
I have just started learning visual F#, and it looks surprisingly fun to do. For my first project I got my hands dirty by immediately make a windows form to download info from a page and display it in a RichTextBox on the form. Problem is, once the form shows and information is downloaded, it immediately closes. How do I keep my masterpiece open for viewing? Any advice?
I have 2 Files currently:
Program.fs
Script1.fs
Program.fs is supposed to "create" the form, where Script1.fs is merely the entrypoint for the application.
Program.fs
namespace Program1
open System.Windows.Forms
module public HelloWorld =
let form = new Form(Visible = true, TopMost = true, Text = "Welcome to F#")
let textB = new RichTextBox(Dock = DockStyle.Fill, Text = "Initial Text")
form.Controls.Add textB
open System.IO
open System.Net
/// Get the contents of the URL via a web request
let http (url: string) =
let req = System.Net.WebRequest.Create(url)
let resp = req.GetResponse()
let stream = resp.GetResponseStream()
let reader = new StreamReader(stream)
let html = reader.ReadToEnd()
resp.Close()
html
textB.Text <- http "http://www.google.com"
Script1.fs
open Program1
[<EntryPoint>]
let main argv=
printfn "Running F# App"
HelloWorld.form.Show();
0
I need to reiterate, I started with F#. This is my first application I wrote. How do I keep the form open?

You need to call Application.Run and pass your form object into it. http://msdn.microsoft.com/en-us/library/system.windows.forms.application.run(v=vs.110).aspx
This will create a message loop, and keep your application alive until the form is closed.
open Program1
[<EntryPoint>]
let main argv=
printfn "Running F# App"
System.Windows.Forms.Application.Run(HelloWorld.form)
0

Related

WebBrowser control from Console app using F#

I have a .netcoreapp3.1 Console App
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWpf>true</UseWpf>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
In Program.fs, I am instantiating a WebBrowser control and handling the DocumentCompleted event
let run() =
let uri = "https://www.microsoft.com"
let browser = new WebBrowser()
browser.DocumentCompleted.Add(fun _ -> handlePage browser uris)
browser.Navigate(uri)
[<STAThread>]
[<EntryPoint>]
let main argv =
run()
Console.ReadKey() |> ignore
0
When I run it, the DocumentCompleted event is never fired or handled - the program runs through to the end.
Am I missing?
Thanks in advance.
Am I missing?
The message pump. You need a Dispatcher to run a WPF app.
On another note, you're using the WinForms WebBrowser, that one exposes the DocumentCompleted event. The WPF variant has the LoadCompleted event, ref this post.
In WPF, however, the concept of Loaded is related to the visual tree. As you're not rendering the control, the event will never be raised. If we instead use the Navigated event, we can get there with minimal fuss.
open System
open System.Windows
open System.Windows.Controls
type BrowserApplication() =
inherit Application()
let run() =
let uri = "https://www.microsoft.com"
let browser = new WebBrowser()
browser.Navigated.Add(fun _ -> Console.WriteLine("Done navigating"))
browser.Navigate(uri)
do run();
[<EntryPoint;STAThread>]
let main argv =
BrowserApplication().Run()

WPF native windows 10 toasts

Using .NET WPF and Windows 10, is there a way to push a local toast notification onto the action center using c#? I've only seen people making custom dialogs for that but there must be a way to do it through the os.
You can use a NotifyIcon from System.Windows.Forms namespace like this:
class Test
{
private readonly NotifyIcon _notifyIcon;
public Test()
{
_notifyIcon = new NotifyIcon();
// Extracts your app's icon and uses it as notify icon
_notifyIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
// Hides the icon when the notification is closed
_notifyIcon.BalloonTipClosed += (s, e) => _notifyIcon.Visible = false;
}
public void ShowNotification()
{
_notifyIcon.Visible = true;
// Shows a notification with specified message and title
_notifyIcon.ShowBalloonTip(3000, "Title", "Message", ToolTipIcon.Info);
}
}
This should work since .NET Framework 1.1. Refer to this MSDN page for parameters of ShowBalloonTip.
As I found out, the first parameter of ShowBalloonTip (in my example that would be 3000 milliseconds) is generously ignored. Comments are appreciated ;)
I know this is an old post but I thought this might help someone that stumbles on this as I did when attempting to get Toast Notifications to work on Win 10.
This seems to be good outline to follow -
Send a local toast notification from desktop C# apps
I used that link along with this great blog post- Pop a Toast Notification in WPF using Win 10 APIs
to get my WPF app working on Win10. This is a much better solution vs the "old school" notify icon because you can add buttons to complete specific actions within your toasts even after the notification has entered the action center.
Note- the first link mentions "If you are using WiX" but it's really a requirement. You must create and install your Wix setup project before you Toasts will work. As the appUserModelId for your app needs to be registered first. The second link does not mention this unless you read my comments within it.
TIP- Once your app is installed you can verify the AppUserModelId by running this command on the run line shell:appsfolder . Make sure you are in the details view, next click View , Choose Details and ensure AppUserModeId is checked. Compare your AppUserModelId against other installed apps.
Here's a snipit of code that I used. One thing two note here, I did not install the "Notifications library" mentioned in step 7 of the first link because I prefer to use the raw XML.
private const String APP_ID = "YourCompanyName.YourAppName";
public static void CreateToast()
{
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(
ToastTemplateType.ToastImageAndText02);
// Fill in the text elements
XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
stringElements[0].AppendChild(toastXml.CreateTextNode("This is my title!!!!!!!!!!"));
stringElements[1].AppendChild(toastXml.CreateTextNode("This is my message!!!!!!!!!!!!"));
// Specify the absolute path to an image
string filePath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + #"\Your Path To File\Your Image Name.png";
XmlNodeList imageElements = toastXml.GetElementsByTagName("image");
imageElements[0].Attributes.GetNamedItem("src").NodeValue = filePath;
// Change default audio if desired - ref - https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-audio
XmlElement audio = toastXml.CreateElement("audio");
//audio.SetAttribute("src", "ms-winsoundevent:Notification.Reminder");
//audio.SetAttribute("src", "ms-winsoundevent:Notification.IM");
//audio.SetAttribute("src", "ms-winsoundevent:Notification.Mail"); // sounds like default
//audio.SetAttribute("src", "ms-winsoundevent:Notification.Looping.Call7");
audio.SetAttribute("src", "ms-winsoundevent:Notification.Looping.Call2");
//audio.SetAttribute("loop", "false");
// Add the audio element
toastXml.DocumentElement.AppendChild(audio);
XmlElement actions = toastXml.CreateElement("actions");
toastXml.DocumentElement.AppendChild(actions);
// Create a simple button to display on the toast
XmlElement action = toastXml.CreateElement("action");
actions.AppendChild(action);
action.SetAttribute("content", "Show details");
action.SetAttribute("arguments", "viewdetails");
// Create the toast
ToastNotification toast = new ToastNotification(toastXml);
// Show the toast. Be sure to specify the AppUserModelId
// on your application's shortcut!
ToastNotificationManager.CreateToastNotifier(APP_ID).Show(toast);
}
UPDATE
This seems to be working fine on windows 10
https://msdn.microsoft.com/library/windows/apps/windows.ui.notifications.toastnotificationmanager.aspx
you will need to add these nugets
Install-Package WindowsAPICodePack-Core
Install-Package WindowsAPICodePack-Shell
Add reference to:
C:\Program Files (x86)\Windows Kits\8.1\References\CommonConfiguration\Neutral\Windows.winmd
And
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
And use the following code:
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText04);
// Fill in the text elements
XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
for (int i = 0; i < stringElements.Length; i++)
{
stringElements[i].AppendChild(toastXml.CreateTextNode("Line " + i));
}
// Specify the absolute path to an image
string imagePath = "file:///" + Path.GetFullPath("toastImageAndText.png");
XmlNodeList imageElements = toastXml.GetElementsByTagName("image");
ToastNotification toast = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier("Toast Sample").Show(toast);
The original code can be found here: https://www.michaelcrump.net/pop-toast-notification-in-wpf/
I managed to gain access to the working API for windows 8 and 10 by referencing
Windows.winmd:
C:\Program Files (x86)\Windows Kits\8.0\References\CommonConfiguration\Neutral
This exposes Windows.UI.Notifications.
You can have a look at this post for creating a COM server that is needed in order to have notifications persisted in the AC with Win32 apps https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/.
A working sample can be found at https://github.com/WindowsNotifications/desktop-toasts

Same functionality as HTTPClient in Codename one

I was wondering how I can achieve something like an HTTPClient.
I tried WebBrowser class but it seems that the execution continues even though the URL specified has not yet loaded.
public void testWebBrowser(){
final WebBrowser b = new WebBrowser(){
#Override
public void onLoad(String url) {
BrowserComponent c = (BrowserComponent)this.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
// I want this Javascript context here
}
};
// just a test URL
b.setURL("http://youtube.com");
// Suppose to get the Javascript context here though it executes without waiting for the whole page to load
}
How can I get the JS Context from within a WebBrowser context? Like a synchronous execution
WebBrowser browser = new WebBrowser();
browser.setURL("someURL");
// wait execution till the whole page in "someURL" loads till it executes the next line
BrowserComponent c = (BrowserComponent)browser.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
If I understand correctly you are trying to create a scraping solution?
That's probably not the ideal approach since this will actually create a web browser which you then need to automate with JavaScript. I would suggest you create a webservice that encapsulates the HttpClient functionality and drive it with ConnectionRequest. This way when the web site changes you can just fix your server in a way seamless to your installed base.

How to upload file in silverlight

I want to save (upload) a file in silverlight into the silverlight application folder.
I get the URI of the application
string str3 = App.Current.Host.Source.AbsoluteUri + "/Recording/";
but i don't know how to save file.
I use this code.....
string extension = "wav";
// Create an instance of the open file dialog box.
OpenFileDialog openFileDialog1 = new OpenFileDialog();
// Set filter options and filter index.
openFileDialog1.Filter = String.Format("{1} files (*.{0})|*.{0}|WAV FILES (*.*)|*.*", extension, "Audio");
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = false;
// Call the ShowDialog method to show the dialog box.
bool? userClickedOK = openFileDialog1.ShowDialog();
// Process input if the user clicked OK.
if (userClickedOK == true)
{
string str = App.Current.Host.Source.AbsoluteUri + "/Recording/";
openFileDialog1.File.CopyTo(str);
}
That won't work by itself (I assume your application is hosted on a web server), you need an uploader that will send the content to the server, and a server side handler that will receive and store the file.
Take you pick from one of those Silverlight uploaders:
Silverlight File Uploader
Silverlight File Upload
Silverlight Multi File Uploader
Personally I went with the first one, but I believe they're all quite good.

Silverlight HttpWebRequest.Create hangs inside async block

I am trying to prototype a Rpc Call to a JBOSS webserver from Silverlight (4). I have written the code and it is working in a console application - so I know that Jboss is responding to the web request. Porting it to silverlight 4, is causing issues:
let uri = new Uri(queryUrl)
// this is the line that hangs
let request : HttpWebRequest = downcast WebRequest.Create(uri)
request.Method <- httpMethod;
request.ContentType <- contentType
It may be a sandbox issue, as my silverlight is being served off of my file system and the Uri is a reference to the localhost - though I am not even getting an exception. Thoughts?
Thx
UPDATE 1
I created a new project and ported my code over and now it is working; something must be unstable w/ regard to the F# Silverlight integration still. Still would appreciate thoughts on debugging the "hanging" web create in the old model...
UPDATE 2
let uri = Uri("http://localhost./portal/main?isSecure=IbongAdarnaNiFranciscoBalagtas")
// this WebRequest.Create works fine
let req : HttpWebRequest = downcast WebRequest.Create(uri)
let Login = async {
let uri = new Uri("http://localhost/portal/main?isSecure=IbongAdarnaNiFranciscoBalagtas")
// code hangs on this WebRequest.Create
let request : HttpWebRequest = downcast WebRequest.Create(uri)
return request
}
Login |> Async.RunSynchronously
I must be missing something; the Async block works fine in the console app - is it not allowed in the Silverlight App?
(Thanks for sending this to fsbugs, to force us to take a hard look.)
The problem is Async.RunSynchronously. When called on the UI thread, this blocks the UI thread. And it turns out that WebRequest.Create() on Silverlight dispatches to the UI thread. So it is a deadlock.
In general, try to avoid Async.RunSynchronously on Silverlight (or on any UI thread). You can use Async.StartImmediate in this example. Alternatively, I think you can call RunSynchronously from any background thread without issues. (I have not tried enough end-to-end Silverlight scenarios myself to offer more advice as yet. You might check out
Game programming in F# (with Silverlight and WPF)
F# and Silverlight
F# async on the client side
for a few short examples.)
(In retrospect, the F# design team thinks that we maybe should not have included Async.RunSynchronously in FSharp.Core for Silverlight; the method potentially violates the spirit of the platform (no blocking calls). It's possible we'll deprecate that method in future Silverlight releases. On the other hand, it still does have valid uses for CPU-intensive parallelism on Silverlight, e.g. running a bunch on (non-IO) code in parallel on background threads.)
Seems like a similar issue here - though no reference to silverlight (in fact it is a "windows service class"):
http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/10854fc4-2149-41e2-b315-c533586bb65d
I had similar problem. I was making a Silverlight MVVM ViewModel to bind data from web.
Don Syme commented himself:
I’m not a data-binding expert, but I
believe you can’t “hide” the asyncness
of a view model like this for WPF and
Silverlight. I think you would need to
expose Task, Async or an
observable collection. AFAIK the only
way to get Silverlight and WPF to bind
asynchronously to a property is if it
is an observable collection.
Anyway, I installed F# Power Pack to get AsyncReadToEnd.
It didn't solve the case... I added domains to trusted sites but it didn't help... Then I added a MySolution.Web -asp.net-site and clientaccesspolicy.xml. I don't know if those had any effect.
Now, with Async.StartImmediate I got web service call working:
let mutable callresult = ""
//let event = new Event<string>()
//let eventvalue = event.Publish
let internal fetch (url : Uri) trigger =
let req = WebRequest.CreateHttp url
//req.CookieContainer <- new CookieContainer()
let asynccall =
async{
try
let! res = req.AsyncGetResponse()
use stream = res.GetResponseStream()
use reader = new StreamReader(stream)
let! txt = reader.AsyncReadToEnd()
//event.Trigger(txt)
callresult <- txt //I had some processing here...
trigger "" |> ignore
with
| :? System.Exception as ex ->
failwith(ex.ToString()) //just for debug
}
asynccall |> Async.StartImmediate
Now I will need my ViewModel to listen the mutable callresult.
In your case you need also a crossdomain.xml to the server.
The trigger is needed to use the UI-thread:
let trigger _ =
let update _ = x.myViewModelProperty <- callresult
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(new Action(update)) |> ignore
fetch serviceUrl trigger
I think you are hitting the maximum http connections restriction: see Aynchronous web server calls in Silverlight and maximum HTTP connections and http://weblogs.asp.net/mschwarz/archive/2008/07/21/internet-explorer-8-and-maximum-concurrent-connections.aspx

Resources