F#
F # में WPF का परिचय
खोज…
परिचय
यह विषय दिखाता है कि WPF एप्लिकेशन में कार्यात्मक प्रोग्रामिंग का फायदा कैसे उठाया जाए। पहला उदाहरण Māris Krivtežs (संदर्भ निचले भाग में अनुभाग टिप्पणियां) द्वारा एक पोस्ट से आता है। इस परियोजना को दोबारा शुरू करने का कारण दुगना है:
1 \
2 \ _ Gjallarhorn कार्यान्वयन के लिए एक आसान संक्रमण के लिए जैसा दिखता है।
टिप्पणियों
लाइब्रेरी डेमो प्रोजेक्ट्स @GitHub
- FSharp.ViewModule ( FsXaml के तहत)
- Gjallarhorn (रेफरी के नमूने)
मैरिस क्रिवेत्स ने इस विषय पर दो महान पोस्ट लिखे:
- एफ # एक्सएएमएल आवेदन - एमवीवीएम बनाम एमवीसी जहां दोनों दृष्टिकोणों के पेशेवरों और विपक्षों पर प्रकाश डाला गया है।
मुझे लगता है कि इन XAML एप्लिकेशन शैलियों में से कोई भी कार्यात्मक प्रोग्रामिंग से बहुत लाभ नहीं उठाता है। मैं कल्पना करता हूं कि आदर्श अनुप्रयोग में वह दृश्य शामिल होगा जो घटनाओं और घटनाओं को वर्तमान दृश्य स्थिति को उत्पन्न करता है। सभी एप्लिकेशन लॉजिक को इवेंट्स और व्यू मॉडल को फ़िल्टर और मैनिपुलेट करके हैंडल किया जाना चाहिए, और आउटपुट में इसे एक नया व्यू मॉडल तैयार करना चाहिए जो कि व्यू पर वापस आ जाए।
- एफ # एक्सएएमएल - घटना ने उपरोक्त विषय में संशोधित एमवीवीएम के रूप में चलाया ।
FSharp.ViewModule
हमारे डेमो ऐप में एक स्कोरबोर्ड होता है। स्कोर मॉडल एक अपरिवर्तनीय रिकॉर्ड है। स्कोरबोर्ड ईवेंट यूनियन प्रकार में निहित हैं।
namespace Score.Model
type Score = { ScoreA: int ; ScoreB: int }
type ScoringEvent = IncA | DecA | IncB | DecB | NewGame
घटनाओं को सुनने और तदनुसार दृश्य मॉडल को अपडेट करने से परिवर्तन का प्रचार किया जाता है। ओओपी के रूप में सदस्यों को मॉडल प्रकार में जोड़ने के बजाय, हम अनुमत कार्यों को होस्ट करने के लिए एक अलग मॉड्यूल घोषित करते हैं।
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Score =
let zero = {ScoreA = 0; ScoreB = 0}
let update score event =
match event with
| IncA -> {score with ScoreA = score.ScoreA + 1}
| DecA -> {score with ScoreA = max (score.ScoreA - 1) 0}
| IncB -> {score with ScoreB = score.ScoreB + 1}
| DecB -> {score with ScoreB = max (score.ScoreB - 1) 0}
| NewGame -> zero
हमारा दृश्य मॉडल EventViewModelBase<'a>
, जिसमें IObservable<'a>
प्रकार की एक प्रॉपर्टी EventStream
है। इस मामले में हम जिन घटनाओं की सदस्यता लेना चाहते हैं, वे ScoringEvent
।
नियंत्रक एक कार्यात्मक तरीके से घटनाओं को संभालता है। इसका हस्ताक्षर Score -> ScoringEvent -> Score
हमें दिखाता है कि जब भी कोई घटना होती है, तो मॉडल का वर्तमान मूल्य एक नए मूल्य में बदल जाता है। यह हमारे मॉडल को शुद्ध रहने की अनुमति देता है, हालांकि हमारा दृश्य मॉडल नहीं है।
एक eventHandler
दृश्य की स्थिति को बदलने के लिए प्रभारी है। EventViewModelBase<'a>
से EventViewModelBase<'a>
हम EventValueCommand
और EventValueCommandChecked
का उपयोग EventValueCommand
घटनाओं को आदेशों तक ले जा सकते हैं।
namespace Score.ViewModel
open Score.Model
open FSharp.ViewModule
type MainViewModel(controller : Score -> ScoringEvent -> Score) as self =
inherit EventViewModelBase<ScoringEvent>()
let score = self.Factory.Backing(<@ self.Score @>, Score.zero)
let eventHandler ev = score.Value <- controller score.Value ev
do
self.EventStream
|> Observable.add eventHandler
member this.IncA = this.Factory.EventValueCommand(IncA)
member this.DecA = this.Factory.EventValueCommandChecked(DecA, (fun _ -> this.Score.ScoreA > 0), [ <@@ this.Score @@> ])
member this.IncB = this.Factory.EventValueCommand(IncB)
member this.DecB = this.Factory.EventValueCommandChecked(DecB, (fun _ -> this.Score.ScoreB > 0), [ <@@ this.Score @@> ])
member this.NewGame = this.Factory.EventValueCommand(NewGame)
member __.Score = score.Value
फ़ाइल (* .xaml.fs) के पीछे का कोड वह है जहाँ सब कुछ एक साथ रखा जाता है, यानी MainViewModel
में अपडेट फंक्शन ( controller
) इंजेक्ट किया MainViewModel
।
namespace Score.Views
open FsXaml
type MainView = XAML<"MainWindow.xaml">
type CompositionRoot() =
member __.ViewModel = Score.ViewModel.MainViewModel(Score.Model.Score.update)
प्रकार CompositionRoot
एक आवरण है कि XAML फाइल में संदर्भित के रूप में कार्य करता है।
<Window.Resources>
<ResourceDictionary>
<local:CompositionRoot x:Key="CompositionRoot"/>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource CompositionRoot}" Path="ViewModel" />
</Window.DataContext>
मैं XAML फ़ाइल में किसी भी गहराई से गोता नहीं लगाऊंगा क्योंकि यह बुनियादी WPF सामान है, पूरे प्रोजेक्ट को GitHub पर पाया जा सकता है ।
Gjallarhorn
Gjallarhorn लाइब्रेरी में मुख्य प्रकार IObservable<'a>
को कार्यान्वित करते हैं, जो कार्यान्वयन को परिचित बना देगा (FSharp.ViewModule उदाहरण से EventStream
संपत्ति याद रखें)। हमारे मॉडल का एकमात्र वास्तविक परिवर्तन अपडेट फ़ंक्शन के तर्कों का क्रम है। साथ ही, अब हम ईवेंट के बजाय संदेश शब्द का उपयोग करते हैं।
namespace ScoreLogic.Model
type Score = { ScoreA: int ; ScoreB: int }
type ScoreMessage = IncA | DecA | IncB | DecB | NewGame
// Module showing allowed operations
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Score =
let zero = {ScoreA = 0; ScoreB = 0}
let update msg score =
match msg with
| IncA -> {score with ScoreA = score.ScoreA + 1}
| DecA -> {score with ScoreA = max (score.ScoreA - 1) 0}
| IncB -> {score with ScoreB = score.ScoreB + 1}
| DecB -> {score with ScoreB = max (score.ScoreB - 1) 0}
| NewGame -> zero
Gjallarhorn के साथ UI बनाने के लिए, डेटा बाइंडिंग का समर्थन करने के लिए कक्षाएं बनाने के बजाय, हम एक Component
रूप में संदर्भित सरल फ़ंक्शन बनाते हैं। उनके निर्माता में पहला तर्क source
BindingSource
(Gjallarhorn.Bindable में परिभाषित) प्रकार का है, और मॉडल को देखने के लिए उपयोग किया जाता है, और घटनाओं से संदेशों में वापस दिखाई देता है।
namespace ScoreLogic.Model
open Gjallarhorn
open Gjallarhorn.Bindable
module Program =
// Create binding for entire application.
let scoreComponent source (model : ISignal<Score>) =
// Bind the score to the view
model |> Binding.toView source "Score"
[
// Create commands that turn into ScoreMessages
source |> Binding.createMessage "NewGame" NewGame
source |> Binding.createMessage "IncA" IncA
source |> Binding.createMessage "DecA" DecA
source |> Binding.createMessage "IncB" IncB
source |> Binding.createMessage "DecB" DecB
]
वर्तमान कार्यान्वयन FSharp.ViewModule संस्करण से भिन्न है, जिसमें दो आदेशों में CanExecute ठीक से लागू नहीं है। एप्लिकेशन की प्लंबिंग को भी सूचीबद्ध करें।
namespace ScoreLogic.Model
open Gjallarhorn
open Gjallarhorn.Bindable
module Program =
// Create binding for entire application.
let scoreComponent source (model : ISignal<Score>) =
let aScored = Mutable.create false
let bScored = Mutable.create false
// Bind the score itself to the view
model |> Binding.toView source "Score"
// Subscribe to changes of the score
model |> Signal.Subscription.create
(fun currentValue ->
aScored.Value <- currentValue.ScoreA > 0
bScored.Value <- currentValue.ScoreB > 0)
|> ignore
[
// Create commands that turn into ScoreMessages
source |> Binding.createMessage "NewGame" NewGame
source |> Binding.createMessage "IncA" IncA
source |> Binding.createMessageChecked "DecA" aScored DecA
source |> Binding.createMessage "IncB" IncB
source |> Binding.createMessageChecked "DecB" bScored DecB
]
let application =
// Create our score, wrapped in a mutable with an atomic update function
let score = new AsyncMutable<_>(Score.zero)
// Create our 3 functions for the application framework
// Start with the function to create our model (as an ISignal<'a>)
let createModel () : ISignal<_> = score :> _
// Create a function that updates our state given a message
// Note that we're just taking the message, passing it directly to our model's update function,
// then using that to update our core "Mutable" type.
let update (msg : ScoreMessage) : unit = Score.update msg |> score.Update |> ignore
// An init function that occurs once everything's created, but before it starts
let init () : unit = ()
// Start our application
Framework.application createModel init update scoreComponent
डिकॉउंड व्यू को सेट करने के साथ लेफ्ट, MainWindow
टाइप और लॉजिकल एप्लिकेशन को मिला कर।
namespace ScoreBoard.Views
open System
open FsXaml
open ScoreLogic.Model
// Create our Window
type MainWindow = XAML<"MainWindow.xaml">
module Main =
[<STAThread>]
[<EntryPoint>]
let main _ =
// Run using the WPF wrappers around the basic application framework
Gjallarhorn.Wpf.Framework.runApplication System.Windows.Application MainWindow Program.application
अतिरिक्त जानकारी और अधिक विस्तृत उदाहरण के लिए यह मुख्य अवधारणाओं को प्रस्तुत करता है, कृपया रीड कोपसे के पद को देखें। क्रिसमस ट्रीज़ परियोजना इस दृष्टिकोण के कुछ लाभों पर प्रकाश डालती है:
- प्रभावी रूप से हमें (मैन्युअल रूप से) मॉडल के कस्टम संग्रह में मॉडल की प्रतिलिपि बनाने, उन्हें प्रबंधित करने और मैन्युअल रूप से परिणामों से वापस मॉडल का निर्माण करने की आवश्यकता है।
- एक शुद्ध मॉडल को बनाए रखते हुए, संग्रह के भीतर अपडेट पारदर्शी तरीके से किए जाते हैं।
- तर्क और दृष्टिकोण को दो अलग-अलग परियोजनाओं द्वारा होस्ट किया जाता है, चिंताओं को अलग करने पर जोर दिया जाता है।