实现设置序列化为 XML 的消息抑制系统

Implementing a message suppression system with settings serialization to XML

在我的应用程序中,我需要一个系统,其中显示给用户的某些消息具有“不再显示”复选框。在“设置”菜单中,我希望有一个“已关闭的消息”部分,其中包含消息名称和每个消息的复选框,以便用户可以在需要时取消关闭它们。如果消息被关闭,用户在消息对话框中选择的最后一个选项应该成为默认选项。

此外,设置应保存到 XML 文件中 - 包括消息抑制状态和默认选择。我目前的解决方案很粗糙,想知道有没有更好的方法。

消息class定义为:

Public Class Message
    Public Property title As String
    Public Property content As String
    Public Property buttons As Utilities.Enums.messageButtonTypes 'Ok/Cancel, Yes/No, etc.
    Public Property allowDoNotShowAgain As Boolean = False        'whether the message is dismissable
    Public Property doNotShowAgain As Boolean = False             'the actual dismiss state
    Public Property result As Boolean
    Public Property rememberedResult As Boolean                   'last user choice if the message is dismissed
End Class

特定消息在 MSG 模块中初始化:

Module Msg

    'This message is not dismissable
    Public connectionNotEstablished As New Message() With {
        .title = "Connection not established",
        .content = "Connection not established. Please check if the host application is running.",
        .buttons = Utilities.Enums.messageButtonTypes.Ok
        }

    'This message is dismissable
    Public noResultsPlotsDefined As New Message() With {
        .title = "No plots defined",
        .content = "You have not defined any plots. Would you like to run the study anyway?",
        .buttons = Utilities.Enums.messageButtonTypes.YesNo,
        .allowDoNotShowAgain = True
        }
        
    'Just a list to store references to all the messages for binding, looping, etc.
    Public allMessages As New List(Of Message) From {
        connectionNotEstablished,
        noResultsPlotsDefined
        }

    Public Function ShowMessage(message As Message) As Boolean
        If message.doNotShowAgain Then message.result = message.rememberedResult : Return message.rememberedResult 'If message is dismissed, return the last user choice

        Dim messageDialog As New MessageDialog(message.title, message.content, message.buttons, message.allowDoNotShowAgain, message.defaultButtonCustomCaption, message.cancelButtonCustomCaption)
        message.result = messageDialog.ShowDialog()
        message.doNotShowAgain = messageDialog.doNotShowAgain
        If message.doNotShowAgain Then message.rememberedResult = message.result
        Return message.result
    End Function
End Module

在各种函数中调用特定的消息,例如,像这样:

Msg.ShowMessage(connectioNotEstablished)

到目前为止,这非常简单 - 在构建我的应用程序时使用起来非常方便。现在,我不确定的一点 - AppSettings class。正如我所说,我需要存储每条消息的一些属性,以便我可以 WPF 绑定到设置 window 中的消息列表。现在,AppSettings 引用了 MSG class 消息列表:

Public Class AppSettings

    Public Property messages As List(Of Message) = Msg.allMessages 

    Public Sub SaveToDefaultPath()
        Save(Constants.Paths.settingsFilePath)
    End Sub    

    Private Sub Save(ByVal filename As String)
        Using sw As StreamWriter = New StreamWriter(filename)
            Dim xmls As XmlSerializer = New XmlSerializer(GetType(AppSettings))
            xmls.Serialize(sw, Me)
        End Using
    End Sub

    Private Function Read(ByVal filename As String) As AppSettings
        Using sw As StreamReader = New StreamReader(filename)
            Dim xmls As XmlSerializer = New XmlSerializer(GetType(AppSettings))
            Return TryCast(xmls.Deserialize(sw), AppSettings)
        End Using
    End Function
End Class

在我的设置 WPF window 中,然后我可以绑定到 messages 属性,并选择将 title 显示为 TextBlock,doNotShowAgain作为复选框,rememberedResult 作为组合框。我还没有这样做,但我认为对于当前的应用程序体系结构来说应该非常简单。

问题出在 XML 的序列化和反序列化(参见 AppSettings class 的最后两个函数)。因为这个 class 存储了对整个消息列表的引用,它不仅有 titledoNotShowAgainrememberedResult,还有消息内容和它的其他属性,这真的XML 文件混乱。

我不知道如何解决这个问题。也许我可以只将每条消息所需的变量存储在 AppSettings 中,但这需要某种双向转换器或其他东西。至此,我开始怀疑这是否真的是实现我所需的正确方法。

我不是第一个实现它的人,所以也许有这样的约定。有什么建议吗?

编辑:在等待答案时,我已经成功实现了将消息关闭状态保存到 XML - 不幸的是,它保存了整个 message class 数据,而不仅仅是 titledoNotShowAgainrememberedResult。为了完成这项工作,我只需要做一个小改动——AppSettings 中的 属性 messages 被声明为数组而不是列表,因此 XML反序列化器不会将消息附加到该列表,而是将其作为一个整体替换。

Public Class AppSettings
    Public Property Messages() As Message() = Msg.allMessages.ToArray()
    ...
End Class

因此,虽然这有效(将这些 messages 绑定到 WPF window 也有效),但 XML 文件中每条消息都包含不必要的值,例如:

<Message>
  <title>No plots defined</title>
  <content>You have not defined any plots. Would you like to run the study anyway?</content>
  <buttons>YesNo</buttons>
  <allowDoNotShowAgain>true</allowDoNotShowAgain>
  <doNotShowAgain>false</doNotShowAgain>
  <result>false</result>
  <rememberedResult>false</rememberedResult>
</Message>

但是对于这个用例,XML 文件中的每条消息只有这个位就足够了:

<Message>
  <title>No plots defined</title>
  <doNotShowAgain>false</doNotShowAgain>
  <rememberedResult>false</rememberedResult>
</Message>

所以我的问题仍然存在 - 这里最好的解决方案是什么?我还在球场上吗?

看来您唯一的问题是 Xml 文件混乱。因此,您可以使用 <XmlIgnore>

告诉序列化程序忽略某些属性
Public Class Message
    Public Property title As String
    <XmlIgnore>
    Public Property content As String
    <XmlIgnore>
    Public Property buttons As Utilities.Enums.messageButtonTypes 'Ok/Cancel, Yes/No, etc.
    <XmlIgnore>
    Public Property allowDoNotShowAgain As Boolean = False 'whether the message is dismissable
    Public Property doNotShowAgain As Boolean = False 'the actual dismiss state
    <XmlIgnore>
    Public Property result As Boolean
    Public Property rememberedResult As Boolean 'last user choice if the message is dismissed
End Class

序列化程序既不会序列化也不会反序列化这些属性。

现在您还可以序列化由 linq 查询定义的消息子集,就像这样

<XmlIgnore>
Public Property messages As List(Of Message) = Msg.allMessages 
<XmlElement("messages")>
Public Property messagesAllowDoNotShowAgain As List(Of Message) = Msg.allMessages.Where(Function(m) m.allowDoNotShowAgain).ToList()