简体   繁体   中英

How to manually re-hook a non-serialized INotifyPropertyChanged event handler to the deserialized object

Any event handlers marked as [NonSerialized] get destroyed on deserialization, so you need to manually re-hook all the required event handlers to the deserialized object after deserialization [Binary]. The only problem is, I don't know how to do that. Any help?

My serializable class:

[Serializable]
public class DownloadEntry : INotifyPropertyChanged
{
    [field: NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public string DownloadID { get; set; }
    public Uri DownloadLink { get; set; }
    public string FileName { get; set; }
    private long size;
    public long Size
    {
        get { return size; }
        set
        {
            size = value;
            OnPropertyChanged("Size");
        }
    }
    public string SizePretty
    {
        get
        {
            return Helper.SizeSuffix(Size);
        }
        set
        {
            SizePretty = Size.ToString();
        }
    }
    private string timeleft;
    public string TimeLeft
    {
        get { return timeleft; }
        set
        {
            timeleft = value;
            OnPropertyChanged("TimeLeft");
        }
    }
    private string status;
    public string Status
    {
        get { return status; }
        set
        {
            status = value;
            OnPropertyChanged("Status");
        }
    }
    private string transferrate;
    public string TransferRate
    {
        get { return transferrate; }
        set
        {
            transferrate = value;
            OnPropertyChanged("TranferRate");
        }
    }
    public DateTime DateAdded { get; set; }
    public DateTime LastTryDate { get; set; }
    public string SaveTo { get; set; }
    public string Q { get; set; }
    public string Description { get; set; }
    public string AuthUsername { get; set; }
    public string AuthPassword { get; set; }
    public string ObtainedFrom { get; set; }
    [NonSerialized]
    private bool running;
    public bool Running 
    { 
        get { return running; } 
        set 
        { 
            running = value;
            OnPropertyChanged("Running");
        }
    }
}

Class where serialization and deserialization takes place:

public static class Downloads
{
    public static ObservableCollection<DownloadEntry> DownloadEntries = new ObservableCollection<DownloadEntry>();

    public static void Deserialize()
    {
        if (File.Exists("downloads.dat"))
        {
            IFormatter formatter = new BinaryFormatter();
            using (FileStream stream = File.OpenRead("downloads.dat"))
            {
                DownloadEntries = (ObservableCollection<DownloadEntry>)formatter.Deserialize(stream);
            }
        }
    }

    public static void Serialize()
    {
        IFormatter formatter = new BinaryFormatter();
        using (FileStream stream = File.Create("downloads.dat"))
        {
            formatter.Serialize(stream, DownloadEntries);
        }
    }
}

What I found on here, but there were no explanations on how to do it.

https://stackoverflow.com/a/6697942/4932305 :

most of my implementations of INotifyPropertyChanged I explicitly mark the PropertyChanged event as being non-serialized and then manually re-hook the event upon deserialization as appropriate.

Code present but it's in VB.net: Events not working with after Deserialization :

The problem is not that the Changed event isn't being fired; as long as the same class definition (with the setter that raises the event) is used for the deserialized object (with DataContract serialization that isn't a requisite), the event will be raised. What's happening is that the deserialized object no longer has the handler attached.

You cannot serialize or deserialize event handlers; at the very least, you shouldn't. Because they may point to references other than the current object reference, and because the deserialized object is a new reference in what is probably a different runtime, event handler references from the serialized object are useless on deserialization, because the reference will no longer point to the expected object in the new runtime's heap.

You have to implement your static class to Deserialized it using the serialized contents (xml or binary) and subscribe to required events.

public static DownloadEntry Deserialize(string serializedXmlContents) 
{
    DownloadEntry downloadEntry= ... // Do the deserialization
    // Subscribe to your events
    return downloadEntry;
}

The solution is here: http://web.archive.org/web/20150531190503/http://www.gavaghan.org/blog/2007/07/20/demonstrating-bindinglist-serialization-bug/

Because I couldn't apply it to my ObservableCollection. I just used a BindingList, which acts pretty much the same as an ObservableCollection. All I had to do was change CollectionChanged event to ListChanged and my problem was solved.

I have spent days obsessing over this. Thankfully, that's over now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM