2016-05-04 2 views
3

Я пытаюсь создать реактивное приложение в F # с использованием WPF, и я столкнулся с некоторыми проблемами с доступом к элементам XAML из кода.Доступ к элементам XAML (WPF) в F #

В 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. Я могу получить доступ к элементу grid внутри кода, но моя проблема в том, что я не знаю, как получить доступ к его дочерним элементам, которые я создал программно. Я думал о, возможно, рудиментарном решении, которое заключается в том, чтобы поймать событие щелчка со всей сетки и в то же время получить координаты мыши, чтобы вычислить, какая ячейка была нажата. Но это, вероятно, не очень хороший способ сделать это. Надеюсь, мой вопрос понятен, иначе, дайте мне знать.

+0

вы можете установить «grid.Children» при необходимости и загрузить ToggleButtons – ASh

+0

также: в цикле можно создавать RowDefinitions и ColumnDefinitions. Или просто используйте 'UniformGrid Columns =" ​​16 "Rows =" 16 "' вместо 'Grid' – ASh

+3

Почему вы не используете FsXAML + FSharp.ViewModule? –

ответ

3

Как я сказал в комментарии, проще использовать 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

Таким образом, у ou может делать что-либо с вашим объектом в функции click.

P.S. Я позволил себе немного изменить стиль ToggleButton, чтобы картина была более понятной.

0

Я не большой поклонник сторонних решений в качестве ответа. Если я понимаю ваш вопрос, вы пытаетесь получить доступ к элементу, указанному в вашем 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 

Ваш случай может немного отличаться, но это общая идея.

Смежные вопросы