Formular una preguntaFormular una pregunta
 

RespondidaWPF Databinding kills serialization

  • domingo, 08 de noviembre de 2009 1:00GreatElectron Medallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuario
     
    I have a problem that when I databind a WPF control to a class, that class can no longer be serialized: If I try to serialize it, I get this exception:

    System.Runtime.Serialization.SerializationException was unhandled by user code
      Message="Type 'MS.Internal.Data.ValueChangedEventManager+ValueChangedRecord' in Assembly 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' is not marked as serializable."

    Anyone know how to avoid this? From the exception message it looks to me like the problem may be that the serialization is somehow picking up the event handler that WPF has stuck on the data object's relevent property_changed event.

    In more detail: The property concerned is called Primary, and to allow for databinding I've done the usual event stuff:

    Private _primary As Boolean

    Public Event PrimaryChanged As EventHandler

    Public Property Primary() As Boolean

          Get

                Return _primary

          End Get

          Set(ByVal value As Boolean)

                _primary = value

                RaiseEvent PrimaryChanged(Me, EventArgs.Empty)

          End Set

    End Property



    Then this XAML:

    <Button IsEnabled="{Binding Path=Primary, Mode=TwoWay}" ...



    In order to get the error, both of those bits of code need to be in place. If I remove either the PrimaryChangedEvent or the binding in the XAML, then the serialization works fine (but of course, then the button can't pick up changes to the Primary property).

    I'm not doing anything funny with serialization: The class is simply marked with the Serializable attribute, nothing else. I'm doing serialization because the class needs to be copied to a server using remoting in response to certain user actions.

Respuestas

  • lunes, 09 de noviembre de 2009 23:51Adam Sills Medallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuario
     RespondidaTiene código

    When you binary serialize a class, all of its private members are serialized. This includes any events on the class. To get around, you need to mark your event as NonSerialized except that attribute doesn't apply to events and so you instead need to apply it to a field.

    To get around this, you need to provide a custom addhandler and removehandler method for your event and keep a private delegate that you can mark with NonSerialized.

    The following code is a class that implementes INotifyPropertyChanged and implements its interface. It has a private PropertyChangedEventHandler field which is used to store the event subscriptions. Also note that the declaration of the event itself is marked "Custom" which allows you to handle the add/remove/invoke of the event manually. This allows you to keep the event non-serialized.

    <Serializable()> Public Class Class1
        Implements System.ComponentModel.INotifyPropertyChanged
    
        <NonSerialized()> Private propChangedEvent As System.ComponentModel.PropertyChangedEventHandler
    
        Public Custom Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
            AddHandler(ByVal value As System.ComponentModel.PropertyChangedEventHandler)
                propChangedEvent = [Delegate].Combine(propChangedEvent, value)
            End AddHandler
    
            RemoveHandler(ByVal value As System.ComponentModel.PropertyChangedEventHandler)
                propChangedEvent = [Delegate].Remove(propChangedEvent, value)
            End RemoveHandler
    
            RaiseEvent(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
                propChangedEvent(sender, e)
            End RaiseEvent
        End Event
    End Class
    
    • Marcado como respuestaGreatElectron viernes, 13 de noviembre de 2009 15:02
    •  

Todas las respuestas

  • lunes, 09 de noviembre de 2009 23:51Adam Sills Medallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuario
     RespondidaTiene código

    When you binary serialize a class, all of its private members are serialized. This includes any events on the class. To get around, you need to mark your event as NonSerialized except that attribute doesn't apply to events and so you instead need to apply it to a field.

    To get around this, you need to provide a custom addhandler and removehandler method for your event and keep a private delegate that you can mark with NonSerialized.

    The following code is a class that implementes INotifyPropertyChanged and implements its interface. It has a private PropertyChangedEventHandler field which is used to store the event subscriptions. Also note that the declaration of the event itself is marked "Custom" which allows you to handle the add/remove/invoke of the event manually. This allows you to keep the event non-serialized.

    <Serializable()> Public Class Class1
        Implements System.ComponentModel.INotifyPropertyChanged
    
        <NonSerialized()> Private propChangedEvent As System.ComponentModel.PropertyChangedEventHandler
    
        Public Custom Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
            AddHandler(ByVal value As System.ComponentModel.PropertyChangedEventHandler)
                propChangedEvent = [Delegate].Combine(propChangedEvent, value)
            End AddHandler
    
            RemoveHandler(ByVal value As System.ComponentModel.PropertyChangedEventHandler)
                propChangedEvent = [Delegate].Remove(propChangedEvent, value)
            End RemoveHandler
    
            RaiseEvent(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
                propChangedEvent(sender, e)
            End RaiseEvent
        End Event
    End Class
    
    • Marcado como respuestaGreatElectron viernes, 13 de noviembre de 2009 15:02
    •  
  • viernes, 13 de noviembre de 2009 15:01GreatElectron Medallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuarioMedallas del usuario
     
    Thanks Adam, after some other googling, that does seem to be the simplest solution. Strange that you can solve it very easily in C# with [field:NonSerialized], but there's no equivalent in VB. Your suggestion does mean a lot of code that I don't want cluttering up my business objects, but seems like it's the least bad 'hack' available :(