在 XAML 和 F 中建立對話方塊

螺旋儀引數的 XAML 檔案如下。它包括三個用於呼吸描記器引數的文字框和一組三個用於顏色的單選按鈕。當我們給單選按鈕提供相同的組名時 - 就像我們在這裡一樣 - 當選擇一個時,WPF 處理開/關切換。

<!-- This first part is boilerplate, except for the title, height and width.
     Note that some fussing with alignment and margins may be required to get
     the box looking the way you want it. -->
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Parameters" Height="200" Width="250">
    <!-- Here we define a layout of 3 rows and 2 columns below the title bar -->
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <!-- Define a label and a text box for the first three rows. Top row is
             the integer radius of the outer circle -->
        <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="0" 
                    Grid.ColumnSpan="2">
            <Label VerticalAlignment="Top" Margin="5,6,0,1" Content="R: Outer" 
                   Height="24" Width='65'/>
            <TextBox x:Name="radiusR"  Margin="0,0,0,0.5" Width="120" 
                     VerticalAlignment="Bottom" Height="20">Integer</TextBox>
        </StackPanel>
        <!-- This defines a label and text box for the integer radius of the
             inner circle -->
        <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="1" 
                    Grid.ColumnSpan="2">
            <Label VerticalAlignment="Top" Margin="5,6,0,1" Content="r: Inner" 
                   Height="24" Width='65'/>
            <TextBox x:Name="radiusr"  Margin="0,0,0,0.5" Width="120" 
                     VerticalAlignment="Bottom" Height="20" Text="Integer"/>
        </StackPanel>
        <!-- This defines a label and text box for the float ratio of the inner
             circle radius at which the pen is positioned -->
        <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="2" 
                    Grid.ColumnSpan="2">
            <Label VerticalAlignment="Top" Margin="5,6,0,1" Content="l: Ratio" 
                   Height="24" Width='65'/>
            <TextBox x:Name="ratiol"  Margin="0,0,0,1" Width="120" 
                     VerticalAlignment="Bottom" Height="20" Text="Float"/>
        </StackPanel>
        <!-- This defines a radio button group to select color -->
        <StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="3" 
                    Grid.ColumnSpan="2">
            <Label VerticalAlignment="Top" Margin="5,6,4,5.333" Content="Color" 
                   Height="24"/>
            <RadioButton x:Name="buttonBlue" Content="Blue" GroupName="Color" 
                         HorizontalAlignment="Left"  VerticalAlignment="Top"
                         Click="buttonBlueClick"
                         Margin="5,13,11,3.5" Height="17"/>
            <RadioButton x:Name="buttonRed"  Content="Red"  GroupName="Color" 
                         HorizontalAlignment="Left" VerticalAlignment="Top"
                         Click="buttonRedClick"
                         Margin="5,13,5,3.5" Height="17" />
            <RadioButton x:Name="buttonRandom"  Content="Random"  
                         GroupName="Color" Click="buttonRandomClick"
                         HorizontalAlignment="Left" VerticalAlignment="Top"
                         Margin="5,13,5,3.5" Height="17" />
        </StackPanel>
        <!-- These are the standard OK/Cancel buttons -->
        <Button Grid.Row="4" Grid.Column="0" Name="okButton" 
                Click="okButton_Click" IsDefault="True">OK</Button>
        <Button Grid.Row="4" Grid.Column="1" Name="cancelButton" 
                IsCancel="True">Cancel</Button>
    </Grid>
</Window>

現在我們為 Dialog.Box 新增程式碼。按照慣例,用於處理對話方塊與程式其餘部分的介面的程式碼名為 XXX.xaml.fs,其中關聯的 XAML 檔名為 XXX.xaml。

namespace Spirograph

open System.Windows.Controls

type DialogBox(app: App, model: Model, win: MainWindowXaml) as this =
  inherit DialogBoxXaml()

  let myApp   = app
  let myModel = model
  let myWin   = win
  
  // These are the default parameters for the spirograph, changed by this dialog
  // box
  let mutable myR = 220                 // outer circle radius
  let mutable myr = 65                  // inner circle radius
  let mutable myl = 0.8                 // pen position relative to inner circle
  let mutable myColor = MBlue           // pen color

  // These are the dialog box controls. They are initialized when the dialog box
  // is loaded in the whenLoaded function below.
  let mutable RBox: TextBox = null
  let mutable rBox: TextBox = null
  let mutable lBox: TextBox = null

  let mutable blueButton: RadioButton   = null
  let mutable redButton: RadioButton    = null
  let mutable randomButton: RadioButton = null

  // Call this functions to enable or disable parameter input depending on the
  // state of the randomButton. This is a () -> () function to keep it from
  // being executed before we have loaded the dialog box below and found the
  // values of TextBoxes and RadioButtons.
  let enableParameterFields(b: bool) = 
    RBox.IsEnabled <- b
    rBox.IsEnabled <- b
    lBox.IsEnabled <- b

  let whenLoaded _ =
    // Load and initialize text boxes and radio buttons to the current values in 
    // the model. These are changed only if the OK button is clicked, which is 
    // handled below. Also, if the color is Random, we disable the parameter
    // fields.
    RBox <- this.FindName("radiusR") :?> TextBox
    rBox <- this.FindName("radiusr") :?> TextBox
    lBox <- this.FindName("ratiol")  :?> TextBox

    blueButton   <- this.FindName("buttonBlue")   :?> RadioButton
    redButton    <- this.FindName("buttonRed")    :?> RadioButton
    randomButton <- this.FindName("buttonRandom") :?> RadioButton

    RBox.Text <- myModel.MyR.ToString()
    rBox.Text <- myModel.Myr.ToString()
    lBox.Text <- myModel.Myl.ToString()

    myR <- myModel.MyR
    myr <- myModel.Myr
    myl <- myModel.Myl
  
    blueButton.IsChecked   <- new System.Nullable<bool>(myModel.MyColor = MBlue)
    redButton.IsChecked    <- new System.Nullable<bool>(myModel.MyColor = MRed)
    randomButton.IsChecked <- new System.Nullable<bool>(myModel.MyColor = MRandom)
   
    myColor <- myModel.MyColor
    enableParameterFields(not (myColor = MRandom))

  let whenClosing _ =
    // Show the actual spirograph parameters in a message box at close. Note the 
    // \n in the sprintf gives us a linebreak in the MessageBox. This is mainly
    // for debugging, and it can be deleted.
    let s = sprintf "R = %A\nr = %A\nl = %A\nColor = %A" 
                    myModel.MyR myModel.Myr myModel.Myl myModel.MyColor
    System.Windows.MessageBox.Show(s, "Spirograph") |> ignore
    ()
  
  let whenClosed _ =
    () 
  
  do 
    this.Loaded.Add whenLoaded
    this.Closing.Add whenClosing
    this.Closed.Add whenClosed

  override this.buttonBlueClick(sender: obj, 
                                eArgs: System.Windows.RoutedEventArgs) =
    myColor <- MBlue
    enableParameterFields(true)
    () 
  
  override this.buttonRedClick(sender: obj, 
                               eArgs: System.Windows.RoutedEventArgs) =
    myColor <- MRed      
    enableParameterFields(true)
    () 
  
  override this.buttonRandomClick(sender: obj, 
                                  eArgs: System.Windows.RoutedEventArgs) =
    myColor <- MRandom
    enableParameterFields(false)
    () 
  
  override this.okButton_Click(sender: obj,
                               eArgs: System.Windows.RoutedEventArgs) =
    // Only change the spirograph parameters in the model if we hit OK in the 
    // dialog box.
    if myColor = MRandom
    then myModel.Randomize
    else myR <- RBox.Text |> int
         myr <- rBox.Text |> int
         myl <- lBox.Text |> float

         myModel.MyR   <- myR
         myModel.Myr   <- myr
         myModel.Myl   <- myl
         model.MyColor <- myColor

    // Note that setting the DialogResult to nullable true is essential to get
    // the OK button to work.
    this.DialogResult <- new System.Nullable<bool> true         
    () 

這裡的大部分程式碼都致力於確保 Spirograph.fs 中的 spirograph 引數與此對話方塊中顯示的引數相匹配。請注意,沒有錯誤檢查:如果為前兩個引數欄位中預期的整數輸入浮點,程式將崩潰。因此,請在你自己的努力中新增錯誤檢查。

另請注意,禁用引數輸入欄位,在單選按鈕中選擇隨機顏色。它只是為了展示它是如何完成的。

為了在對話方塊和程式之間來回移動資料,我使用 System.Windows.Element.FindName() 來查詢適當的控制元件,將其轉換為應該控制的控制元件,然後從中獲取相關的設定。控制。大多數其他示例程式使用資料繫結。我沒有出於兩個原因:第一,我無法弄清楚如何使它工作,其次,當它不起作用時,我沒有任何錯誤資訊。也許有人在 StackOverflow 上訪問它可以告訴我如何使用資料繫結而不包括一整套新的 NuGet 包。