wpf - 在 F# 中访问 XAML (WPF) 元素

标签 wpf xaml f# reactive-programming observable

我正在尝试使用 WPF 在 F# 中创建响应式应用程序,但在从代码访问 XAML 元素时遇到了一些问题。

在 XAML 文件中,我创建了一个网格,每个网格包含 16 列和行:

        <StackPanel Name="cellStackPanel">
        <Grid Name="cellGrid" Height="500" Width="500" Margin="10,10,10,10" Background="#CCCCCC">
            <Grid.Resources>
                <Style TargetType="ToggleButton">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ToggleButton}">
                                <Border x:Name="border" Background="#FFFFFFFF" Margin="1,1,1,1">
                                    <ContentPresenter x:Name="contentPresenter"/>
                                </Border>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsChecked" Value="true">
                                        <Setter Property="Background" TargetName="border" Value="Black"/>
                                    </Trigger>
                                    <Trigger Property="Control.IsMouseOver"  Value="true">
                                        <Setter Property="Background" TargetName="border" Value="#CCCCCC"/>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Grid.Resources>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
        </Grid>
    </StackPanel>

在 F# 中,我遍历网格并以编程方式将每个单元格初始化为 ToggleButtons:

    let initCell (x,y) (grid : Grid) =
        let cell = ToggleButton()
        Grid.SetColumn(cell, x)
        Grid.SetRow(cell, y)
        ignore (grid.Children.Add(cell))

现在我想创建一个可观察对象(在递归异步循环中等待)来单击我的任何 ToggleButtons。我可以在代码中访问网格元素本身,但我的问题是我不知道如何访问我以编程方式创建的子元素。我正在考虑一个也许是基本的解决方案,即从整个网格捕获单击事件,同时获取鼠标坐标来计算单击了哪个单元格。但这可能不是一个好方法。 我希望我的问题是可以理解的,否则,请告诉我。

最佳答案

正如我在评论中所说,使用 FsXAML + FSharp.ViewModule 更容易。

我不太明白你想要什么,所以一个简单的例子:

 <Grid>
        <ListBox 
            ItemsSource="{Binding Cells}" 
            HorizontalContentAlignment="Stretch"
            VerticalContentAlignment="Stretch">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="{Binding N}" Columns="{Binding N}"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <ToggleButton Content="{Binding Text}"
                                  IsChecked="{Binding IsChecked}"
                                  Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}},
                                                    Path=DataContext.ClickCommand}"
                                  CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=DataContext}"
                                  >

                        <ToggleButton.Style>
                            <Style TargetType="ToggleButton">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type ToggleButton}">
                                            <Border x:Name="border" Background="Wheat" Margin="0">
                                                <ContentPresenter x:Name="contentPresenter"/>
                                            </Border>
                                            <ControlTemplate.Triggers>
                                                <Trigger Property="IsChecked" Value="true">
                                                    <Setter Property="Background" TargetName="border" Value="DarkGreen"/>
                                                </Trigger>
                                                <Trigger Property="Control.IsMouseOver"  Value="true">
                                                    <Setter Property="Background" TargetName="border" Value="#CCCCCC"/>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ToggleButton.Style>
                    </ToggleButton>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Padding" Value="0" />
                    <Setter Property="Focusable" Value="False"/>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>

您可以使用 ItemsControl 代替 ListBox,但配置它需要一些时间 =)

.fs:

type State = {mutable IsChecked:bool; Text:string}

type MainViewModel() as self = 
    inherit ViewModelBase()   

    let n = 16
    let data = [1..n*n] |> List.map(fun i -> {IsChecked = false; Text = string i})

    let click (state:obj) = 
        let st = (state :?> State)
        MessageBox.Show(sprintf "%s %b" st.Text st.IsChecked ) |> ignore

    let clickcommand =  self.Factory.CommandSyncParam(click)

    member __.ClickCommand = clickcommand
    member __.Cells = data
    member __.N = n

Picture

因此,您可以在 click 函数中对对象执行任何操作。

附注我允许自己稍微改变一下你的ToggleButton样式以使图片更清晰。

关于wpf - 在 F# 中访问 XAML (WPF) 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37024904/

相关文章:

c# - XAML View /控件的 GUI 测试框架

user-interface - F# System.TypeInitializationException...为什么?

f# - F# 中 NumericLiteralN 的含义是什么?

c# - 即使实现了 INotifyProperty 接口(interface),ObservableCollection 也不会刷新

wpf - 单击空白处时如何处理事件?

c# - 如何在xaml/c#中旋转图像

c# - 多次显示和隐藏带有幻灯片动画的 WPF 窗口,同时每次都一致地显示动画

f# - 通过类型分析的功能程序示例 'writing themselves'

wpf - 在 ContentPresenter 中设置自动生成的 Textblock 样式

.net - RichTextbox SelectionStart 返回错误的索引