我是F#的新手,我想在现实世界中建模具有相当复杂的"有一个"关系的东西.在层次结构的顶部有四种类型,A - D,具有以下关系:
A
|
+--A
|
+--B
| |
| +--B
| |
| +--D
| |
| +--D
|
+--C
| |
: +--D
| |
| +--D
:
Run Code Online (Sandbox Code Playgroud)
因此,类型B可以具有A或B的"父",而类型D可以具有B,C或D的父级.
我想使用有区别的联合来约束每种类型的父级,因此不能将它们分配给无效的父级,例如:
type B_Parent = A | B
type D_Parent = B | C | D
Run Code Online (Sandbox Code Playgroud)
然后我想使用记录来模拟每个类型,其中一个字段是父类,例如:
type A = { parent:A_Parent; ... }
type B = { parent:B_Parent; ... }
type C = { parent:C_Parent; ... }
type D = { parent:D_Parent; ... }
Run Code Online (Sandbox Code Playgroud)
C_Parent不是问题,因为它的父类型A是事先声明的.我为A_Parent使用了'A选项'.但我还没有弄清楚如何定义B_Parent和D_Parent,因为它们对自己和其他类型的嵌套依赖?
我正在尝试将WPF与F#一起使用.我使用F#Empty Windows App模板创建了一个项目(实际上,我使用不同的设置创建了几个).然后我添加了FSharp.ViewModule和FsXaml.Wpf引用.当项目只有MainWindow.xaml和MainWindow.xaml.fs时,它工作正常.但是,只要我添加了另一个.xaml View文件和.fs ViewModel文件,我就会在.fs文件顶部的"namespace ViewModels"声明中收到以下错误:
The type provider '...\packages\FsXaml.Wpf.2.1.0\lib\net45\FsXaml.Wpf.TypeProvider.dll' reported an error: Assembly attribute 'TypeProviderAssemblyAttribute' refers to a designer assembly 'FsXaml.Wpf.TypeProvider' which cannot be loaded or doesn't exist. Could not load file or assembly 'file:///...\packages\FsXaml.Wpf.2.1.0\lib\net45\FsXaml.Wpf.TypeProvider.dll' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)
Run Code Online (Sandbox Code Playgroud)
在帖子"FsXaml加载错误"(我无法添加评论,因为我没有足够的声誉)中描述了类似的问题.但我已经尝试了那里给出的答案 - 更改CPU和F#运行时 - 并且两者都不起作用.
我在Windows 7 SP1 64位虚拟机中使用Visual Studio 2015社区版.我尝试使用以下框架和DLL版本:
编辑1:在回复下面的里德评论时,这是我的两个显示错误的代码文件.首先,MainWindow.xaml.fs:
namespace ViewModels
open FSharp.ViewModule
open FsXaml
type MainView = XAML<"MainWindow.xaml", true> …Run Code Online (Sandbox Code Playgroud) 在我之前的问题“使用 FSharp.ViewModule 启用对话框确定按钮”中,我达到了仅当对话框字段的验证器为真并且 ViewModule 的 IsValid 属性为真时才启用对话框的确定按钮的地步。但在那之后我又遇到了几个问题:
1) 单击确定按钮并没有关闭对话框,即使我IsDefault="true"在 XAML 中设置。
2) 单击确定按钮时,有时我想做比 ViewModule 验证器提供的检查更多的检查(例如,检查电子邮件地址)。然后,如果此自定义验证失败,我想阻止对话框关闭。
但是我不知道在使用 F# 和 MVVM 时如何做到这两点。首先,我尝试将 XAML 放入 C# 项目和 F# 库中的视图模型代码。然后我在后面的代码中使用了 OK 按钮的 Click 处理程序来关闭窗口。这修复了 1),但不是 2)。
所以这是我的 XAML:
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="OK" IsEnabled="{Binding IsValid}" IsDefault="true" Command="{Binding OkCommand}"
<!--Click="OnOK"--> />
Run Code Online (Sandbox Code Playgroud)
和我的视图模型 - 在validate函数中带有注释以显示单击“确定”按钮时我想要执行的操作:
let name = self.Factory.Backing( <@ self.Name @>, "", notNullOrWhitespace)
let email = self.Factory.Backing( <@ self.Email @>, "", notNullOrWhitespace)
let dialogResult = …Run Code Online (Sandbox Code Playgroud) 在我之前的问题中,我询问如何使用WPF和MVVM创建一个可关闭的对话框,用于在F#中添加新的Person记录.现在我的下一步是创建另一个对话框来编辑这些记录.但我还没有弄清楚如何将现有记录传递给ViewModel并使用它来填充对话框的字段.我得到异常,因为F#记录是不可变的,而ViewModel似乎期望一个可变对象.
我将向您展示我现有的Add对话框的代码 - 假设Edit对话框看起来一样.
这是Person记录:
type Person = { Name: string; Email: string }
Run Code Online (Sandbox Code Playgroud)
这是Add对话框的XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fsxaml="http://github.com/fsprojects/FsXaml"
xmlns:local="clr-namespace:ViewModels;assembly=Test3"
local:DialogCloser.DialogResult="{Binding DialogResult}"
Title="Add Person" Height="150" Width="210" ResizeMode="NoResize" >
<Window.DataContext>
<local:PersonAddVM />
</Window.DataContext>
<StackPanel>
<Grid FocusManager.FocusedElement="{Binding ElementName=_name}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<Label Content="_Name" Target="_name" Grid.Row="0" Grid.Column="0" Margin="2" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" x:Name="_name"
Grid.Row="0" Grid.Column="1" Margin="4" />
<Label Content="_Email" Target="_email" Grid.Row="2" Grid.Column="0" Margin="2" HorizontalAlignment="Left" />
<TextBox Text="{Binding …Run Code Online (Sandbox Code Playgroud)