访问F#中的XAML(WPF)元素
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了访问F#中的XAML(WPF)元素相关的知识,希望对你有一定的参考价值。
我正在尝试使用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))
现在我想创建一个observable(等待一个递归的异步循环)来点击我的任何ToggleButtons。我可以在代码中访问网格元素,但我的问题是我不知道如何访问我以编程方式创建的子元素。我正在考虑一个可能是基本的解决方案,即从整个网格中捕获click事件,同时获取鼠标坐标以计算单击了哪个单元格。但这可能不是一个好方法。我希望我的问题是可以理解的,否则,请告诉我。
正如我在评论中所说的更容易使用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
因此,您可以在函数click
中对您的对象执行任何操作。
附:我允许自己改变你的ToggleButton样式,使图片更清晰。
作为一个答案,我不是第三方解决方案的忠实粉丝。如果我理解您的问题,那么您正在尝试访问XAML中指定的元素。这可以通过将XAML转换为字符串类型然后使用以下内容轻松完成:
let xamlBytes = Encoding.UTF8.GetBytes(xamlString)
let xamlStream = new MemoryStream(xamlBytes)
let xamlRoot = XamlReader.Load(xamlStream)
let rootElement = xamlRoot :?> FrameworkElement
let elementYouNeed = rootElement.FindName("nameOfElementYouNeed") :?> TypeOfElementYouNeed
你的情况可能略有不同,但这是一般的想法。
以上是关于访问F#中的XAML(WPF)元素的主要内容,如果未能解决你的问题,请参考以下文章
如何通过XAML,WPF中的数据绑定设置VisualState INITIALIZATION
WPF - 从 XAML 中的 StringFormat 绑定属性访问属性值