为什么我的ValueConverter没有被触发?

时间:2016-03-19 16:54:18

标签: wpf f#

为什么我的ValueConverter没有被触发?

当我的视图模型的构造函数被执行时,我的值转换器被触发。但是,当我为Cells属性分配新值时,它不会被触发。

我希望这一行能够触发我的价值转换器更新:

this.Cells <- grid |> cycleThroughCells
                   |> Map.toSeq
                   |> Seq.map snd
                   |> Seq.toList

但它没有。

我有以下ViewModel:

type ViewModel() as this =
    inherit ViewModelBase()

    let rowCount = 6
    let mutable grid = rowCount |> createGrid
                                |> setCell { X=3; Y=1; State=Alive }
                                |> setCell { X=3; Y=0; State=Alive }
                                |> setCell { X=4; Y=1; State=Alive }

    let mutable _cells = grid |> Map.toSeq
                              |> Seq.map snd
                              |> Seq.toList
    let cycleHandler _ = 

        this.Cells <- grid |> cycleThroughCells
                           |> Map.toSeq
                           |> Seq.map snd
                           |> Seq.toList
    member this.Play =
        DelegateCommand ((fun _ -> let timer = createTimer 500 cycleHandler
                                   do while true do
                                      do Async.RunSynchronously timer), fun _ -> true) :> ICommand
    member this.Cells
        with get() = _cells 
        and set(value) =
            _cells <- value
            base.NotifyPropertyChanged(<@ this.Cells @>)

我的ViewModelBase如下:

open System.ComponentModel
open Microsoft.FSharp.Quotations.Patterns

type ViewModelBase () =
    let propertyChanged = 
        Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()

    let getPropertyName = function 
        | PropertyGet(_,pi,_) -> pi.Name
        | _ -> invalidOp "Expecting property getter expression"

    interface INotifyPropertyChanged with
        [<CLIEvent>]
        member this.PropertyChanged = propertyChanged.Publish

    member private this.NotifyPropertyChanged propertyName = 
        propertyChanged.Trigger(this,PropertyChangedEventArgs(propertyName))

    member this.NotifyPropertyChanged quotation = 
        quotation |> getPropertyName |> this.NotifyPropertyChanged

我的XAML如下:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Client;assembly=Client"
        Background="Black"
        Title="Game of Life" Height="450" Width="500">

    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>

    <Window.Resources>
        <local:StateToBrushConverter x:Key="StateToBrushConverter" />
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />

            <RowDefinition />
            <RowDefinition />
            <RowDefinition />

            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />

            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Rectangle Grid.Row="0" Grid.Column="0" Fill="{Binding Cells[0], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="0" Grid.Column="1" Fill="{Binding Cells[1], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="0" Grid.Column="2" Fill="{Binding Cells[2], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="0" Grid.Column="3" Fill="{Binding Cells[3], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="0" Grid.Column="4" Fill="{Binding Cells[4], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="0" Grid.Column="5" Fill="{Binding Cells[5], Converter={StaticResource StateToBrushConverter}}" />

        <Rectangle Grid.Row="1" Grid.Column="0" Fill="{Binding Cells[6], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="{Binding Cells[7], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="1" Grid.Column="2" Fill="{Binding Cells[8], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="1" Grid.Column="3" Fill="{Binding Cells[9], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="1" Grid.Column="4" Fill="{Binding Cells[10], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="1" Grid.Column="5" Fill="{Binding Cells[11], Converter={StaticResource StateToBrushConverter}}" />

        <Rectangle Grid.Row="2" Grid.Column="0" Fill="{Binding Cells[12], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="2" Grid.Column="1" Fill="{Binding Cells[13], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="2" Grid.Column="2" Fill="{Binding Cells[14], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="2" Grid.Column="3" Fill="{Binding Cells[15], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="2" Grid.Column="4" Fill="{Binding Cells[16], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="2" Grid.Column="5" Fill="{Binding Cells[17], Converter={StaticResource StateToBrushConverter}}" />

        <Rectangle Grid.Row="3" Grid.Column="0" Fill="{Binding Cells[18], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="3" Grid.Column="1" Fill="{Binding Cells[19], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="3" Grid.Column="2" Fill="{Binding Cells[20], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="3" Grid.Column="3" Fill="{Binding Cells[21], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="3" Grid.Column="4" Fill="{Binding Cells[22], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="3" Grid.Column="5" Fill="{Binding Cells[23], Converter={StaticResource StateToBrushConverter}}" />

        <Rectangle Grid.Row="4" Grid.Column="0" Fill="{Binding Cells[24], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="4" Grid.Column="1" Fill="{Binding Cells[25], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="4" Grid.Column="2" Fill="{Binding Cells[26], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="4" Grid.Column="3" Fill="{Binding Cells[27], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="4" Grid.Column="4" Fill="{Binding Cells[28], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="4" Grid.Column="5" Fill="{Binding Cells[29], Converter={StaticResource StateToBrushConverter}}" />

        <Rectangle Grid.Row="5" Grid.Column="0" Fill="{Binding Cells[30], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="5" Grid.Column="1" Fill="{Binding Cells[31], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="5" Grid.Column="2" Fill="{Binding Cells[32], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="5" Grid.Column="3" Fill="{Binding Cells[33], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="5" Grid.Column="4" Fill="{Binding Cells[34], Converter={StaticResource StateToBrushConverter}}" />
        <Rectangle Grid.Row="5" Grid.Column="5" Fill="{Binding Cells[35], Converter={StaticResource StateToBrushConverter}}" />


        <Button Grid.Row="6" Grid.Column="1" Content="Go!" Command="{Binding Play}" />
    </Grid>

</Window>

我的ValueConverter如下:

type StateToBrushConverter() =
    interface IValueConverter with
        member x.Convert(value, targetType, parameter, culture) = 
            let cell = value :?> Cell
            match cell.State with 
            | Alive -> SolidColorBrush(Colors.LightGreen) :> obj
            | Dead  -> SolidColorBrush(Colors.Black)      :> obj

        member x.ConvertBack(value, targetType, parameter, culture) = failwith "Not implemented yet"

2 个答案:

答案 0 :(得分:4)

当我尝试运行此代码时,我得到了异常。 不确定,这可能是应该的,但是这段代码对我没有错误:

 type MainViewModel() as self = 
    inherit ViewModelBase()   

    let rowCount = 6
    let mutable grid = rowCount |> createGrid
                                |> setCell { Y=2; X=1; State=Alive }
                                |> setCell { Y=2; X=2; State=Alive }
                                |> setCell { Y=3; X=2; State=Alive }
                                |> setCell { Y=3; X=3; State=Alive }

    let mutable _cells = grid |> Map.toSeq
                              |> Seq.map snd
                              |> Seq.toList

    let mutable count = 0
    let mutable isEnabled = true

    let cycleHandler _ = 
        _cells <- grid |> cycleThroughCells
                       |> Map.toSeq
                       |> Seq.map snd
                       |> Seq.toList
        count <- count + 1
        self.NotifyPropertyChanged(<@ self.Cells @>)
        self.NotifyPropertyChanged(<@ self.Count @>)

    let change _ = 
        isEnabled <- false
        self.NotifyPropertyChanged(<@ self.IsEnabled @>)

        async
            {
                while true do
                    do! Async.Sleep 2000
                    cycleHandler()
            }
        |> Async.Start

    member __.Play = DelegateCommand (change, fun _ -> isEnabled) :> ICommand
    member __.N = rowCount
    member __.Cells = _cells
    member __.Count = count
    member __.IsEnabled = isEnabled

我改变了你的Xaml,因为我不认为描述36个矩形是个好主意。 一种选择是使用ListBox:

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Button Grid.Row="0" Content="Go!" Command="{Binding Play}" IsEnabled="{Binding IsEnabled}"  Margin="5"  HorizontalAlignment="Right" />
        <TextBlock Grid.Row="0" Text="{Binding Count}"  Margin="5" HorizontalAlignment="Left"></TextBlock>
        <ListBox Grid.Row="1" Margin="2" ItemsSource="{Binding Cells}" IsEnabled="False"
            HorizontalContentAlignment="Stretch"
            VerticalContentAlignment="Stretch">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="{Binding N}" Columns="{Binding N}"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Rectangle Stretch="Fill"
                               Fill="{Binding Converter={StaticResource StateToBrushConverter}}"></Rectangle>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Padding" Value="0" />
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>

因此,您可以根据rowCount的值创建不同维度的网格。

我添加了一个变量“count”,表示即使图片没有改变,也会以指定的间隔调用该函数。

结果:

enter image description here

答案 1 :(得分:2)

我认为问题在于你绑定到列表中的各个元素,但是为列表本身触发NotifyPropertyChanged。您是否尝试使用ObservableCollection替换列表并使用Clear + AddRange在每个tick上重新填充它?