Compartilhar via


Passo a passo: Criando uma biblioteca de F# portátil

Ao seguir esta explicação passo a passo, você pode criar uma montagem em F# e usá-la com um aplicativo do Silverlight, um aplicativo tradicional para desktop ou em um aplicativo Windows Store criado com APIs .NET.Desse modo, você pode escrever a parte da interface do usuário do aplicativo em outra linguagem .NET, como C# ou Visual Basic, e a parte do algoritmo em F#.Você também pode suportar diversas interfaces do usuário segmentadas para plataformas diferentes.

Não é possível usar a interface do usuário do Windows Store diretamente de F#. Portanto, convém escrever a interface do usuário do seu aplicativo Windows Store em outra linguagem .NET e o código F# em uma biblioteca portátil.Você pode escrever interfaces do usuário do Silverlight e do Windows Presentation Foundation (WPF) diretamente em F#, mas convém aproveitar as ferramentas de design adicionais disponíveis ao escrever códigos C# ou Visual Basic no Visual Studio.

Pré-requisitos

Para criar um aplicativo Windows Store, você deve ter o Windows 8 no seu computador de desenvolvimento.

Para criar um projeto do Silverlight, você deve ter o Silverlight 5 no seu computador de desenvolvimento.

O aplicativo Planilha

Nesta explicação passo a passo, você desenvolverá uma planilha simples que apresenta uma grade ao usuário e aceita fórmulas e números em suas células.A camada F# processa e valida todos os dados inseridos e, em particular, analisa o texto da fórmula e computa os resultados das fórmulas.Primeiro, você criará o código do algoritmo de F#, que inclui o código para análise de expressões que envolvem referências a células, números e operadores matemáticos.Esse aplicativo também inclui o código para acompanhar quais células devem ser atualizadas quando um usuário atualiza o conteúdo de outra célula.Em seguida, você criará as interfaces do usuário.

A figura a seguir mostra o aplicativo que você criará nesta explicação passo a passo.

Interface do usuário do aplicativo Planilha

Captura de tela de F# portátil explicação do final do aplicativo

Esta explicação passo a passo contém as seções a seguir.

  • How To: Create an F# Portable Library

  • How To: Create a Silverlight App that Uses an F# Portable Library

  • How To: Create a ... Style App That Uses an F# Portable Library

  • How to: Create a Desktop App That References a Portable Library That Uses F#

Como: Criar uma biblioteca portátil de F#

  1. Na barra de menu, escolha Arquivo, Novo Projeto.Na caixa de diálogo Novo Projeto, expanda Visual F#, escolha o tipo de projeto Biblioteca Portátil de F# e atribua um nome à Planilha da biblioteca.O projeto faz referência a uma versão especial do FSharp.Core.

  2. No Gerenciador de Soluções, expanda o nó Referências e selecione o nó FSharp.Core.Na janela Propriedades, o valor da propriedade FullPath deve conter .NETPortable, que indica que você está usando a versão portátil da biblioteca principal F#.Você também pode analisar as diversas bibliotecas .NET que pode acessar por padrão.Todas essas bibliotecas trabalham com um subconjunto comum do .NET Framework definido como portátil .NET.Você pode remover referências desnecessárias. No entanto, se adicionar referências, sua montagem de referências deverá ser disponibilizada em todas as plataformas que estiver segmentando.A documentação de uma montagem geralmente indica as plataformas nas quais ela está disponível.

  3. Abra o menu de atalho do projeto e escolha Propriedades.Na guia Aplicativo, a estrutura de destino é definida como Subconjunto Portátil .NET.Para Visual Studio 2012, esse subconjunto segmenta .NET para aplicativos Windows Store, o .NET Framework 4.5 e o Silverlight 5.Essas configurações são importantes pois, como uma biblioteca portátil, seu aplicativo deve ser executado no tempo de execução disponível em diversas plataformas.Os tempos de execução de aplicativos Windows Store e do Silverlight 5 contêm subconjuntos de todo o .NET Framework.

  4. Renomeie o arquivo de código principal como Spreadsheet.fs. Em seguida, cole o código a seguir na janela do editor.Esse código define a funcionalidade de uma planilha básica.

    namespace Portable.Samples.Spreadsheet
    
    open System
    open System.Collections.Generic
    
    [<AutoOpen>]
    module Extensions = 
        type HashSet<'T> with
            member this.AddUnit(v) = ignore( this.Add(v) )
    
    type internal Reference = string
    
    /// Result of formula evaluation
    [<RequireQualifiedAccess>]
    type internal EvalResult = 
        | Success of obj
        | Error of string
    
    /// Function that resolves reference to value.
    /// If formula that computes value fails, this function should also return failure.
    type internal ResolutionContext = Reference -> EvalResult
    
    /// Parsed expression
    [<RequireQualifiedAccess>]
    type internal Expression = 
        | Val of obj
        | Ref of Reference
        | Op of (ResolutionContext -> list<Expression> -> EvalResult) * list<Expression>
        with 
        member this.GetReferences() = 
            match this with
            | Expression.Ref r -> Set.singleton r
            | Expression.Val _ -> Set.empty
            | Expression.Op (_, args) -> (Set.empty, args) ||> List.fold (fun acc arg -> acc + arg.GetReferences())
    
    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    module internal Operations = 
        
        let eval (ctx : ResolutionContext) = 
            function
            | Expression.Val v -> EvalResult.Success v
            | Expression.Ref r -> ctx r
            | Expression.Op (f, args) -> try f ctx args with e -> EvalResult.Error e.Message
        
        type private Eval = Do 
            with 
            member this.Return(v) = EvalResult.Success v
            member this.ReturnFrom(v) = v
            member this.Bind(r, f) = 
                match r with
                | EvalResult.Success v -> f v
                | EvalResult.Error _-> r
    
        let private mkBinaryOperation<'A, 'R> (op : 'A -> 'A -> 'R) ctx =
            function
            | [a; b] -> 
                Eval.Do {
                    let! ra = eval ctx a
                    let! rb = eval ctx b
                    match ra, rb with
                    | (:? 'A as ra), (:? 'A as rb) -> return op ra rb
                    | _ -> return! EvalResult.Error "Unexpected type of argument"
                }
            | _ -> EvalResult.Error "invalid number of arguments"
    
        let add = mkBinaryOperation<float, float> (+)
        let sub = mkBinaryOperation<float, float> (-)
        let mul = mkBinaryOperation<float, float> (*)
        let div = mkBinaryOperation<float, float> (/)
    
        let ge = mkBinaryOperation<float, bool> (>=)
        let gt = mkBinaryOperation<float, bool> (>)
    
        let le = mkBinaryOperation<float, bool> (<=)
        let lt = mkBinaryOperation<float, bool> (<)
    
        let eq = mkBinaryOperation<IComparable, bool> (=)
        let neq = mkBinaryOperation<IComparable, bool> (<>)
    
        let mmax = mkBinaryOperation<float, float> max
        let mmin = mkBinaryOperation<float, float> min
    
        let iif ctx = 
            function
            | [cond; ifTrue; ifFalse] -> 
                Eval.Do {
                    let! condValue = eval ctx cond
                    match condValue with
                    | :? bool as condValue-> 
                        let e = if condValue then ifTrue else ifFalse
                        return! eval ctx e
                    | _ -> return! EvalResult.Error "Condition should be evaluated to bool"
                }
            | _ -> EvalResult.Error "invalid number of arguments"
        
        let get (name : string) = 
            match name.ToUpper() with
            | "MAX" -> mmax
            | "MIN" -> mmin
            | "IF" -> iif
            | x -> failwithf "unknown operation %s" x
    
    module internal Parser =
        let private some v (rest : string) = Some(v, rest)
        let private capture pattern text =
            let m = System.Text.RegularExpressions.Regex.Match(text, "^(" + pattern + ")(.*)")
            if m.Success then
                some m.Groups.[1].Value m.Groups.[2].Value
            else None
        let private matchValue pattern = (capture @"\s*") >> (Option.bind (snd >> capture pattern))
    
        let private matchSymbol pattern = (matchValue pattern) >> (Option.bind (snd >> Some))
        let private (|NUMBER|_|) = matchValue @"-?\d+\.?\d*"
        let private (|IDENTIFIER|_|) = matchValue @"[A-Za-z]\w*"
        let private (|LPAREN|_|) = matchSymbol @"\("
        let private (|RPAREN|_|) = matchSymbol @"\)"
        let private (|PLUS|_|) = matchSymbol @"\+"
        let private (|MINUS|_|) = matchSymbol @"-"
        let private (|GT|_|) = matchSymbol @">"
        let private (|GE|_|) = matchSymbol @">="
        let private (|LT|_|) = matchSymbol @"<"
        let private (|LE|_|) = matchSymbol @"<="
        let private (|EQ|_|) = matchSymbol @"="
        let private (|NEQ|_|) = matchSymbol @"<>"
        let private (|MUL|_|) = matchSymbol @"\*"
        let private (|DIV|_|) = matchSymbol @"/"
        let private (|COMMA|_|) = matchSymbol @","
        let private operation op args rest = some (Expression.Op(op, args)) rest
        let rec private (|Factor|_|) = function
            | IDENTIFIER(id, r) ->
                match r with
                | LPAREN (ArgList (args, RPAREN r)) -> operation (Operations.get id) args r
                | _ -> some(Expression.Ref id) r
            | NUMBER (v, r) -> some (Expression.Val (float v)) r
            | LPAREN(Logical (e, RPAREN r)) -> some e r
            | _ -> None
    
        and private (|ArgList|_|) = function
            | Logical(e, r) ->
                match r with
                | COMMA (ArgList(t, r1)) -> some (e::t) r1
                | _ -> some [e] r
            | rest -> some [] rest
    
        and private (|Term|_|) = function
            | Factor(e, r) ->
                match r with
                | MUL (Term(r, rest)) -> operation Operations.mul [e; r] rest
                | DIV (Term(r, rest)) -> operation Operations.div [e; r] rest
                | _ -> some e r
            | _ -> None
    
        and private (|Expr|_|) = function
            | Term(e, r) ->
                match r with
                | PLUS (Expr(r, rest)) -> operation Operations.add [e; r] rest
                | MINUS (Expr(r, rest)) -> operation Operations.sub [e; r] rest
                | _ -> some e r
            | _ -> None
    
        and private (|Logical|_|) = function
            | Expr(l, r) ->
                match r with
                | GE (Logical(r, rest)) -> operation Operations.ge [l; r] rest
                | GT (Logical(r, rest)) -> operation Operations.gt [l; r] rest
                | LE (Logical(r, rest)) -> operation Operations.le [l; r] rest
                | LT (Logical(r, rest)) -> operation Operations.lt [l; r] rest
                | EQ (Logical(r, rest)) -> operation Operations.eq [l; r] rest
                | NEQ (Logical(r, rest)) -> operation Operations.neq [l; r] rest
                | _ -> some l r
            | _ -> None
    
        and private (|Formula|_|) (s : string) =
            if s.StartsWith("=") then
                match s.Substring(1) with
                | Logical(l, t) when System.String.IsNullOrEmpty(t) -> Some l
                | _ -> None
            else None
    
        let parse text = 
            match text with
            | Formula f -> Some f
            | _ -> None
    
    type internal CellReference = string
    
    module internal Dependencies = 
    
        type Graph() = 
            let map = new Dictionary<CellReference, HashSet<CellReference>>()
    
            let ensureGraphHasNoCycles(cellRef) =
                let visited = HashSet()
                let rec go cycles s =
                    if Set.contains s cycles then failwith ("Cycle detected:" + (String.concat "," cycles))
                    if visited.Contains s then cycles
                    else
                    visited.AddUnit s
                    if map.ContainsKey s then
                        let children = map.[s]
                        ((Set.add s cycles), children)
                            ||> Seq.fold go
                            |> (fun cycle -> Set.remove s cycles)
                    else
                        cycles
    
                ignore (go Set.empty cellRef)
    
            member this.Insert(cell, parentCells) = 
                for p in parentCells do
                    let parentSet = 
                        match map.TryGetValue p with
                        | true, set -> set
                        | false, _ ->
                            let set = HashSet()
                            map.Add(p, set)
                            set
                    parentSet.AddUnit cell
                try 
                    ensureGraphHasNoCycles cell
                with
                    _ -> 
                    this.Delete(cell, parentCells)
                    reraise()
                                 
            member this.GetDependents(cell) = 
                let visited = HashSet()
                let order = Queue()
                let rec visit curr = 
                    if not (visited.Contains curr) then 
                        visited.AddUnit curr
                        order.Enqueue(curr)
                        match map.TryGetValue curr with
                        | true, children -> 
                            for ch in children do
                                visit ch
                        | _ -> ()
    
                        
                visit cell
                order :> seq<_>
    
            member this.Delete(cell, parentCells) = 
                for p in parentCells do
                    map.[p].Remove(cell)
                    |> ignore
    
    type Cell = 
        {
            Reference : CellReference
            Value : string
            RawValue : string
            HasError : bool
        }
    
    type RowReferences = 
        {
            Name : string
            Cells : string[]
        }
    
    type Spreadsheet(height : int, width : int) = 
        
        do 
            if height <=0 then failwith "Height should be greater than zero"
            if width <=0 || width > 26 then failwith "Width should be greater than zero and lesser than 26"
    
        let rowNames = [| for i = 0 to height - 1 do yield string (i + 1)|]
        let colNames = [| for i = 0 to (width - 1) do yield string (char (int 'A' + i)) |]
    
        let isValidReference (s : string) = 
            if s.Length < 2 then false
            else
            let c = s.[0..0]
            let r = s.[1..]
            (Array.exists ((=)c) colNames) && (Array.exists ((=)r) rowNames)
    
        let dependencies = Dependencies.Graph()
        let formulas = Dictionary<_, Expression>()
    
        let values = Dictionary()
        let rawValues = Dictionary()
    
        let setError cell text = 
            values.[cell] <- EvalResult.Error text
    
        let getValue reference = 
            match values.TryGetValue reference with
            | true, v -> v
            | _ -> EvalResult.Success 0.0
        
        let deleteValue reference = 
            values.Remove(reference)
            |> ignore
    
        let deleteFormula cell = 
            match formulas.TryGetValue cell with
            | true, expr ->
                dependencies.Delete(cell, expr.GetReferences())
                formulas.Remove(cell) 
                |> ignore
            | _ -> ()
    
        let evaluate cell = 
            let deps = dependencies.GetDependents cell
            for d in deps do
                match formulas.TryGetValue d with
                | true, e -> 
                    let r = Operations.eval getValue e
                    values.[d] <- r
                | _ -> ()
            deps
    
        let setFormula cell text = 
            let setError msg = 
                setError cell msg
                [cell] :> seq<_>
            
            try 
                match Parser.parse text with
                | Some expr ->
                    let references = expr.GetReferences()
                    let invalidReferences = [for r in references do if not (isValidReference r) then yield r]
                    if not (List.isEmpty invalidReferences) then
                        let msg = sprintf "Formula contains invalid references:%s" (String.concat ", " invalidReferences)
                        setError msg
                    else
                    try
                        dependencies.Insert(cell, references)
                        formulas.Add(cell, expr)
                        |> ignore
                        evaluate cell
                    with
                        e -> setError e.Message
                | _ -> setError "Invalid formula text"
            with e -> setError e.Message
    
        member this.Headers = colNames
        member this.Rows = rowNames
        member this.GetRowReferences() = 
            seq { for r in rowNames do
                  let cells = [| for c in colNames do yield c + r |]
                  yield { Name = r; Cells = cells } }
    
        member this.SetValue(cellRef : Reference, value : string) : Cell[] = 
            rawValues.Remove(cellRef)
            |> ignore
    
            if not (String.IsNullOrEmpty value) then
                rawValues.[cellRef] <- value
    
            deleteFormula cellRef
            
            let affectedCells = 
                if (value <> null && value.StartsWith "=") then
                    setFormula cellRef value
                elif String.IsNullOrEmpty value then
                    deleteValue cellRef
                    evaluate cellRef
                else
                    match Double.TryParse value with
                    | true, value -> 
                        values.[cellRef] <- EvalResult.Success value
                        evaluate cellRef
                    | _ -> 
                        values.[cellRef] <- EvalResult.Error "Number expected"
                        [cellRef] :> _
            [| for r in affectedCells do 
                let rawValue = 
                    match rawValues.TryGetValue r with
                    | true, v -> v
                    | false, _ -> ""
    
                let valueStr, hasErr = 
                    match values.TryGetValue r with
                    | true, (EvalResult.Success v) -> (string v), false
                    | true, (EvalResult.Error msg) -> msg, true
                    | false, _ -> "", false
                let c = {Reference = r; Value = valueStr; RawValue = rawValue; HasError = hasErr}
                yield c |]
    

Como: Criar um aplicativo do Silverlight que usa uma biblioteca portátil de F#

  1. Na barra de menu, escolha Arquivo, Adicionar e depois Novo Projeto.Na caixa de diálogo Adicionar Novo Projeto, expanda Visual C#, Silverlight e escolha Aplicativo do Silverlight.A caixa de diálogo Novo Aplicativo do Silverlight é exibida.

  2. Verifique se a caixa de seleção de Hospedar o Aplicativo do Silverlight em um Novo Site está marcada e, no menu suspenso, verifique se a opção Projeto de Aplicativo da Web ASP.NET está selecionada. Em seguida, escolha o botão OK.Dois projetos são criados: um projeto tem o controle Silverlight e o outro é um aplicativo da Web ASP.NET que hospeda o controle.

  3. Adicione um referência ao projeto de planilha.Abra o menu de atalho do nó de referências do projeto Silverlight. Em seguida, escolha Adicionar Referência.O Gerenciador de Referências é exibido.Expanda o nó da solução e escolha o projeto Planilha. Em seguida, escolha o botão OK.

  4. Nessa etapa, você cria um modelo de exibição que descreve tudo que a interface do usuário deve fazer sem descrever sua aparência.Abra o menu de atalho do nó do projeto, escolha Adicionar e depois Novo Item.Adicione um arquivo de código, chame-o de ViewModel.cs e cole o seguinte código nele:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Input;
    using Portable.Samples.Spreadsheet;
    
    namespace SilverlightFrontEnd
    {
        public class SpreadsheetViewModel
        {
            private Spreadsheet spreadsheet;
            private Dictionary<string, CellViewModel> cells = new Dictionary<string, CellViewModel>();
    
            public List<RowViewModel> Rows { get; private set; }
            public List<string> Headers { get; private set; }
    
    
            public string SourceCode
            {
                get
                {
                    return @"
    type Spreadsheet(height : int, width : int) = 
    
        do 
            if height <= 0 then failwith ""Height should be greater than zero""
            if width <= 0 || width > 26 then failwith ""Width should be greater than zero and lesser than 26""
    
        let rowNames = [| for i = 0 to height - 1 do yield string (i + 1)|]
        let colNames = [| for i = 0 to (width - 1) do yield string (char (int 'A' + i)) |]
    
        let isValidReference (s : string) = 
            if s.Length < 2 then false
            else
            let c = s.[0..0]
            let r = s.[1..]
            (Array.exists ((=)c) colNames) && (Array.exists ((=)r) rowNames)
    
        let dependencies = Dependencies.Graph()
        let formulas = Dictionary<_, Expression>()
    
        let values = Dictionary()
        let rawValues = Dictionary()
    
        let setError cell text = 
            values.[cell] <- EvalResult.E text
    
        let getValue reference = 
            match values.TryGetValue reference with
            | true, v -> v
            | _ -> EvalResult.S 0.0
    
        let deleteValue reference = 
            values.Remove(reference)
            |> ignore
    
        let deleteFormula cell = 
            match formulas.TryGetValue cell with
            | true, expr ->
                dependencies.Delete(cell, expr.GetReferences())
                formulas.Remove(cell) 
                |> ignore
            | _ -> ()
    ";
                }
            }
    
            public SpreadsheetViewModel(Spreadsheet spreadsheet)
            {
                this.spreadsheet = spreadsheet;
                Rows = new List<RowViewModel>();
                foreach (var rowRef in spreadsheet.GetRowReferences())
                {
                    var rowvm = new RowViewModel { Index = rowRef.Name, Cells = new List<CellViewModel>() };
    
                    foreach (var reference in rowRef.Cells)
                    {
                        var cell = new CellViewModel(this, reference);
                        cells.Add(reference, cell);
                        rowvm.Cells.Add(cell);
                    }
                    Rows.Add(rowvm);
    
                }
                Headers = new[] { "  " }.Concat(spreadsheet.Headers).ToList();
            }
    
            public void SetCellValue(string reference, string newText)
            {
                var affectedCells = spreadsheet.SetValue(reference, newText);
                foreach (var cell in affectedCells)
                {
                    var cellVm = cells[cell.Reference];
                    cellVm.RawValue = cell.RawValue;
    
                    if (cell.HasError)
                    {
                        cellVm.Value = "#ERROR";
                        cellVm.Tooltip = cell.Value; // will contain error
                    }
                    else
                    {
                        cellVm.Value = cell.Value;
                        cellVm.Tooltip = cell.RawValue;
                    }
                }
            }
        }
    
        public class RowViewModel
        {
            public string Index { get; set; }
            public List<CellViewModel> Cells { get; set; }
        }
    
        public class CellViewModel : INotifyPropertyChanged
        {
            private SpreadsheetViewModel spreadsheet;
    
            private string rawValue;
            private string value;
            private string reference;
            private string tooltip;
    
            public CellViewModel(SpreadsheetViewModel spreadsheet, string reference)
            {
                this.spreadsheet = spreadsheet;
                this.reference = reference;
            }
    
            public string RawValue
            {
                get
                {
                    return rawValue;
                }
                set
                {
                    var changed = rawValue != value;
                    rawValue = value;
                    if (changed) RaisePropertyChanged("RawValue");
                }
            }
            public string Value
            {
                get
                {
                    return value;
                }
                set
                {
                    var changed = this.value != value;
                    this.value = value;
                    if (changed) RaisePropertyChanged("Value");
                }
            }
            public string Tooltip
            {
                get
                {
                    return tooltip;
                }
                set
                {
                    var changed = this.tooltip != value;
                    this.tooltip = value;
                    if (changed)
                    {
                        RaisePropertyChanged("Tooltip");
                        RaisePropertyChanged("TooltipVisibility");
                    }
                }
            }
    
            public Visibility TooltipVisibility
            {
                get { return string.IsNullOrEmpty(tooltip) ? Visibility.Collapsed : Visibility.Visible; }
            }
    
            public event PropertyChangedEventHandler PropertyChanged = delegate { };
    
            public void SetCellValue(string newValue)
            {
                spreadsheet.SetCellValue(reference, newValue);
            }
    
            private void RaisePropertyChanged(string name)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }
    
  5. No projeto de controle Silverlight, abra MainPage.xaml, que declara o layout da interface do usuário da planilha principal.Em MainPage.xaml, cole o código XAML a seguir no elemento de grade existente.

    <TextBlock Text="{Binding SourceCode}" FontSize="20" FontFamily="Consolas" Foreground="LightGray"/>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
          <StackPanel.Resources>
            <Style x:Key="CellBorder" TargetType="Border">
              <Setter Property="BorderThickness" Value="0.5"/>
              <Setter Property="BorderBrush" Value="LightGray"/>
            </Style>
            <Style x:Key="CaptionBorder" TargetType="Border" BasedOn="{StaticResource CellBorder}">
              <Setter Property="Background" Value="LightBlue"/>
            </Style>
            <Style x:Key="TextContainer" TargetType="TextBlock">
              <Setter Property="FontSize" Value="26"/>
              <Setter Property="FontFamily" Value="Segoe UI"/>
              <Setter Property="Width" Value="200"/>
              <Setter Property="Height" Value="60"/>
            </Style>
    
            <Style x:Key="CaptionText" TargetType="TextBlock" BasedOn="{StaticResource TextContainer}">
              <Setter Property="TextAlignment" Value="Center"/>
              <Setter Property="Foreground" Value="DimGray"/>
            </Style>
            <Style x:Key="ValueEditor" TargetType="TextBox">
              <Setter Property="Width" Value="200"/>
              <Setter Property="Height" Value="60"/>
              <Setter Property="FontSize" Value="26"/>
              <Setter Property="FontFamily" Value="Segoe UI"/>
    
            </Style>
            <Style x:Key="ValueText" TargetType="TextBlock" BasedOn="{StaticResource TextContainer}">
              <Setter Property="TextAlignment" Value="Center"/>
              <Setter Property="VerticalAlignment" Value="Center"/>
              <Setter Property="Foreground" Value="Black"/>
            </Style>
    
          </StackPanel.Resources>
          <Border Style="{StaticResource CellBorder}">
            <StackPanel>
    
              <ItemsControl ItemsSource="{Binding Headers}">
                <ItemsControl.ItemsPanel>
                  <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Horizontal" />
                  </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                  <DataTemplate>
                    <Border Style="{StaticResource CaptionBorder}">
                      <TextBlock Text="{Binding}" Style="{StaticResource CaptionText}"/>
                    </Border>
                  </DataTemplate>
                </ItemsControl.ItemTemplate>
              </ItemsControl>
    
              <ItemsControl ItemsSource="{Binding Rows}">
                <ItemsControl.ItemTemplate>
                  <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                      <Border Style="{StaticResource CaptionBorder}">
                        <TextBlock Text="{Binding Index}" Style="{StaticResource CaptionText}"/>
                      </Border>
                      <ItemsControl ItemsSource="{Binding Cells}">
                        <ItemsControl.ItemsPanel>
                          <ItemsPanelTemplate>
                            <VirtualizingStackPanel  Orientation="Horizontal"/>
                          </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                          <DataTemplate>
                            <Border Style="{StaticResource CellBorder}">
                              <Grid>
                                <TextBox
                                  Name="editor"
                                  Tag="{Binding ElementName=textContainer}"
                                  Visibility="Collapsed"
                                  LostFocus="OnLostFocus"
                                  KeyUp="OnKeyUp"
                                  Text ="{Binding RawValue}"
                                  Style="{StaticResource ValueEditor}"/>
                                <TextBlock
                                  Name="textContainer"
                                  Tag="{Binding ElementName=editor}"
                                  Visibility="Visible"
                                  Text="{Binding Value}"
                                  Style="{StaticResource ValueText}"
                                  MouseLeftButtonDown="OnMouseLeftButtonDown"
                                  ToolTipService.Placement="Mouse">
                                  <ToolTipService.ToolTip>
                                    <ToolTip Visibility="{Binding TooltipVisibility}">
                                      <TextBlock Text="{Binding Tooltip}" Style="{StaticResource TextContainer}" Visibility="{Binding TooltipVisibility}"/>
                                    </ToolTip>
                                  </ToolTipService.ToolTip>
                                </TextBlock>
                              </Grid>
                            </Border>
                          </DataTemplate>
                        </ItemsControl.ItemTemplate>
                      </ItemsControl>
                    </StackPanel>
                  </DataTemplate>
                </ItemsControl.ItemTemplate>
              </ItemsControl>
    
            </StackPanel>
          </Border>
        </StackPanel>
    
  6. Em MainPage.xaml.cs, adicione using SilverlightFrontEnd; à lista de diretivas de uso. Em seguida, adicione os seguintes métodos à classe SilverlightApplication1.

            void OnLostFocus(object sender, RoutedEventArgs e)
            {
                var editor = (TextBox)e.OriginalSource;
                var text = editor.Text;
    
                HideEditor(e);
    
                EditValue(editor.DataContext, text);
            }
    
            void OnKeyUp(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Escape)
                {
                    HideEditor(e);
                    e.Handled = true;
                    return;
                }
                else if (e.Key == Key.Enter)
                {
                    var editor = (TextBox)e.OriginalSource;
                    var text = editor.Text;
    
                    HideEditor(e);
    
                    EditValue(editor.DataContext, text);
                    e.Handled = true;
                }
            }
    
            private void EditValue(object dataContext, string newText)
            {
                var cvm = (CellViewModel)dataContext;
                cvm.SetCellValue(newText);
            }
    
            private void OnMouseLeftButtonDown(object sender, RoutedEventArgs e)
            {
                var textBlock = (TextBlock)e.OriginalSource;
                var editor = (TextBox)textBlock.Tag;
                textBlock.Visibility = Visibility.Collapsed;
                editor.Visibility = Visibility.Visible;
                editor.Focus();
            }
    
            private void HideEditor(RoutedEventArgs e)
            {
                var editor = (TextBox)e.OriginalSource;
                var textBlock = (TextBlock)editor.Tag;
                editor.Visibility = Visibility.Collapsed;
                textBlock.Visibility = Visibility.Visible;
            }
    
  7. Em App.xaml.cs, adicione as seguintes diretivas de uso a ele:

    using SilverlightFrontEnd;
    using Portable.Samples.Spreadsheet;
    

    Cole o seguinte código no manipulador de eventos do Application_Startup:

                var spreadsheet = new Spreadsheet(5, 5);
                var spreadsheetViewModel = new SpreadsheetViewModel(spreadsheet);
                var main = new MainPage();
                main.DataContext = spreadsheetViewModel;
                this.RootVisual = main;
    
  8. Você pode testar o front end do Silverlight iniciando o projeto Silverlight diretamente ou o aplicativo da Web ASP.NET que hospeda o controle do Silverlight.Abra o menu de atalho do nó para qualquer um desses projetos. Em seguida, escolha Definir como Projeto de Inicialização.

Como: Criar um aplicativo Windows Store que usa uma biblioteca portátil de F#

  1. Nesta seção, você criará um aplicativo Windows Store que usa o código da planilha F# como seu componente de computação.Na barra de menu, escolha Arquivo, Adicionar, Novo Projeto.A caixa de diálogo Novo Projeto é exibida.Em Instalado, expanda Visual C#, Windows Store e escolha o modelo Aplicativo em Branco.Chame o projeto de NewFrontEnd e escolha o botão OK.Se sua licença de desenvolvedor for solicitada para criar aplicativos Windows Store, insira suas credenciais.Se você não tiver credenciais, saiba como configurá-las aqui.

    O projeto é criado.Anote a configuração e o conteúdo desse projeto.As referências padrão incluem o .NET para aplicativos da Windows Store, que é o subconjunto do .NET Framework compatível com aplicativos do Windows Store, e a montagem do Windows, que inclui as APIs do Tempo de Execução do Windows e a interface do usuário de aplicativos do Windows Store.As subpastas Comuns e Ativos foram criadas.A subpasta Ativos contém diversos ícones que se aplicam a aplicativos do Windows Store, e a subpasta Comuns contém rotinas compartilhadas com modelos para serem usados por aplicativos do Windows Store.O modelo de projeto padrão também criou App.xaml, BlankPage.xaml e seus arquivos code-behind C# associados, App.xaml.cs e BlankPage.xaml.cs.O App.xaml descreve o aplicativo geral, e o BlankPage.xaml descreve a superfície da interface do usuário definida.Por fim, todos os arquivos .pfx e .appxmanifest suportam a segurança e os modelos de implementação de aplicativos do Windows Store.

  2. Acrescente uma referência ao projeto Planilha abrindo o menu de atalho do nó de referências do projeto Silverlight. Em seguida, escolha Adicionar Referência.No Gerenciador de Referências, expanda o nó da solução e escolha o projeto Planilha. Em seguida, escolha o botão OK.

  3. Você precisará de alguns códigos que já usou no projeto Silverlight para suportar o código da interface do usuário do aplicativo do Windows Store.Esse código está no ViewModels.cs.Abra o menu de atalho do nó do projeto de NewFrontEnd, escolha Adicionar e depois Novo Item.Adicione um arquivo de código C# e chame-o de ViewModels.cs.Cole o código de ViewModels.cs no projeto Silverlight e, então, altere o bloco das diretivas de uso na parte superior desse arquivo.Remova System.Windows, que é usado para a interface do usuário do Silverlight, e adicione Windows.UI.Xaml e Windows.Foundation.Collections, que são usados para a interface do usuário do aplicativo do Windows Store.O Silverlight e a interface do usuário do Windows Store são baseados no WPF. Portanto, eles são compatíveis um com o outro.O bloco atualizado das diretivas de uso deve ser parecido com o seguinte exemplo:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;
    using Portable.Samples.Spreadsheet;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    

    Além disso, altere o namespace em ViewModels.cs de SilverlightFrontEnd para NewFrontEnd.

    Você pode reutilizar o restante do código no ViewModels.cs. No entanto, alguns tipos, como Visibilidade, agora são as versões de aplicativos do Windows Store, e não do Silverlight.

  4. Neste aplicativo do Windows Store, o arquivo de código App.xaml.cs deve ter um código de inicialização semelhante ao que apareceu no manipulador de eventos do Application_Startup do aplicativo do Silverlight.Em um aplicativo do Windows Store, esse código aparece no manipulador de eventos do OnLaunched da classe do aplicativo.Adicione o seguinte código ao manipulador de eventos do OnLaunched no App.xaml.cs:

    var spreadsheet = new Spreadsheet(5, 5);
    var spreadsheetViewModel = new SpreadSheetViewModel(spreadsheet);
    
  5. Adicione uma diretiva de uso ao código da planilha.

    using Portable.Samples.Spreadsheet;
    
  6. No App.xaml.cs, o OnLaunched contém o código que especifica a página a ser carregada.Você adicionará uma página que quer que o aplicativo carregue quando for iniciado por um usuário.Altere o código em OnLaunched para navegar até a primeira página, conforme mostra o seguinte exemplo:

    // Create a frame, and navigate to the first page.
    var rootFrame = new Frame();
    rootFrame.Navigate(typeof(ItemsPage1), spreadsheetViewModel);
    

    Você pode excluir BlankPage1.xaml e seu arquivo code-behind porque eles não são usados neste exemplo.

  7. Abra o menu de atalho do nó do projeto de NewFrontEnd, escolha Adicionar e depois Novo Item.Adicione uma página de itens e mantenha o nome padrão, ItemsPage1.xaml.Esta etapa adiciona o ItemsPage1.xaml e seu arquivo code-behind, ItemsPage1.xaml.cs, ao projeto.O ItemsPage1.xaml começa com uma tag principal do common:LayoutAwarePage com muitos atributos, conforme mostra o seguinte código XAML:

    <common:LayoutAwarePage
        x:Name="pageRoot"
        x:Class="NewFrontEnd.ItemsPage1"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:NewFrontEnd"
        xmlns:common="using:NewFrontEnd.Common"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    

    A interface do usuário do aplicativo Windows Store é idêntica à do aplicativo do Silverlight criado, e o formato XAML é o mesmo nesse caso.Portanto, você pode reutilizar o XAML do MainPage.xaml no projeto Silverlight de ItemsPage1.xaml na interface do usuário do aplicativo do Windows Store.

  8. Copie o código do elemento de grade de nível superior de MainPage.xaml do projeto Silverlight e cole-o no elemento de grade de nível superior em ItemsPage1.xaml no projeto da interface do usuário do aplicativo do Windows Store.Quando você cola o código, pode substituir qualquer conteúdo existente do elemento de grade.Altere o atributo de plano de fundo do elemento de grade para "Branco" e substitua MouseLeftButtonDown por PointerPressed.

    O nome desse evento difere em aplicativos do Silverlight e aplicativos do Windows Store.

  9. Em ItemsPage.xaml.cs, defina a propriedade DataContext alterando o método OnNavigatedTo.

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        this.DataContext = e.Parameter;
    }
    
  10. Copie o seguinte código do manipulador de eventos e cole-o na classe ItemsPage1: OnLostFocus, OnKeyUp, EditValue, OnPointerPressed, and HideEditor.

    void OnLostFocus(object sender, RoutedEventArgs e)
            {
                var editor = (TextBox)e.OriginalSource;
                var text = editor.Text;
    
                HideEditor(e);
    
                EditValue(editor.DataContext, text);
            }
    
            void OnKeyUp(object sender, KeyEventArgs e)
            {
                if (e.Key == Windows.System.VirtualKey.Escape)
                {
                    HideEditor(e);
                    e.Handled = true;
                    return;
                }
                else if (e.Key == Windows.System.VirtualKey.Enter)
                {
                    var editor = (TextBox)e.OriginalSource;
                    var text = editor.Text;
    
                    HideEditor(e);
    
                    EditValue(editor.DataContext, text);
                    e.Handled = true;
                }            
            }
    
            private void EditValue(object dataContext, string newText)
            {
                var cvm = (CellViewModel)dataContext;
                cvm.SetCellValue(newText);
            }
    
            private void OnPointerPressed(object sender, RoutedEventArgs e)
            {
                var textBlock = (TextBlock)e.OriginalSource;
                var editor = (TextBox)textBlock.Tag;
                textBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                editor.Visibility = Windows.UI.Xaml.Visibility.Visible;
    
                editor.Focus(FocusState.Programmatic);
            }
    
            private void HideEditor(RoutedEventArgs e)
            {
                var editor = (TextBox)e.OriginalSource;
                var textBlock = (TextBlock)editor.Tag;
                editor.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                textBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
            }
    
  11. Altere o projeto de inicialização para o projeto de seu aplicativo do Windows Store.Abra o menu de atalho do nó do projeto NewFrontEnd, escolha Definir como Projeto de Inicialização e pressione a tecla F5 para executar o projeto.

Criação de uma biblioteca portátil em C# que usa F#

A amostra anterior duplica o código de modo que o código ViewModels.cs apareça em vários projetos.Nesta seção, você criará um projeto de biblioteca portátil C# que conterá esse código.Em alguns casos, você deve adicionar informações ao arquivo de configuração de um aplicativo quando ele consome bibliotecas portáteis que usam F#.Nesse caso, um aplicativo para desktop, que segmenta a versão para desktop do .NET Framework 4.5, faz referência a uma biblioteca portátil de C# que, por sua vez, faz referências a uma biblioteca portátil de F#.Nesse caso, você deve adicionar um redirecionamento de associação ao arquivo app.config do aplicativo principal.Você deve adicionar esse redirecionamento, pois somente uma versão da biblioteca FSharp.Core é carregada. No entanto, as bibliotecas portáteis fazem referência à versão portátil de .NET.Todas as chamadas às versões portáteis de .NET de funções FSharp.Core devem ser redirecionadas à versão única do FSharp.Core carregada em um aplicativo para desktop.Os redirecionamentos de associações são necessários somente no aplicativo para desktop, pois os ambientes de tempo de execução do Silverlight 5 e dos aplicativos do Windows Store usam a versão portátil .NET do FSharp.Core, não a versão completa para desktop.

Como: Criar um aplicativo para desktop que faz referência a uma biblioteca portátil que usa F#

  1. Na barra de menu, escolha Arquivo, Adicionar, Novo Projeto.Em Instalado, expanda o nó Visual C#, escolha o modelo de projeto Biblioteca Portátil .NET e atribua o nome ViewModels ao projeto.

  2. Você deve definir as segmentações dessa biblioteca portátil .NET de modo a corresponder à biblioteca portátil de F# à qual adicionará uma referência.Caso contrário, uma mensagem de erro informará sobre a falta de correspondência.No menu de atalho do projeto ViewModels, escolha Propriedades.Na guia Biblioteca, altere as segmentações dessa biblioteca portátil de modo a corresponder ao .NET Framework 4.5, ao Silverlight 5 e aos aplicativos do Windows Store.

  3. No menu de atalho do nó de Referências, escolha Adicionar Referência.Em Solução, marque a caixa de seleção ao lado de Planilha.

  4. Copie o código de ViewModels.cs de um dos outros projetos e cole-o no arquivo de código do projeto ViewModels.

  5. Faça as seguintes alterações, que tornam o código em ViewModels completamente independente da plataforma da interface do usuário:

    1. Remova as diretivas de uso de System.Windows, System.Windows.Input, Windows.Foundation.Collections e Windows.UI.Xaml, se estiverem presentes.

    2. Altere o namespace para ViewModels.

    3. Remova a propriedade TooltipVisibility.Essa propriedade usa Visibilidade, que é um objeto dependente da plataforma.

  6. Na barra de menu, escolha Arquivo, Adicionar, Novo Projeto.Em Instalado, expanda o nó Visual C# e escolha o modelo de projeto Aplicativo WPF.Chame o novo projeto de Desktop e escolha o botão OK.

  7. Abra o menu de atalho do nó de Referências no projeto Desktop e escolha Adicionar Referência.Em Solução, escolha os projetos Planilha e ViewModels.

  8. Abra o arquivo app.config do aplicativo WPF, e então, adicione as seguintes linhas de código.Esse código configura os redirecionamentos de associação apropriados aplicáveis quando um aplicativo para desktop que segmenta o .NET Framework 4.5 faz referência a uma biblioteca portátil .NET que usa F#.As bibliotecas portáteis .NET usam a versão 2.3.5.0 da biblioteca FSharp.Core, e os aplicativos para desktop do .NET Framework 4.5 usam a versão 4.3.0.0.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
        </startup>
        <runtime>
            <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
                <dependentAssembly>
                    <assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
                    <bindingRedirect oldVersion="2.3.5.0" newVersion="4.3.0.0"/>
                </dependentAssembly>
            </assemblyBinding>
        </runtime>
    </configuration>
    

    Agora, você deve adicionar uma referência à versão portátil da biblioteca F# Core.Essa referência é necessária sempre que você tem um aplicativo que consome uma biblioteca portátil que faz referência a uma biblioteca portátil de F#.

  9. Abra o menu de atalho do nó de Referências no projeto Desktop e escolha Adicionar Referência.Escolha Procurar e vá até Reference Assemblies\Microsoft\FSharp\3.0\Runtime\.NETPortable\FSharp.Core.dll na pasta Arquivos de Programas em que o Visual Studio está instalado.

  10. No projeto Desktop, adicione as diretivas de uso de ViewModels.cs e Portable.Samples.Spreadsheet a App.xaml.cs e MainWindow.xaml.cs.

    using ViewModels;
    using Portable.Samples.Spreadsheet;
    
  11. Abra o arquivo MainWindow.xaml e altere o atributo do título da classe do Window para Planilha.

  12. Copie o código do elemento de grade de nível superior de MainPage.xaml no projeto Silverlight e cole-o no elemento de grade de MainWindow.xaml no projeto Desktop.

  13. Copie o código do manipulador de eventos em MainPage.xaml.cs do projeto Silverlight e cole-o em MainWindow.xaml.cs no projeto Desktop.

            void OnLostFocus(object sender, RoutedEventArgs e)
            {
                var editor = (TextBox)e.OriginalSource;
                var text = editor.Text;
    
                HideEditor(e);
    
                EditValue(editor.DataContext, text);
            }
    
            void OnKeyUp(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Escape)
                {
                    HideEditor(e);
                    e.Handled = true;
                    return;
                }
                else if (e.Key == Key.Enter)
                {
                    var editor = (TextBox)e.OriginalSource;
                    var text = editor.Text;
    
                    HideEditor(e);
    
                    EditValue(editor.DataContext, text);
                    e.Handled = true;
                }
            }
    
            private void EditValue(object dataContext, string newText)
            {
                var cvm = (CellViewModel)dataContext;
                cvm.SetCellValue(newText);
            }
    
            private void OnMouseLeftButtonDown(object sender, RoutedEventArgs e)
            {
                var textBlock = (TextBlock)e.OriginalSource;
                var editor = (TextBox)textBlock.Tag;
                textBlock.Visibility = Visibility.Collapsed;
                editor.Visibility = Visibility.Visible;
                editor.Focus();
            }
    
            private void HideEditor(RoutedEventArgs e)
            {
                var editor = (TextBox)e.OriginalSource;
                var textBlock = (TextBlock)editor.Tag;
                editor.Visibility = Visibility.Collapsed;
                textBlock.Visibility = Visibility.Visible;
            }
    
  14. Adicione o código de inicialização de planilhas ao construtor MainWindow em MainWindow.xaml.cs e substitua referências a MainPage por referências a MainWindow.

        public MainWindow()
        {
                var spreadsheet = new Spreadsheet(5, 5);
                var spreadsheetViewModel = new SpreadsheetViewModel(spreadsheet);
    
    
                this.DataContext = spreadsheetViewModel;
                InitializeComponent();
        }
    
  15. Abra o menu de atalho do projeto Desktop. Em seguida, escolha Definir como Projeto de Inicialização.

  16. Pressione a tecla F5 para criar o aplicativo e, então, depure-o.

Próximas etapas

Como alternativa, você pode modificar os projetos do aplicativo do Windows Store e do aplicativo do Silverlight para usar na nova biblioteca portátil ViewModels.

Continue a saber mais sobre os aplicativos do Windows Store no Centro de Desenvolvedores Windows.

Consulte também

Conceitos

Aplicativos da Windows Store

Desenvolvimento entre plataformas com o .NET Framework

Outros recursos

Visual F# exemplos e explicações passo a passo

Silverlight