How can I use WPF menus and dialog boxes in F#? - wpf

I've been trying to find an example of using XAML and F# - without C# - to set up traditional menus and dialog boxes. Everything I can find online either uses C# or it is old, before the most recent versions of F# and .NET. Can anyone suggest an example I can look at? Thanks.

When you try to learn WPF, you come across many C# examples based on "good old" code behind rather than MVVM or MVC. The following explains how to quickly create an F# WPF code behind application. Using this, it becomes easier to experiment with all those examples.
Create an F# console application.
Change the Output type of the application to Windows Application.
Add FsXaml from NuGet.
Add these four source files, and arrange them in this order.
MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="First Demo" Height="200" Width="300">
<Canvas>
<Button Name="btnTest" Content="Test" Canvas.Left="10" Canvas.Top="10" Height="28" Width="72"/>
</Canvas>
</Window>
MainWindow.xaml.fs
namespace FirstDemo
type MainWindowXaml = FsXaml.XAML<"MainWindow.xaml">
type MainWindow() =
inherit MainWindowXaml()
let whenLoaded _ =
()
let whenClosing _ =
()
let whenClosed _ =
()
let btnTestClick _ =
this.Title <- "Yup, it works!"
()
do
this.Loaded.Add whenLoaded
this.Closing.Add whenClosing
this.Closed.Add whenClosed
this.btnTest.Click.Add btnTestClick
App.xaml
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.fs
namespace FirstDemo
open System
open System.Windows
type App = FsXaml.XAML<"App.xaml">
module Main =
[<STAThread; EntryPoint>]
let main _ =
let app = App()
let mainWindow = new MainWindow()
app.Run(mainWindow) // Returns application's exit code.
Delete the Program.fs file.
Change the Build Action to Resource for the two xaml files.
Add a reference to the .NET assembly UIAutomationTypes.
Compile and run.
You can't use the designer to add event handlers. Simply add them manually in the code behind.
StackOverflow is possibly not the best place to post complete demos like this one, especially if this spins off more questions along the same line. If there is another better place, e.g. a public repo for this kind of thing, please let me know.

Related

How to use ModernWPF Programaticially

ModernWPF is a great project, as it restyles my WPF app in modern UI style. The docs tell what XAML changes to be done to activate the styling. Unfortunately I am working with an application that is not XAML based (Python.NET).
I tried already to add the dictionaries programaticially like:
app = Application()
app.Resources.MergedDictionaries.Add(ModernWpf.ThemeResources())
app.Resources.MergedDictionaries.Add(ModernWpf.Controls.XamlControlsResources())
app.Run(win)
The main window win is created via XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
Title="Enterprisey-FireDrop Express" Width="640" Height="480"
ui:WindowHelper.UseModernWindowStyle="True">
...
</Window>
Any hints?
In C# the following seems to work, but I'm not sure if the same can be done in Python since I don't know Python at all:
var app = new Application();
var themeResources = new ModernWpf.ThemeResources();
((ISupportInitialize)themeResources).BeginInit();
app.Resources.MergedDictionaries.Add(themeResources);
((ISupportInitialize)themeResources).EndInit();
app.Resources.MergedDictionaries.Add(new ModernWpf.Controls.XamlControlsResources());
app.Run(new MainWindow());

Why can't I launch a WPF app written in F#?

I receive the following warning when attempting to build a WPF app written in F#:
Main module of program is empty: nothing will happen when it is run
As a result, I am unable to launch the application.
I have verified that all AssemblyInfo.fs files has a "do()" at the end.
I have played around with the order of files as well.
Any suggestions?
UPDATE
I Added a file to the end of my project.
The file has the following code:
module Bootstrap
open System.Windows
[<EntryPoint>]
let main args =
System.Windows.Application.Current.Run() |> ignore;
// Return 0. This indicates success.
0
When I attempt to run the file though, I hit a null reference exception on Application.Current.
My solution looks like this:
Here's a minimal working example, using FSharp.ViewModule.Core and FsXaml.Wpf packages:
MainWindow.xaml:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:ViewModel;assembly=fsxamltest">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
</Window>
ViewModel.fs:
namespace ViewModel
open FSharp.ViewModule
type MainViewModel() as me = inherit ViewModelBase()
App.fs:
open System
type App = FsXaml.XAML<"App.xaml">
[<STAThread;EntryPoint>]
let main _ = App().Root.Run()
App.xaml:
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"/>
If that doesn't work, please show us the full code or create MCVE.

WPF project within another WPF project. Commands not working

I have a project solution that contains 2 WPF projects. Project B is contained, and called, from within project A.
The issue I'm having, is that the button commands on project B MainWindow are not hitting it's corresponding ViewModel. This doesn't happen when I run Project B as a standalone application (everything's fine).
In both Projects A and B, I'm using Galasofts MVVM light framework, housing my view models in the static ViewModelLocator class.
Any idea's would be greatly appreciated.
Thanks
Edit: For simplicity, I've just included 1 button.
ribbon:RibbonControl xmlns:Designer="clr-namespace:Nouvem.LabelDesigner.View.Designer" x:Class="Nouvem.LabelDesigner.View.Designer.DesignerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:systemMessage="clr-namespace:Nouvem.LabelDesigner.View.SystemMessage"
xmlns:loc="clr-namespace:Nouvem.Shared.Localisation;assembly=Nouvem.Shared"
UseLayoutRounding="True"
mc:Ignorable="d"
d:DesignHeight="1000" d:DesignWidth="1200"
Background="White"
DataContext="{Binding LabelDesigner, Source={StaticResource Locator}}" >
<Grid>
<Button Command="{Binding TestCommand}"/>
</Grid>
</ribbon:RibbonControl>
ViewModel
public RelayCommand TestCommand { get; set; }
public LabelDesignerViewModel()
{
if (this.IsInDesignMode)
{
return;
}
this.TestCommand = new RelayCommand(() =>
{
// test - not hitting when called from another project
});
}
`
if you do something like new ProjectB.MainWindow.Show() in Project A, then all logic in Project B/App.xaml.cs and resources in Project B/App.xaml are ignored.
if you run Project A, then Application.Current is ProjectA.App and ProjectB.App is not instantiated.
there are two solutions:
1. Create Project B as library:
create new shared library called Project B Library, which you will reference from Project A as well as from Project B. Project B will be just an exe container.
2. Start Project B as new Process
use Process.Start() method to start Project B. You can interact with Project B trought WCF using netNamedPipeBinding

windowsformhost cant load a usercontrol from another dll

So I have a dll from another project which contains many useful classes and controls for me (lets call it foo.dll). I'm making an WPF app. I need to use some of them in my app. I created my usercontrol for windows forms and referenced UserControlForMe from foo.dll. It's shown, all good. Now I want to insert my usercontrol into a wpf form. It looks like this:
<UserControl x:Class="FlatRectangular_Profile.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:uc="clr-namespace:FlatRectangular_Profile.UC"
Height="2093" Width="717">
<Grid Name="grid">
<WindowsFormsHost>
<uc:WindowsFormsProfManual ></uc:WindowsFormsProfManual>
</WindowsFormsHost>
</Grid>
</UserControl>
But here I get an error "cant load type UserControlForMe from foo.dll". No info on that error. Again, UserControlForMe loads in WindowsFormsProfManual. All these is going on in one class library. I referenced everything that foo.dll needed.
No idea how what to do next. I also tried to load it in code in usercontrol.loaded event, but it fails too, and shows stacktrace which leads to the constructor of the UserControlForMe.
I guess you'll have to add the assembly to your namespace import to point your application in the right direction:
xmlns:uc="clr-namespace:FlatRectangular_Profile.UC;Assembly=MyDLL"
I found a workaround since I cant get why it is not working. If I load a UserControlForMe from foo.dll directly to the windowsformhost, it works. But if there is a "buffer" dll, it works in this dll, but doesnt open in futher window. Also I add a UserControlForMe programmatically to a windowsformhost.

Overspecified Namespace in UserControl fails WPF Build

I have a very simple user control, and I'm trying to instantiate it in XAML. I find that when I go a bit overzealous with the namespacing, I run into problems with x:Name.
Here is my UserControl:
<UserControl x:Class="UserControlTest.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300">
<Grid>
<Label Name="Label1">Label</Label>
</Grid>
</UserControl>
Here is the code-behind for the UserControl:
Namespace UserControlTest
Partial Public Class UserControl1
End Class
End Namespace
Now, note that I have the root namespace of my VB.Net project set to "UserControlTest". Knowing that, have a look at my main window:
Here is my main window:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:control="clr-namespace:UserControlTest.UserControlTest"
Title="Window1" Height="300" Width="300">
<Grid>
<control:UserControl1 />
</Grid>
</Window>
See how the control alias needs to have "UserControlTest.UserControlTest"? That's because I have the root namespace of my project set to UserControlTest, and I have defined the namespace of the UserControl to be UserControlTest, also. If I don't use a namespace for the UserControl, I don't have any troubles.
However, because I have done this, the build fails should I try to apply an x:Name to the UserControl, as follows:
<control:UserControl1 x:Name="test"/>
That will fail the build, with this error:
Type 'UserControlTest.UserControlTest.UserControl1' is not defined.
Can anybody explain why? Do I need to avoid putting my UserControls into namespaces just so I can give them x:Name values? I'd like to manipulate my UserControls from code-behind, and without an x:Name, I'm up the creek. But I don't want to sacrifice namespace usage just to get it!
Thanks very much.
I had the same problem (after rebuilding the project, first it worked fine...). I put UserControl into separate namespace.
What is the namespace defined as in the code-behind of your user control?
If your project was called Foo and you had a folder called Controls inside that project, any new user control added to that folder would be given the namespace Foo.Controls.
Then in your XAML you can reference it like so:
xmlns:Controls="clr-namespace:Foo.Controls"
...
<Controls:UserControl1 x:Name="uc1"/>
It seems like you have a naming issue.
EDIT:
Here's how I'm doing it in a project of mine.
StatusBar.xaml.cs
namespace Client.Controls.UserControls
{
public partial class StatusBar : UserControl
{
...
}
}
StatusBar.xaml
<UserControl x:Class="Client.Controls.UserControls.StatusBar">
</UserControl>
Main.xaml.cs
using Client.Controls.UserControls;
namespace Client
{
public partial class Main : Window
{
...
}
}
Main.xaml
<Window x:Class="Client.Main"
xmlns:UserControls="clr-namespace:Client.Controls.UserControls">
<UserControls:StatusBar x:Name="mainStatusBar" />
</Window>
I encountered the same problem in a vb.net project, and despite trying the suggestions here and elsewhere, could not solve the issue. In fact, I was able to take the exact same code out of our project to a new project, and it worked just fine (as far as I could determine all the configuration in the new project was identical). In the solution provided by David, I notice he is using c# - I am wondering if this is some weirdness associated with vb.net.
In the end, the user control I needed to use was quite simple and I implemented it as a resource ControlTemplate to get around the issue. Sorry I don't have a better answer, I am really not happy wih my findings...

Resources