如何将数据绑定到XAML中的union case值?

时间:2016-01-24 21:43:34

标签: wpf xaml f#

我如何将数据绑定到XAML中的联合案例值?

目前,数据绑定值指向String20对象,而不是union情例(即String20)所包含的实际字符串值。

示例:

<ListView ItemsSource="{Binding Modules}" DisplayMemberPath="Author.First" />

作者定义:

type Name = { 
    First:String20
    Last:String20
    Suffix:String20 option 
}

type Module = { 
    Author:Name
    Duration:Duration 
}

XAML:

<Window x:Class="Client.MainWindow"
        ...
        xmlns:manageModules="clr-namespace:ManageModules;assembly=ManageModules">

    <Window.DataContext>
        <manageModules:CreationViewModel />
    </Window.DataContext>

    <Grid>
        <ListView ItemsSource="{Binding Modules}" DisplayMemberPath="Author.First" 
                  TextElement.Foreground="LightBlue" Background="Black" />
    </Grid>
</Window>

视图模型:

namespace ManageModules
    ...
    type CreationViewModel() =
        inherit ViewModelBase()
        let name =      { First=String20("Scott"); Last=String20("Nimrod"); Suffix=None }
        let duration =  { Hours=1; Minutes=30; Seconds=0 }
        let moduleItem = { Author=name; Duration=duration }

        let mutable (_modules:Module ObservableCollection) = ObservableCollection()

        do _modules.Add(moduleItem)

        member this.Modules
            with get()      = _modules
            and set(value)  = _modules <- value

        member this.Add moduleItem = 
            _modules.Add(moduleItem)

业务范围:

module ManageModule.Entities

type Duration = { 
    Hours:int
    Minutes:int
    Seconds:int 
}

type String20 = String20 of string

type Name = { 
    First:String20
    Last:String20
    Suffix:String20 option 
}

type Module = { 
    Author:Name
    Duration:Duration 
}

and Section = 
    | Introduction of Module
    | Conclusion of Module
    | Content of Module list

2 个答案:

答案 0 :(得分:6)

我的建议是修改String20,使其包含一个普通string值的附加成员 - 然后您可以绑定到普通的string成员。

要将成员添加到类型,您可以写:

type String20 = 
  | String20 of string
  member this.Value = 
    let (String20(str)) = this
    str

然后你应该写:

<ListView ItemsSource="{Binding Modules}" DisplayMemberPath="Author.First.Value" />

可能有更复杂的方法来解决这个问题 - 我猜WPF有一些机制来指定在幕后绑定中发生的转换 - 但添加Value作为成员可能是一个很好的简单开始。

答案 1 :(得分:0)

另一种方法是使用Tomas暗指的ValueConverter。

这个例子是用C#编写的。后来,当我找到更多时间时,我将使用F#演示相同的方法。

我更喜欢在域逻辑中保持简洁性,而不是让UI或数据存储框架规定(即将人质)域逻辑。

因此,我使用C#提供了以下值转换器:

using System;
using System.Globalization;
using System.Windows.Data;
using static ManageModule.Entities;

namespace Client.Converters
{
    public class String20Converter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var unionCase = value as String20;
            return unionCase.Item;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

更新的XAML如下所示:

<Window x:Class="Client.MainWindow"
        ...
        xmlns:local="clr-namespace:Client"
        xmlns:manageModules="clr-namespace:ManageModules;assembly=ManageModules"
        xmlns:converters="clr-namespace:Client.Converters"
        mc:Ignorable="d"
        Background="Black"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <manageModules:CreationViewModel />
    </Window.DataContext>

    <Window.Resources>
        <converters:String20Converter x:Key="String20Converter" />
    </Window.Resources>

    <Grid>
        <ListView ItemsSource="{Binding Modules}" Background="Black">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Author.First, Converter={StaticResource String20Converter}}"
                               Foreground="LightBlue" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Window>

请注意我们如何保留原始域逻辑:

type Duration = { 
    Hours:int
    Minutes:int
    Seconds:int 
}

type String20 = String20 of string

type Name = { 
    First:String20
    Last:String20
    Suffix:String2
}