简体   繁体   中英

Why isn't my ValueConverter being triggered?

Why isn't my ValueConverter being triggered?

My value-converter is triggered when the constructor of my view-model is executed. However, it doesn't get triggered when I assign a new value to the Cells property.

I expect this line to trigger my value-converter update:

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

But it doesn't.

I have the following 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 @>)

My ViewModelBase is the following:

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

My XAML is the following:

<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>

My ValueConverter is the following:

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"

When I try to run this code I got the exception. Not sure, this may be due, but this code works for me without errors:

 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

I changed your Xaml, because I don't think to describe 36 rectangles is a good idea. One option is to use a 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>

Thus you can create grid of different dimensions depending on the value of rowCount.

I added a variable "count" to show that the function is called with the specified interval, even though the picture does not change.

Result:

在此处输入图片说明

I believe the problem lies in the fact that you're binding to individual elements of the list, but firing the NotifyPropertyChanged for the list itself. Have you tried replacing the list with an ObservableCollection and using Clear + AddRange to repopulate it on each tick?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM