Rebol VID MVC:如果Rebol不支持自定义事件,如何从模型更新多个视图?

时间:2011-06-19 10:50:33

标签: model-view-controller rebol

假设我有一个同时具有2个VID形式的模型/控制器。如果Rebol不支持自定义事件,我将如何更新指向同一模型的2个视图?

1 个答案:

答案 0 :(得分:4)

我继续前进并乐于实现属性编辑器MVC。

此示例允许您直接从GUI动态创建模型和视图,因此非常适合显示系统的运行。

当多个视图编辑相同的数据时,您会看到它们保持同步。多个模型可能各有多个视图。

这只是一个例子,展示了在REBOL中构建MVC模式是多么容易。 REBOL中的许多结构在精神上已经是MVC,即使它们没有明确地以这样的方式进行营销。

enter image description here

rebol [
    title: "MVC pattern example"
    purpose: {
        shows an example of a raw MVC pattern in REBOL
        the views can create new models and new views, showing interaction
        between separate models, views  and the controler.
    }
]

model!: context [
    data: none

    views: []

    modify: func [label value][
        set in data label value
    ]


    propagate: func [
        /only label 
        /local view
    ][
        foreach view views [
            either only [
                view/refresh/only label
            ][
                view/refresh
            ]
        ]
    ]
]


view!: context [
    controller: none  ; our controller

    model: none ; our model

    label: none ; what label in data does this view manipulate?

    gui:  [
        across
        space 2x10
        style separator box 275x3 edge [size: 1x1 effect: 'ibevel color: (white * .75)]
    ]

    lbl: none   ; gui face
    fld: none   ; gui face

    refresh: func [/only label][
        ; GENERATE the gui if its not been built for this view yet.
        if block? gui [
            gui: copy/deep gui
            ; add a button for each item of data in the model, clicking on them changes
            ; what the field edits.
            foreach item words-of model/data [
                append gui compose/deep bind/copy [
                    btn (to-string item) [
                        print "^/---"
                        label: (to-lit-word item) 
                        probe label
                        refresh
                    ]
                ] self
            ]
            ; we must bind because the block is being used in new objects created dynamically.
            ; if we don't bind the blocks, they stay bound to the class... important detail.
            append gui copy/deep bind/copy [
                return
                separator
                return
                lbl: h1 200 (to-string label)
                return
                fld: field [controller/modify model label face/text]
                btn "randomize" [controller/randomize model label]
                return
                pad 0x10
                separator
                return
                pad 160x0
                btn "new view" [controller/new-view (model)]
                btn "new model" [controller/new-model]
                btn "close" [unview/only gui]
            ] self
            gui: view/new layout gui
        ]

        ; refresh the gui, when its already built (including on first view)
        if any [
            none? label
            label = self/label
        ] [
            probe model/data
            probe self/label
            fld/text: copy get in model/data self/label
            lbl/text: copy to-string self/label
            show fld
            show lbl
        ]
    ]
]


controller!: context [
    models: []

    ; this just describes how the models should be built, 
    ; it could be a hard-coded in new-model()
    model-data: [sid: "0" name: "unknown" occupation: "unknown"]

    new-model: func [/local model view prev-model prev-view][
        unless empty? models [prev-model: last models]

        append models model: make model! [data: context model-data ]
        view: new-view model

        if prev-model [
            ; tweak window position which is a bit screwed up in rebol
            prev-view: last prev-model/views
            view/gui/offset/x: view/gui/offset/x  + system/view/no-resize-border/x 
            view/gui/offset/y: prev-view/gui/offset/y + prev-view/gui/size/y + system/view/no-resize-border/y 8
            show view/gui 
        ]

        model
    ]

    new-view: func [model /local view prev-view][
        probe model/data
        if not empty? model/views [
            probe length? model/views
            prev-view: last model/views
        ]
        append model/views view: make view! compose [
            model: (model) 
            label: (to-lit-word first words-of model/data) 
            controller: (self) ; here self is the controller, since we are composing 
                               ; the value within a controller function
        ]

        view/refresh
        if prev-view [
            ; tweak window position which is a bit screwed up in rebol
            view/gui/offset/x: prev-view/gui/offset/x + prev-view/gui/size/x     + system/view/no-resize-border/x 
            view/gui/offset/y: prev-view/gui/offset/y - system/view/title-size/y - system/view/no-resize-border/y - 2
            show view/gui 
        ]

        view
    ]


    ; general case "set" operation
    modify: func [model label value][
        model/modify label value
        model/propagate/only label
    ]

    ; just an example controler method
    randomize: func [
        model
        label
    ][
        modify model label random copy get in model/data label
    ]
]





ids: make controller! []

ids/new-model

do-events