本文来安利大家一个有趣而且强大的库,通过F#和C#混合编程编写WPF应用,可以在WPF中使用到F#强大的数据处理能力
在阅读本文之前,我期望大家已了解部分F#的知识。在学会C#基础知识之下,了解F#基础知识是很快的,而本文也仅仅只是用到很少的知识。大家都知道,使用F#能带来很好的数据处理能力,从F#语法层面带来的编写效率提升和编写逻辑的优化,而F#本身是没有带GUI可视化应用编程的。但是F#是在dotnet体系下的,天然就可以用上dotnet系的技术,当然就包括WPF了。因此上一句话说F#本身是没有带GUI可视化应用编程是完全错误的,因为F#可以非常方便调起WPF和WinForms等成熟的UI框架作为自身的可视化应用框架
通过Elmish.WPF库,将可以让开发更顺,以下是一个简单的例子。这个例子的代码完全放在Elmish.WPF库里。
咱依然通过VS创建一个空白的C#空白WPF程序,在此例子里面,几乎没有C#多少的戏份,只是为了使用C#更好的驱动WPF程序而已,因为部分初始化方法和类型等在F#写起来的代码量可不少。本文的主角将交给XAML和F#这两个语言
打开App.xaml.cs文件,咱将修改本文仅有的一点C#代码
public partial class App:Application
{
public App()
{
this.Activated+=StartElmish;
}
private void StartElmish(object sender,EventArgs e)
{
this.Activated-=StartElmish;
Program.main(MainWindow);
}
}
以上代码的Program类是在F#项目里面编写的,也就是说C#语言只是胶水,用来做初始化的连接
接下来咱来创建一个简单的界面,这个界面如下
基本功能是点击加号和减号分别实现当前数值的加减
大概的XAML代码如下
<StackPanel Orientation="Horizontal"HorizontalAlignment="Center"VerticalAlignment="Top"Margin="0,25,0,0">
<TextBlock Text="{Binding CounterValue,StringFormat='Counter value:{0}'}"Width="110"Margin="0,5,10,5"/>
<Button Command="{Binding Decrement}"Content="-"Margin="0,5,10,5"Width="30"/>
<Button Command="{Binding Increment}"Content="+"Margin="0,5,10,5"Width="30"/>
<TextBlock Text="{Binding StepSize,StringFormat='Step size:{0}'}"Width="70"Margin="0,5,10,5"/>
<Slider Value="{Binding StepSize}"TickFrequency="1"Maximum="10"Minimum="1"IsSnapToTickEnabled="True"Width="100"Margin="0,5,10,5"/>
<Button Command="{Binding Reset}"Content="Reset"Margin="0,5,10,5"Width="50"/>
</StackPanel>
在XAML中定义了命令和值的绑定,没有做实际的实现
接下来创建一个F#项目,将这个项目被刚才创建的WPF项目所引用,将WPF项目作为启动项目。运行的顺序是先启动WPF项目,进入App类里面,在App的Activated事件,调用F#项目的逻辑,然后接下来就交给F#来进行数据绑定,在F#项目中,将给MainWindow附加自身作为DataContext数据用于在XAML绑定
在F#的Program.fs文件里面添加如下代码
module Elmish.WPF.Samples.SingleCounter.Program
open Serilog
open Serilog.Extensions.Logging
open Elmish.WPF
type Model=
{Count:int
StepSize:int}
type Msg=
|Increment
|Decrement
|SetStepSize of int
|Reset
let init=
{Count=0
StepSize=1}
let canReset=(<>)init
let update msg m=
match msg with
|Increment->{m with Count=m.Count+m.StepSize}
|Decrement->{m with Count=m.Count-m.StepSize}
|SetStepSize x->{m with StepSize=x}
|Reset->init
let bindings():Binding<Model,Msg>list=[
"CounterValue"|>Binding.oneWay(fun m->m.Count)
"Increment"|>Binding.cmd Increment
"Decrement"|>Binding.cmd Decrement
"StepSize"|>Binding.twoWay(
(fun m->float m.StepSize),
int>>SetStepSize)
"Reset"|>Binding.cmdIf(Reset,canReset)
]
let designVm=ViewModel.designInstance init(bindings())
let main window=
let logger=
LoggerConfiguration()
.MinimumLevel.Override("Elmish.WPF.Update",Events.LogEventLevel.Verbose)
.MinimumLevel.Override("Elmish.WPF.Bindings",Events.LogEventLevel.Verbose)
.MinimumLevel.Override("Elmish.WPF.Performance",Events.LogEventLevel.Verbose)
.WriteTo.Console()
.CreateLogger()
WpfProgram.mkSimple(fun()->init)update bindings
|>WpfProgram.withLogger(new SerilogLoggerFactory(logger))
|>WpfProgram.startElmishLoop window
十分简单的逻辑,在F#定义的命令等将可以通过Elmish.WPF库和WPF的XAML绑定,相当于XAML提供界面逻辑,而F#提供数据驱动的处理逻辑。各个语言的职责是XAML负责界面,而F#负责后台逻辑。将XAML和F#连接起来以及应用程序的启动是C#语言。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69997029/viewspace-2773551/ ,如需转载,请注明出处,否则将追究法律责任。