The right way to implement Weak Event Pattern for shared property MVVM

942 views Asked by At

I'm preparing an application that will have multiple objects, each one will have an integer id and string data. I want the data of the objects with the same id to be the same. I will call this object SharedObject (this object acts like a ViewModel for some database table).

One may say it is very easy. You can create a single object and reference it each time you need it. But actually this way will not work for me since I can't predict when and where the object is created.

So to handle this problem I decided to put a static event in the SharedObject class and call it when any SharedObject data is changed (like PropertyChanged event in INotifyPropertyChanged but mine is static. This will make it visible to each instance of SharedObject), but the problem is that I end up with a program full of memory leaks!!

Last night I sorted out that I had to use weak event pattern to solve the memory leak problem.

I searched the net over the last night for full implementation of weak event pattern but I ended up with nothing or deleted pages.

Finally I figured out how to do it with weak event pattern and I created a test project to test this approach, but it did not work, the ReceiveWeakEvent event couldn't be called.

Am I missing something? Or what is the right way to implement this pattern?

By the way I'm using .net 4 because I need my application to support Win XP (this is what my customer needs, don't ask me about it), so I can't use the generic WeakEventManager class.

Sorry for the long post but this problem was about to make me tear my hair off, and thanks in advance.

Here is the full application code:

SharedObject class:

public class SharedObject :INotifyPropertyChanged,IWeakEventListener
{
    static WaekEvent sheardEvent = new WaekEvent();

    public event PropertyChangedEventHandler PropertyChanged;

    public int ID { get; private set; }

    public SharedObject(int id)
    {
        this.ID = id;
        SharedWeakEventManager.AddListener(sheardEvent, this);
    }

    string data;
    public string Data
    {
        get
        {
            return data;
        }
        set
        {
            if(value != data)
            {
                data = value;
                OnPropertyChanged("Data");
                sheardEvent.RiseSharedEvent(this, new PropertyChangedEventArgs("Data"));
            }
        }
    }

    protected virtual void OnPropertyChanged(string propName)
    {
        PropertyChangedEventHandler handler;
        lock (this)
        {
            handler = PropertyChanged;
        }
        if (handler != null)
            handler.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    void OnSharedPropertyChaingo(object sender,PropertyChangedEventArgs e)
    {
        SharedObject s = sender as SharedObject;
        if (s == null || s.ID != ID)
            return;
        data = s.Data;
        OnPropertyChanged("Data");
    }


    public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
    {
        if(managerType == typeof(SharedWeakEventManager) && sender is SharedObject)
        {
            OnSharedPropertyChaingo(sender, e as PropertyChangedEventArgs);
        }
        return false;
    }
}

WaekEvent and SharedWeakEventManager classes:

public class SharedObject 
{
    public event EventHandler SharedEvent;

    public void RiseSharedEvent(object sender, EventArgs args)
    {
        if (SharedEvent != null)
            SharedEvent.Invoke(sender, args);
    }
}
public class SharedWeakEventManager : WeakEventManager
{
    public static SharedWeakEventManager CurrentManager
    {
        get
        {
            var manager_type = typeof(SharedWeakEventManager);
            var manager = WeakEventManager.GetCurrentManager(manager_type) as SharedWeakEventManager;

            if (manager == null)
            {
                manager = new SharedWeakEventManager();
                WeakEventManager.SetCurrentManager(manager_type, manager);
            }

            return manager;
        }
    }

    public static void AddListener(WaekEvent source, IWeakEventListener listener)
    {
        CurrentManager.ProtectedAddListener(source, listener);
    }

    public static void RemoveListener(WaekEvent source, IWeakEventListener listener)
    {
        CurrentManager.ProtectedRemoveListener(source, listener);
    }

    protected override void StartListening(object source)
    {
        ((WaekEvent)source).SharedEvent += this.DeliverEvent;
    }

    protected override void StopListening(object source)
    {
        ((WaekEvent)source).SharedEvent -= this.DeliverEvent;
    }
}   

the XAML part of the MainWindow:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <ListView x:Name="View" Margin="10,49,10,10" SelectionChanged="ListView_SelectionChanged">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="ID" Width="100" DisplayMemberBinding="{Binding ID}"/>
                <GridViewColumn Header="Data" Width="100" DisplayMemberBinding="{Binding Data}"/>
            </GridView>
        </ListView.View>
    </ListView>
    <Button Content="New" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10,13.01,0,0" Click="Button_Click"/>
    <TextBox x:Name="IDText" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="92" Margin="136.2,11,0,0"/>
    <TextBox x:Name="DataText" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="92" Margin="280.799,11,0,0" TextChanged="DataText_TextChanged"/>
    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="ID" VerticalAlignment="Top" Margin="119.593,14,0,0"/>
    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Data" VerticalAlignment="Top" Margin="251.106,15.01,0,0"/>
    <Button Content="Remove" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="377.799,12,0,0" Click="Button_Click_1"/>

</Grid>

MainWindow class:

public partial class MainWindow : Window
{
    private ObservableCollection<SharedObject> sharedObjects;
    public ObservableCollection<SharedObject> SharedObjects
    {
        get
        {
            if (sharedObjects == null)
                sharedObjects = new ObservableCollection<SharedObject>();
            return sharedObjects;
        }
    }

    public SharedObject SelelectedObject { get; set; }
    public MainWindow()
    {
        InitializeComponent();
        View.ItemsSource = SharedObjects;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        int id;
        if (!int.TryParse(IDText.Text, out id))
            return;

        SharedObject x = new SharedObject(id);
        x.Data = DataText.Text;
        SharedObjects.Add(x);
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        if (SelelectedObject == null)
            return;
        SharedObjects.Remove(SelelectedObject);
        SelelectedObject = null;
    }

    private void DataText_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (SelelectedObject != null)
            SelelectedObject.Data = DataText.Text;
    }

    private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        SelelectedObject = View.SelectedItem as SharedObject;
        if (SelelectedObject == null)
            return;
        IDText.Text = SelelectedObject.ID.ToString();
        DataText.Text = SelelectedObject.Data;
    }
}
0

There are 0 answers