首页 > 解决方案 > 使用 XML 设置序列化实现消息抑制系统

问题描述

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

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

消息类定义为:

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 类。就像我说的,我需要存储每条消息的一些属性,以便我可以 WPF 绑定到设置窗口中的消息列表。现在,AppSettings 引用了 MSG 类消息列表:

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 窗口中,我可以绑定到该messages属性,并选择将其显示title为 TextBlock、doNotShowAgainCheckBox 和rememberedResultComboBox。我还没有做那一点,但我认为对于当前的应用程序架构应该非常简单。

问题是 XML 的序列化和反序列化(参见AppSettings类的最后两个函数)。由于此类存储对整个消息列表的引用,该列表不仅具有title,doNotShowAgainrememberedResult,而且还具有消息内容及其其他属性,这确实使 XML 文件变得混乱。

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

我不能成为第一个实现这一点的人,所以也许有这样的约定。有什么建议么?

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

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

因此,虽然这有效(将这些绑定messages到 WPF 窗口也有效),但 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>

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

标签: wpfvb.netuser-interfacexml-serialization

解决方案


看来您唯一的问题是 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()

推荐阅读