Caliburn.Micro and event aggregator -unwanted call

2019-05-26 03:03发布

问题:

I have one problem with publish/handle messages between 2 screens.

My scenario is:

  1. Messenger screen, is it master screen, publish on chat screens, they are slave screens. Messenger view model handle with messages from server.

  2. Chat screen can publishes messages on messenger screen. And messanger view model send this message on server.

Messenger class look like this:

 [Export("MessengerScreen", typeof(IMessengerViewModel))]
    public class MessengerViewModel : Screen, IMessengerViewModel, IInitializable<Account>, IHandle<Rp>
    {
        // ...

        [ImportingConstructor]
        public MessengerViewModel(IPokecService service, IEventAggregator eventAgg)
        {
            _eventAgg = eventAgg;
            _eventAgg.Subscribe(this);
        }


        //publish on slave screen 
        public void Publish(Rp rp)
        {
            _eventAgg.Publish(rp);
        }

        //handle msg from slave screen
        public void Handle(Rp msg)
        {
            //send to server
        }
    }

Slave screen class look like this:

   [Export("ChatScreen", typeof(IChatViewModel))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class ChatViewModel : Screen, IInitializable<DetailData>, IHandle<Rp>
    {
        [ImportingConstructor]
        public ChatViewModel(IEventAggregator eventAgg)
        {
            _eventAgg = eventAgg;
            _eventAgg.Subscribe(this);
        }

        //publish only on messenger screen
        public void Publish(Rp rp)
        {
            _eventAgg.Publish(rp);
        }

        //show message from published from messenger
        public void Handle(Rp rp)
        {
            AddBlockToConversation(rp);
        }

        //if enter is pressed publish on messanger screen
        public void SendRp(KeyEventArgs e)
        {
            if (e.Key == Key.Enter && !string.IsNullOrEmpty(RpText))
            {
                _yourRp.Time = String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now);

                _yourRp.RpText = RpText;

                AddBlockToConversation(_yourRp);


                //publish on messanger screen
                Publish(_yourRp);
            }
        }
    }

My problems are:

First problem is:

  • I call method SendRp from class ChatViewModel.
  • It calls method void Publish() in ChatViewModel,
  • then is call method void Handle() from class MessengerViewModel
  • and then call also method void Handle() from ChatViewModel class.

I don’t want call method Handle() in ChatViewModel class. Why if I send message from ChatViewModel to MessengerViewModel is also called method Handle in ChatViewModel class?

My second problem is:

I would like publish from MessengerViewModel message on only certain slave screen.

MessgerVieModel have in queue messages: {msg1, msg2, msg3, ..., msgN}

I would like publish:

  • msg1 on slave screen #1.
  • msg2 on slave screen #2
  • ...
  • msg3 on slave screen #3

回答1:

MY SOLUTION: I solved my problem with modification class EventAggregator.

Something like this:

Every my view model imlements this interface:

public interface  IViewModelIdentity
{
    string ScreenIdentity { get; set; }
}

And in Publish method in even aggregator class I have this:

 public void Publish(Rp rp)
        {

            WeakReference[] toNotify;
            lock (_subscribers)
                toNotify = _subscribers.ToArray();

            Execute.OnUIThread(() =>
            {
                Log.Info("Publishing {0}.", rp);
                var dead = new List<WeakReference>();

                foreach (var reference in toNotify)
                {
                    var target = reference.Target as IHandle<Rp>;

                    //GET ID OF SCREEN
                    var screenId = reference.Target as IViewModelIdentity;

                    //!
                    if (target != null && screenId != null)
                    {
                        if (screenId.ScreenIdentity=="screen on which we want to send a message")
                        {
                            //PUBLISH ON SCREEN
                            target.Handle(rp);
                        }
                    }
                    else if (!reference.IsAlive)
                        dead.Add(reference);
                }
                if (dead.Count > 0)
                {
                    lock (_subscribers)
                        dead.Apply(x => _subscribers.Remove(x));
                }
            });
        }