我正在学习使用 Elmish.WPF。下面是带有 UserControl ContactDetailsView 的简单示例的选项卡控件的典型 XAML 代码:
XMAL:
<Window x:Class="FrontOfficeV.MainWindow"
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"
xmlns:local="clr-namespace:FrontOfficeV"
xmlns:vm="clr-namespace:Models;assembly=Models"
xmlns:vi="clr-namespace:Views;assembly=Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TabControl Grid.Row="1" ItemsSource="{Binding Details}">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</TabControl.ItemContainerStyle>
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:ContactDetail}">
<vi:ContactDetailsView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Internet}">
<vi:InternetView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:PhoneNumber}">
<vi:PhoneNumbersView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Address}">
<vi:AddressesView/>
</DataTemplate>
</TabControl.Resources>
</TabControl>
</Grid>
</Window>
<UserControl x:Class="Views.ContactDetailsView"
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"
xmlns:local="clr-namespace:Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="{Binding Content}" />
</Grid>
</UserControl>
这是我用来向 xaml 提供视图的 F# 代码:
namespace Models
open Elmish.WPF
open Elmish
open System
open System.Windows
type ContactDetail = { Name: string; Content: string; Text: string }
type Internet = { Name: string; Content: string; Text: string }
type PhoneNumber = { Name: string; Content: string; Text: string }
type Address = { Name: string; Content: string; Text: string }
module FrontOffice =
type Details =
| ContactDetail of ContactDetail * Id: Guid
| Internet of Internet * Id: Guid
| PhoneNumber of PhoneNumber * Id: Guid
| Address of Address * Id: Guid
member this.id =
match this with
| ContactDetail(_, id)
| Internet(_, id)
| PhoneNumber(_, id)
| Address(_, id) -> id
member this.name =
match this with
| ContactDetail(cd,_) -> cd.Name
| Internet(i,_) -> i.Name
| PhoneNumber(pn,_) -> pn.Name
| Address(ad,_) -> ad.Name
member this.content =
match this with
| ContactDetail(cd,_) -> cd.Content
| Internet(i,_) -> i.Content
| PhoneNumber(pn,_) -> pn.Content
| Address(ad,_) -> ad.Content
let contactDetail : ContactDetail = { Name="Contact Detail"; Content="Content for Contact Detail"; Text="here is the contact detail text" }
let internet : Internet = { Name="Internet"; Content="Content for Internet"; Text="here is the internet text" }
let phoneNumber : PhoneNumber = {Name="Phone Number"; Content="Content for phone number"; Text="here is the phone number text" }
let address : Address = { Name="Address"; Content="Content for Address"; Text="here is the Address text" }
let details = [ContactDetail (contactDetail,Guid.NewGuid())
Internet (internet,Guid.NewGuid())
PhoneNumber (phoneNumber,Guid.NewGuid())
Address (address,Guid.NewGuid())
]
/// This is the main data model for our application
type Model = {
ClickCount: int
Message: string
Details: Details list
}
/// This is used to define the initial state of our application. It can take any arguments, but we'll just use unit. We'll need the Cmd type.
/// Notice that we return a tuple. The first field of the tuple tells the program the initial state. The second field holds the command to issue.
/// This is the standard Elmish init() (not special to Elmish.WPF).
let init() =
{
ClickCount = 0
Message = "Hello Elmish.WPF"
Details = details
}
/// This is a discriminated union of the available messages from the user interface
type Msg =
| ButtonClicked
| Reset
/// This is the Reducer Elmish.WPF calls to generate a new model based on a message and an old model.
/// The update function will receive the change required by Msg, and the current state. It will produce a new state and potentially new command(s).
let update (msg: Msg) (model: Model) =
match msg with
| ButtonClicked -> {model with ClickCount = model.ClickCount + 1}
| Reset -> init()
/// Elmish.WPF uses this to provide the data context for your view based on a model.
/// The bindings is the view for Elmish.WPF
/// Define the “view” function using the Bindings module. This is the central public API of Elmish.WPF. Normally in Elm/Elmish this
/// function is called view and would take a model and a dispatch function (to dispatch new messages to the update loop) and return
/// the UI (e.g. a HTML DOM to be rendered), but in Elmish.WPF this function is in general only run once and simply sets up bindings
/// that XAML-defined views can use. Therefore, it is called bindings instead of view.
let bindings(): Binding<Model, Msg> list =
[
// One-Way Bindings
"ClickCount" |> Binding.oneWay (fun m -> m.ClickCount)
"Message" |> Binding.oneWay (fun m -> m.Message)
"Details" |> Binding.subModelSeq((fun m -> m.Details), (fun detail -> detail.id), fun () ->
[
"Id" |> Binding.oneWay (fun (_, detail) -> detail.id)
"Name" |> Binding.oneWay (fun (_, detail) -> detail.name)
"Content" |> Binding.oneWay (fun (_,detail) -> detail.content)
])
// Commands
"ClickCommand" |> Binding.cmd ButtonClicked
"ResetCommand" |> Binding.cmd Reset
]
/// This is the application's entry point. It hands things off to Elmish.WPF
let entryPoint (mainWindow: Window) =
Program.mkSimpleWpf init update bindings
|> Program.runWindowWithConfig
{ ElmConfig.Default with LogTrace = true; Measure = true; MeasureLimitMs = 1 }
mainWindow
一切都编译并运行!
但是,在检查选项卡内容时,WPF 会报告:
Elmish.WPF.ViewModel'2[System.Object.System.Object]
所以我相信 XAML 中的 DataTemplate 无法匹配来自 Elmish.WPF 的 DataType。如何解决这个问题?我错过了什么?(我猜我需要以某种方式将 Elmish.WPF.ViewModel'2[System.Object.System.Object] 设置为适当的 DataType ???)
TIA