【问题标题】:How should I use Interfaces for connecting the core of my application to the UI?我应该如何使用接口将应用程序的核心连接到 UI?
【发布时间】:2021-09-30 18:33:51
【问题描述】:

我正在用 VB.NET 编写一个应用程序,其中各种计算在 Core 类中执行。它们由 MainWindow 类 (UI) 中的用户输入调用。计算完成后,它们会再次显示在主窗口中。

为了避免意大利面条式的代码和不小心使 Core 依赖于 UI,我的理解是我应该使用接口。我想我了解接口的工作原理,但我不确定如何正确实现它以实现核心和 UI 之间的双向通信。

下面是一个非常简单的例子。

核心类:

Public Module Core
    Public UI As UIinterface
        
    Sub Init()
        UI = New MainWindow
    End Sub
        
    Sub RunCalculations()
        Dim calculationResult As String = 5 + 5         
        UI.DisplayResults(calculationResult)
    End Sub

End Module

UIInterface接口:

Public Interface UIInterface
    Sub DisplayResults(results as String)
End Interface

还有我的 MainWindow 类:

Class MainWindow
    Implements UIInterface
    
    Private Sub DisplayResults(results as String) Implements UIInterface.DisplayResults
        label1.text = results
    End Sub
    
    Private Sub RunButton_Click(sender As Object, e As RoutedEventArgs)
        Core.RunCalculations() 'This is probably wrong
    End Sub 
End Class

如您所见,我不明白如何将按钮点击传递给核心。据我了解,这应该以某种方式通过 UIInterface 完成,但我无法弄清楚,而且我似乎无法在任何地方找到任何相关示例。我在接口使用方面发现的只是单向通信。

有人可以建议吗?如果我对Interfaces 的理解有缺陷?如果是这样,将 Core 与 UI 尽可能分开的正确实现是什么,以便以后在不更改 Core 中的任何内容的情况下轻松切换到另一个 UI 实现?

编辑:我正在为我的 UI 使用 WPF。

【问题讨论】:

  • 您的 UI 技术是什么(即 WPF、WinForms 或其他)?如何构建它的答案将取决于技术。
  • @Craig WPF。我编辑了我的帖子以包含其他人的此信息。
  • 您可能需要一些代码隐藏来将DataContext 与视图相关联,但除此之外,您应该使用针对视图模型属性的绑定来处理所有事情。您的 ViewModel 将公开 ICommand 类型的属性,例如 RunCommand,然后在您的视图中,按钮的 xaml 会将单击绑定到“RunCommand”属性。 WPF 将发挥连接一切的魔力,并且它会正常工作。只要确保 ViewModel 实现INotifyPropertyChanged 并通知更改,界面就会响应更改。
  • 重新“使用事件引发调用污染模型代码”,您将不得不让 UI 知道它显示的数据已更改。你还打算怎么做? INotifyPropertyChanged 不会强迫您在任何地方都这样做,您确实可以选择聚合成一个大“刷新”,并理解您的大“刷新”需要知道可能需要的所有内容的名称刷新(增加耦合)。请注意,<CallerMemberName> 可以轻松编写一个可以不带参数调用的“Raise”例程。

标签: vb.net user-interface interface


【解决方案1】:

这是一个基于您提供的代码的简化 WPF 示例。

<!-- Details elided, but if you add a new window in a WPF project,
you'll start out with what you need -->
<Window x:Class="MainWindow"
        Title="MainWindow">
    <Grid>
        <Button Content="Run Calculations" Command="{Binding RunCalculationsCommand}" />
        <TextBlock Text="{Binding Results}" />
    </Grid>
</Window>
Public Class Model
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    'In C#, this can be made into an extension, but I don't think 
    'the VB event model gives us granular enough access to do this.
    Private Sub RaisePropertyChanged(<CallerMemberName> Optional ByVal propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    'Boilerplate nature of visible properties can be annoying; I don't
    'have any suggested workarounds beyond generated code.
    Private _Results As String
    Public Property Results As String
        Get
            Return _Results
        End Get
        Set(value As String)
            _Results = value
            RaisePropertyChanged()
        End Set
    End Property

    'ActionCommand implementation left as an exercise for the reader.
    Public ReadOnly Property RunCalculationsCommand As ICommand = 
        New ActionCommand(AddressOf RunCalculations)

    Public Sub RunCalculations()
        Dim calculationResult As String = (5 + 5).ToString()
        Results = calculationResult
    End Sub
End Class

这需要的最后一件事是将MainWindowDataContext 设置为Model 的实例。对于这个例子,在构造函数中分配一个新实例可能是最直接的(尝试在主窗口 ctor 上放置一个参数,我认为会导致框架出现问题)。

【讨论】:

  • 谢谢你,@Craig。这比我迄今为止发现的任何东西都更容易理解。据我了解,这不是 MVVM,因为没有 ViewModel - View 直接与模型联系(我假设这是 RunCalculations 命令的用途,因为 WPF 不能绑定到常规 Sub,只有 ICommand,对吗?)。与 MVVM 相比,这种方法有什么问题吗?这对我来说似乎更具可读性和更少的困惑。
  • @JustinasRubinovas 你说得对,在这种情况下我确实省略了视图模型。这种方法的主要问题是模型中有面向显示的逻辑,这可能会给单元测试(以及业务逻辑的一般清洁度)带来问题。没错,WPF 只能绑定到命令 --- 请注意,该命令具有一些附加功能,因为有一个 CanExecute 负责启用/禁用按钮。
  • 如果你应该在两者之间添加一个视图模型,它可能会有点乏味,因为它会有一堆只存在链接到模型的传递属性。并且模型仍然需要发出更改通知,尽管至少视图模型可以集中捕获并重新抛出事件(请记住,WPF 只对从 DataContext 发出的通知感兴趣,因此事件会需要以视图模型作为发送者重新发布)。
猜你喜欢
  • 2020-07-31
  • 1970-01-01
  • 2016-02-03
  • 2017-10-13
  • 2017-12-25
  • 2021-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多