-
意図
- Elmish.WPFを使用する
- C#を使わずF#だけで完結させる
- XAMLを埋め込みリソースとして保持して実行時にロードする
- query式で出力を加工し、オブジェクトのプロパティをそのままカラムとして使用する
-
備忘
- XAMLまったく分からない
- XAMLのデータバインディングを忘れても警告も何も出ないので注意する
- F#のオブジェクトのリスト(のようなもの全般)はqueryのおかげで実質テーブル
-
dotnet publish -c Release
でシングルバイナリ(160MB)を出力するようにfsprojを設定してある- SelfContained=falseにすればフレームワーク依存の実行ファイルができる(4MB)
wpf3.fsproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<UseWpf>true</UseWpf>
<OutputType>WinExe</OutputType>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Elmish.WPF" Version="3.5.8" />
</ItemGroup>
<ItemGroup>
<Resource Include="MainWindow.xaml" />
<Compile Include="Library.fs" />
</ItemGroup>
</Project>
Library.fs
module MainWindow
module hoge =
open System.IO
let hoge1 () = // グリッドビューに表示したいオブジェクトのseqを得る関数
let desktop =
System.Environment.GetFolderPath System.Environment.SpecialFolder.DesktopDirectory
let files =
System.IO.Directory.EnumerateFiles(desktop, "*.txt", EnumerationOptions(RecurseSubdirectories = false))
|> Seq.map (fun path -> FileInfo path)
query {
for file in files do
select
{| name = file.Name
size = file.Length |}
}
|> Seq.cast<obj>
module gui =
open Elmish.WPF
type Model = { GridItemList: obj seq }
type Msg =
| Load
| Reset
let init = { GridItemList = [] }
let canReset = (<>) init
let update msg m =
match msg with
| Load -> { m with GridItemList = hoge.hoge1 () }
| Reset -> init
let bindings () : Elmish.WPF.Binding<Model, Msg> list =
[ "Load" |> Binding.cmd Load
"Reset" |> Binding.cmdIf (Reset, canReset)
"GridItemList" |> Binding.oneWay (fun m -> m.GridItemList) ]
let designVm = Elmish.WPF.ViewModel.designInstance init (bindings ())
let main window =
Elmish.WPF.Program.mkSimpleWpf (fun () -> init) update bindings
|> Program.runWindow window
open System.Windows.Markup
open System.Windows.Controls
open System.Windows
[<EntryPoint; System.STAThreadAttribute>]
let entryPoint args =
let uri = new System.Uri(@"/MainWindow.xaml", System.UriKind.Relative)
let stream = Application.GetResourceStream(uri).Stream
let window = XamlReader.Load(stream) :?> System.Windows.Window
gui.main window
MainWindow.xaml
<Window x:Class="System.Windows.Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Elmish.WPF.Samples.SingleCounter;assembly=SingleCounter.Core"
Title="Sample"
Height="200"
Width="500"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d"
d:DataContext="{x:Static vm:Program.designVm}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Button Width="80" Margin="5,5,10,5" Content="Load" Command="{Binding Load}" />
<Button Width="80" Margin="0,5,10,5" Content="Reset" Command="{Binding Reset}" />
</StackPanel>
<DataGrid Grid.Row="1" x:Name="DataGrid1"
Width="Auto" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding GridItemList, Mode=OneWay}"
AutoGenerateColumns="true"
IsReadOnly="true" >
</DataGrid>
</Grid>
</Window>