2016-05-04 20 views
3

私はWPFを使用してF#でリアクティブなアプリケーションを作成しようとしていますが、コードからXAML要素にアクセスする際にいくつかの問題が発生しました。F#でXAML(WPF)要素にアクセスする

私は16列と行をそれぞれ含むグリッド作成したXAMLファイルで:F#の

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

を、Iはグリッドを横断し、プログラム的トグルボタンにすべてのセルを初期化:

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

今、私はToggleButtonsのいずれかをクリックするためのオブザーバブル(再帰的な非同期ループで待機する)を作成します。私はコード内のグリッド要素自体にアクセスできますが、私の問題はプログラマチックに作成した子要素にアクセスする方法がわからないことです。私は、グリッド全体からクリックイベントを捕捉すると同時に、どのセルがクリックされたかを計算するためのマウス座標を取得する、おそらく初歩的な解決策を考えていました。しかし、これはおそらくこれを行う良い方法ではありません。 私の質問が分かりやすいことを願っています。

+0

必要に応じて 'grid.Children'をループして、ToggleButtons – ASh

+0

も取得できます:RowDefinitionsとColumnDefinitionsをループで作成することができます。または、 'Grid 'ではなく' UniformGrid Columns = "16" Rows = "16" 'を使用してください。 – ASh

+3

なぜFsXAML + FSharp.ViewModuleを使用しませんか? –

答えて

3

私はコメントでFsXAML + FSharp.ViewModuleを使いやすくしました。

私はかなり、あなたがしたいものをそう簡単な例を理解していません。)=

.fsをあなたが代わりにリストボックスののItemsControlを使用することができます

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

を、それはいくつかの時間がかかるだろう設定:従って

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

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

あなたの場合は多少異なる場合がありますが、これは一般的な考え方です。

関連する問題