Procedura dettagliata: creazione di una libreria F# portabile
Grazie a questa procedura dettagliata è possibile creare un assembly in F# da utilizzare con un'applicazione Silverlight, un'applicazione desktop tradizionale, o in un'applicazione Windows Store che è possibile creare utilizzando le API .NET. In questo modo è possibile scrivere la porzione dell'interfaccia utente dell'applicazione in un altro linguaggio .NET, come C# o Visual Basic e la porzione di algoritmo in F#. È inoltre possibile supportare diverse interfacce utente destinate a problemi diversi.
Non è possibile utilizzare l'interfaccia utente di Windows Store direttamente da F#, si consiglia quindi di scrivere per la propria applicazione Windows Store in un altro linguaggio .NET e scrivere il codice F# in una libreria portabile. È possibile scrivere le interfacce utente Silverlight e Windows Presentation Foundation (WPF) direttamente in F#, ma è possibile che si desideri trarre vantaggio dagli strumenti di progettazione aggiuntivi disponibili quando si scrive il codice C# o Visual Basic in Visual Studio.
Prerequisiti
Per creare un'applicazione Windows Store è necessario disporre di Windows 8 nel computer di sviluppo.
Per creare un progetto Silverlight, è necessario disporre di Silverlight 5 nel computer di sviluppo.
Applicazione del foglio di lavoro
In questa procedura dettagliata viene sviluppato un foglio di lavoro semplice in cui viene presentata una griglia all'utente e viene accettato un input numerico e le formule nelle celle. Nel livello F# vengono elaborati e convalidati tutti gli input e, in particolare, viene analizzato il testo della formula e elaborati i risultati delle formule. Prima di tutto, viene creato il codice algoritmico F# che include il codice per le espressioni di analisi che interessano i riferimenti alle celle, i numeri e gli operatori matematici. In questa applicazione è incluso il codice per tracciare le celle da aggiornare quando un utente aggiorna il contenuto di un'altra cella. Successivamente verranno create le interfacce utente.
Nella figura seguente viene illustrata l'applicazione che verrà creata in questa procedura dettagliata.
Interfaccia utente dell'applicazione del foglio di lavoro
In questa procedura dettagliata sono contenute le sezioni seguenti.
Procedura: creare una libreria portabile F#
Procedura: creare un'applicazione Silverlight che utilizza una libreria portabile F#
Procedura: creare un'applicazione Windows Store che utilizza una libreria portabile F#
Procedura: creare un'applicazione desktop che fa riferimento a una libreria portabile che utilizza F#
Creazione di una libreria portabile in F#
Procedura: creare una libreria portabile F#
Nella barra dei menu, scegliere File, Nuovo progetto. Nella casella di dialogo Nuovo progetto espandere Visual F# scegliere il tipo di progetto Libreria portabile F#, quindi nominare la libreria Foglio di lavoro. Si noti che il progetto fa riferimento a una versione speciale di FSharp.Core.
In Esplora soluzioni espandere il nodo Riferimenti, quindi selezionare il nodo FSharp.Core. Nella finestra Proprietà, il valore della proprietà FullPath deve contenere .NETPortable che indica che si sta utilizzando la versione portabile della libreria Core F#. È inoltre possibile visualizzare le diverse librerie .NET a cui è possibile accedere per impostazione predefinita. Tali librerie tutto funzionano tutte con un sottoinsieme comune di .NET Framework definito come .NET portabile. È possibile eliminare i riferimenti non necessari, ma se si aggiungono, l'assembly di riferimento deve essere disponibile su tutte le piattaforme di destinazione. La documentazione per un assembly di solito indica le piattaforme sulle quali è disponibile.
Aprire il menu di scelta rapida per il progetto, quindi scegliere Proprietà. Nella scheda Applicazione il framework di destinazione è impostato su Subset portabili .NET. Per Visual Studio 2012 questo sottoinsieme è destinato a .NET per le applicazioni Windows Store, .NET Framework 4.5 e Silverlight 5. Queste impostazioni sono importanti poiché, come libreria portabile, l'applicazione deve essere eseguita nel runtime disponibile in diverse piattaforme. Nei runtime per le applicazioni Windows Store e Silverlight 5 sono contenuti sottoinsiemi di .NET Framework completo.
Rinominare il file di codice principale Spreadsheet.fs e quindi incollare il seguente codice nella finestra dell'editor. In questo codice viene definita la funzionalità di un foglio di calcolo di base.
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 |]
Utilizzo di una libreria portabile in un'applicazione Silverlight
Procedura: creare un'applicazione Silverlight che utilizza una libreria portabile F#
Dalla barra del menu, scegliere File, Aggiungi, quindi Nuovo progetto. Nella finestra di dialogo Aggiungi nuovo progetto espandere Visual C#, Silverlight e quindi selezionare Applicazione Silverlight. Verrà visualizzata la finestra di dialogo Nuova applicazione Silverlight.
Assicurarsi che sia selezionata la casella di controllo per Ospita l'applicazione Silverlight in un nuovo sito Web e assicurarsi di aver selezionato Progetto di applicazione Web ASP.NET nell'elenco a discesa, quindi selezionare il pulsante OK. Vengono creati due progetti:un progetto dispone del controllo Silverlight e l'altro è un'applicazione Web ASP.NET che ospita il controllo.
Aggiungere un riferimento al progetto del foglio di lavoro. Aprire il menu di scelta rapida per il nodo di riferimento del progetto Silverlight, quindi scegliere Aggiungi riferimento. Verrà visualizzata la gestione dei riferimenti. Espandere il nodo Soluzione e selezionare il progetto Spreadsheet e quindi selezionare il pulsante OK.
In questa fase, creare un modello di visualizzazione che descrive tutto quello che deve essere eseguito dall'interfaccia utente senza descrivere come viene visualizzato. Aprire il menu di scelta rapida per il nodo del progetto, scegliere Aggiungi e quindi selezionare Nuovo elemento. Aggiungere un file di codice, denominarlo ViewModel.cs e quindi incollare in esso il seguente codice:
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)); } } }
Nel progetto di controllo Silverlight, aprire MainPage.xaml che dichiara il layout dell'interfaccia utente per il foglio di calcolo principale. In MainPage.xaml, incollare il seguente codice XAML nell'elemento Grid esistente.
<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>
In MainPage.xaml.cs, aggiungere using SilverlightFrontEnd; all'elenco delle direttive using e quindi aggiungere i seguenti metodi alla 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; }
In App.xaml.ca, aggiungere le seguenti direttive using:
using SilverlightFrontEnd; using Portable.Samples.Spreadsheet;
Incollare il codice seguente nel gestore eventi Application_Startup:
var spreadsheet = new Spreadsheet(5, 5); var spreadsheetViewModel = new SpreadsheetViewModel(spreadsheet); var main = new MainPage(); main.DataContext = spreadsheetViewModel; this.RootVisual = main;
È possibile eseguire il test del front end di Silverlight avviando il progetto Silverlight direttamente o avviando l'applicazione Web ASP.NET che ospita il controllo Silverlight. Aprire il menu di scelta rapida per il nodo per uno dei progetti, quindi selezionare Imposta come progetto di avvio.
Utilizzo della libreria portabile in un'applicazione Windows Store
Procedura: creare un'applicazione Windows Store che utilizza una libreria portabile F#
In questa sezione, viene creata un'applicazione Windows Store che utilizza il codice del foglio di lavoro F# e il componente computazionale. Nella barra dei menu, scegliere File, Aggiungi, Nuovo progetto. Verrà visualizzata la finestra di dialogo Nuovo progetto. In Installato espandere Visual C#, Windows Store e quindi selezionare il modello Applicazione vuota. Denominare il progetto NetFrontEnd, quindi selezionare il pulsante OK. Se richiesto dalla licenza dello sviluppatore per creare le applicazioni Windows Store, immettere le credenziali. Se non si dispone delle credenziali, è possibile verificare come installarle qui.
Il progetto viene creato. Annotare la configurazione e i contenuti di questo progetto. Nei riferimenti predefiniti è incluso l'API.NET per le applicazioni Windows Store, che rappresentano il sottoinsieme di .NET Framework compatibile con le applicazioni Windows Store e l'assembly Windows che include le API per Windows Runtime e l'interfaccia utente per le applicazioni Windows Store. Sono stato create le sottocartelle Asset e Comune. Nella sottocartella Asset sono contenute diverse icone che vengono applicate alle applicazioni Windows Store e nella sottocartella Comune sono contenute le routine condivise che rappresentano i modelli per l'utilizzo da parte delle applicazioni Windows Store. Il modello di progetto predefinito ha inoltre creato i file App.xaml, BlankPage.xaml e i relativi code-behind C#, App.xaml.cs e BlankPage.xaml.cs App.xaml descrive l'applicazione generale e BlankPage.xaml descrive la superficie dell'interfaccia utente definita. Infine, nei file con estensione pfx e appxmainfest sono supportati i modelli di sicurezza e distribuzione per le applicazioni Windows Store.
Aggiungere un riferimento al progetto del foglio di lavoro aprendo il menu di scelta rapida per il nodo di riferimento del progetto Silverlight e selezionando Aggiungi riferimento. In Gestione riferimento, espandere il nodo Soluzione e selezionare il progetto del fogli di lavoro e quindi selezionare il pulsante OK.
È necessario un codice già utilizzato nel progetto Silverlight per supportare il codice per l'interfaccia utente dell'applicazione Windows Store. Questo codice si trova in ViewModels.cs. Aprire il menu di scelta rapida per il nodo del progetto per NewFrontEnd, scegliere Aggiungi e quindi selezionare Nuovo elemento. Aggiungere un file di codice C# e denominarlo ViewModels.cs. Incollare il codice da ViewModels.cs nel progetto Silverlight e quindi modificare il blocco di direttive using in alto a questo file. Eliminare System.Windows utilizzato per l'interfaccia utente Silverlight e aggiungere Windows.UI.Xaml and Windows.Foundation.Collections utilizzati per l'interfaccia utente dell'applicazione Windows Store. Le interfacce utente Silverlight e Windows Store si basano su WPF in modo che siano compatibili tra di loro. Il blocco aggiornato di direttive using deve somigliare all'esempio seguente:
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;
Inoltre, modificare lo spazio dei nomi in ViewModels.cs da SilverlightFrontEnd a NewFrontEnd.
È possibile riutilizzare il resto del codice in ViewModels.cs, ma alcuni tipi, come Visibility, sono ora le versioni per le applicazioni Windows Store invece di Silverlight.
In questa applicazione Windows Store, il file di codice App.xaml.cs deve avere codici di avvio simili a quelli visualizzati nel gestore di eventi Application_Startup per l'applicazione Silverlight. In un'applicazione Windows Store questo codice viene visualizzato nel gestore di eventi OnLaunched della classe delle applicazioni. Aggiungere il codice seguente al gestore eventi OnLaunched in App.xaml.cs:
var spreadsheet = new Spreadsheet(5, 5); var spreadsheetViewModel = new SpreadSheetViewModel(spreadsheet);
Aggiungere una direttiva using per il codice del foglio di lavoro.
using Portable.Samples.Spreadsheet;
In App.xaml.ca , OnLaunched contiene il codice che specifica quale pagina caricare. Verrà aggiunta una pagina che si desidera venga caricata dall'applicazione quando viene avviata da un utente. Modificare il codice in OnLaunched per spostarsi nella prima pagina come illustrato nel seguente esempio:
// Create a frame, and navigate to the first page. var rootFrame = new Frame(); rootFrame.Navigate(typeof(ItemsPage1), spreadsheetViewModel);
È possibile eliminare il file BlankPage1.xaml e il relativo file code-behind poiché non sono utilizzati in questo esempio.
Aprire il menu di scelta rapida per il nodo del progetto per NewFrontEnd, scegliere Aggiungi e quindi selezionare Nuovo elemento. Aggiungere una pagina Elementi e conservare il nome predefinito, ItemsPage1.xaml. In questa fase vengono aggiunti il file ItemsPage1.xaml e il relativo file code-behind, ItemsPage1.xaml.cs, al progetto. ItemsPage1.xaml inizia con una tag principale di common:LayoutAwarePage con molti attributi, come mostrato nel seguente codice 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">
L'interfaccia utente per l'applicazione Windows Store è identica a quella per l'applicazione Silverlight creata e il formato XAML è lo stesso in questo caso. È quindi possibile riutilizzare XAML da MainPage.xaml nel progetto Silverlight per ItemsPage1.xaml nell'interfaccia utente per l'applicazione Windows Store.
Copiare il codice nell'elemento Grid di livello superiore di MainPage.xaml per il progetto Silverlight e incollarlo nell'elemento Grid di livello superiore in ItemsPage1.xaml nel progetto per l'interfaccia utente dell'applicazione Windows Store. Quando si incolla il codice, è possibile sovrascrivere i contenuti esistenti dell'elemento Grid. Modificare l'attributo di sfondo nell'elemento Grid in "Bianco" e sostituire MouseLeftButtonDown con PointerPressed.
Il nome di questo evento è diverso nelle applicazioni Silverlight e nelle applicazioni Windows Store.
In ItemsPage.xaml.cs, impostare la proprietà DataContext modificando il metodo OnNavigatedTo.
protected override void OnNavigatedTo(NavigationEventArgs e) { this.DataContext = e.Parameter; }
Copiare il seguente codice del gestore eventi e incollarlo nella classe ItemsPage1: OnLostFocus, OnKeyUp, EditValue, OnPointerPressed e 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; }
Modificare il progetto di avvio nel progetto per l'applicazione Windows Store. Aprire il menu di scelta rapida per il nodo di progetto NewFrontEnd, selezionare Imposta come progetto di avvio e quindi selezionare il pulsante F5 per eseguire il progetto.
Creazione di una libreria portabile in C# che utilizza F#
Nell'esempio precedente il codice viene duplicato poiché il codice ViewModels.cs viene visualizzato in più progetti. In questa sezione viene creato un progetto della libreria portabile C# per contenere questo codice. In alcuni casi, è necessario aggiungere informazioni al file di configurazione di un'applicazione quando utilizza le librerie portabili che utilizzano F#. In questo caso, un'applicazione desktop destinata alla versione desktop di .NET Framework 4.5 fa riferimento a una libreria portabile C# che, a sua volta, fa riferimento a una libreria portabile F#. In tal caso, è necessario aggiungere un reindirizzamento dell'associazione al file app.config dell'applicazione principale. È necessario aggiungere questo reindirizzamento poiché viene caricata solo una versione della libreria FSharp.Core, ma le librerie portabili fanno riferimento alla versione portabile di .NET. Tutte le chiamate alle versioni portabili .NET delle funzioni FSharp.Core devono essere reindirizzate alla versione singola di FSharp.Core caricata in un'applicazione desktop. I reindirizzamenti dell'associazione sono necessari solo nelle applicazioni desktop, poiché gli ambienti di runtime per le applicazioni Silverlight 5 e Windows Store utilizzano la versione portabile .NET di FSharp.Core e non la versione desktop completa.
Procedura: creare un'applicazione desktop che fa riferimento a una libreria portabile che utilizza F#
Nella barra dei menu, scegliere File, Aggiungi, Nuovo progetto. In Installato espandere il nodo Visual C#, scegliere il modello di progetto Libreria portabile .NET, quindi nominare il progetto ViewModels.
È necessario impostare le destinazioni per questa libreria portabile .NET affinché corrisponda alla libreria portabile F# alla quale verrà aggiunto un riferimento. In caso contrario, un messaggio di errore informerà della non corrispondenza. Nel menu di scelta rapida per il progetto ViewModels, scegliere Proprietà. Nella scheda Libreria, modificare le destinazioni per questa libreria portabile affinché corrispondano alle applicazioni .NET Framework 4.5, Silverlight 5 e Windows Store.
Nel menu di scelta rapida del nodo Riferimenti , scegliere Aggiungi riferimento. In Soluzione selezionare la casella di controllo accanto al foglio di lavoro.
Copiare il codice per ViewModels.cs da un altro progetto e incollarlo nel file di codice per il progetto ViewModels.
Apportare le seguenti modifiche che rendono il codice in ViewModels completamente indipendente dalla piattaforma dell'interfaccia utente:
Eliminare le direttive using per System.Windows, System.Windows.Input, Windows.Foundation.Collections e Windows.UI.Xaml, se presenti.
Modificare lo spazio dei nomi in ViewModels.
Eliminare la proprietà TooltipVisibility. In questa proprietà viene utilizzata la visibilità che è un oggetto dipendente dalla piattaforma.
Nella barra dei menu, scegliere File, Aggiungi, Nuovo progetto. In Installato espandere il nodo Visual C#, quindi scegliere il modello di progetto Applicazione WPF. Denominare il nuovo progetto Desktop, quindi selezionare il pulsante OK.
Aprire il menu di scelta rapida per il nodo Riferimenti nel progetto Desktop, quindi scegliere Aggiungi riferimento. In Soluzione selezionare i progetti Spreadsheet e ViewModels.
Aprire il file app.config per l'applicazione WPF e quindi aggiungere le seguenti linee di codice. In questo codice vengono configurati i reindirizzamenti dell'associazione appropriati che vengono applicati quando un'applicazione desktop destinata a .NET Framework 4.5 fa riferimento alla libreria portabile .NET che utilizza F#. Le librerie portabili .NET utilizzano la versione 2.3.5.0 della libreria FSharp.Core e le applicazioni desktop .NET Framework 4.5 utilizzano la versione 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>
È ora necessario aggiungere un riferimento alla versione portabile della libreria F# Core. Questo riferimento è necessario quando si dispone di un'applicazione che utilizza una libreria portabile che fa riferimento a una libreria portabile F#.
Aprire il menu di scelta rapida per il nodo Riferimenti nel progetto Desktop, quindi scegliere Aggiungi riferimento. Selezionare Sfoglia e quindi navigare in Reference Assemblies\Microsoft\FSharp\3.0\Runtime\.NETPortable\FSharp.Core.dll nella cartella Programmi in cui è installato Visual Studio.
Nel progetto Desktop, aggiungere le direttive using per ViewModels.cs e Portable.Samples.Spreadsheet a App.xaml.cs e MainWindow.xaml.cs.
using ViewModels; using Portable.Samples.Spreadsheet;
Aprire il file MainWindow.xaml e quindi modificare l'attributo del titolo della classe Window in Foglio di lavoro.
Copiare il codice nell'elemento Grid di MainPage.xaml nel progetto Silverlight e incollare quel codice nell'elemento Grid di MainWindow.xaml nel progetto Desktop.
Copiare il codice di gestione eventi in MainPage.xaml.cs dal progetto Silverlight e incollarlo in MainWindow.xaml.cs nel progetto 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; }
Aggiungere il codice di avvio del foglio di lavoro al costruttore MainWindow nel file MainWindow.xaml.cs e sostituire i riferimenti a MainPage con i riferimenti a MainWindow.
public MainWindow() { var spreadsheet = new Spreadsheet(5, 5); var spreadsheetViewModel = new SpreadsheetViewModel(spreadsheet); this.DataContext = spreadsheetViewModel; InitializeComponent(); }
Aprire il menu di scelta rapida per il progetto Desktop e quindi selezionare Imposta come progetto di avvio.
Selezionare il tasto F5 per compilare l'applicazione e quindi eseguire il debug.
Passaggi successivi
Come alternativa, è possibile modificare i progetti per le applicazioni Windows Store e Silverlight in modo da utilizzare la nuova libreria portabile ViewModels.
Continuare ad apprendere ciò che riguarda le applicazioni Windows Store su Centro per sviluppatori windows.
Vedere anche
Concetti
Sviluppo multipiattaforma con la libreria di classi portabile