MVVM视图 - 通过事件进行ViewModel通信

时间:2015-02-02 13:24:14

标签: c# wpf events mvvm

编辑:我已经重新设计了我的问题,因为我似乎有点懒惰。

Edit2:更新了代码,忘记了一件事,并在ViewUcBase.UserInputErrorHandler上编写了EventArg而不是UserInputEventArgs

Edit3:改变了VMBase,仍然没有努力。

所以我有这个(部分)当前设置:https://www.dropbox.com/s/8ibsbwv2faigxal/MVVM%20Messaging.png?dl=0

现在我想做以下事情:

  • 在IViewModelComponent中定义一个可以的事件(UserInputError) 由任何特定的视图模型引发(提供一个字符串告诉 事件的消费者出了什么问题)。
  • 在ViewBase中有一个虚拟(默认)使用者,它只显示带有错误字符串的Messagebox。
  • 让ViewBase的构造函数为该事件订阅其处理程序。因此,具体观点不必考虑这一点。

考虑:

  • 我知道有一些框架可以轻松地为您完成此操作。但我喜欢知道在我使用能为我做的事情之前,事情是如何工作的,至少在基础模型上是这样。
  • 编辑:我从https://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx#sec12
  • 得到了我的灵感
  • 我对基于以下内容的事件有了基本的了解:“如何:提升和消费事件”在msdn上(对不起只有2个链接)我已经让它在以前的一个简单项目中工作了。
  • 只要它只查看任务,我就没有问题。

无效的代码。我知道我错过了什么,但似乎无法找到什么。 (最后的错误)

    public interface IViewModelComponent
    {
        event EventHandler UserInputError;

        //just to show I want multiple events.
        event EventHandler SomeOtherEvent;
    }

    public class VMBase: IViewModelComponent
    {
        public event EventHandler<UserInputEventArgs> UserInputError;
        protected virtual void OnUserInputError(UserInputEventArgs e)
        {
            EventHandler<UserInputEventArgs> handler = UserInputError;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        //just to show I want multiple events.
        public event EventHandler SomeOtherEvent;
        ...
    }

    public class UserInputEventArgs: EventArgs
    {
        public string ErrorMessage { get; set; }
    }

    public class AssetForm : VMBase
    {
        private void SomethingBadHappened()
        {
            UserInputEventArgs args = new UserInputEventArgs;
            args.ErrorMessage = "This bad thing hapened";
            OnUserInputError(args);
        }
    }

    public class ViewUcBase : UserControl, IViewComponent
    {
        //EDIT: Forgot the next line
        public IViewModelComponent VM { get; set; }

        //Default implementation of what should be done with the event.
        public virtual void UserInputErrorHandler(object sender, UserInputEventArgs e)
        {
            string header = string.Format("{0} - InputError",DisplayName);
            MessageBox.Show(e.ErrorMessage,header,MessageBoxButton.OK,MessageBoxImage.Exclamation);
        }

        //Subscribing to the event in the base class so it always gets handled.
        public ViewUcBase()
        {
            VM.UserInputError += UserInputErrorHandler; //No overload for 'UserInputErrorHandler' matches delegate 'System.EventHandler'
        }
    }

}

我希望此更新可以更好地解释事情,并且我没有错过任何内容。

2 个答案:

答案 0 :(得分:0)

我使用 发布订阅模式 来处理复杂的类依赖项:

<强>视图模型:

    public class ViewModel : ViewModelBase
    {
        public ViewModel()
        {
            CloseComand = new DelegateCommand((obj) =>
                {
                    MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
                });
        }
}

<强>窗口:

public partial class SomeWindow : Window
{
    Subscription _subscription = new Subscription();

    public SomeWindow()
    {
        InitializeComponent();

        _subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
            {
                this.Close();
            });
    }
}

您可以利用Bizmonger.Patterns获取MessageBus。

<强> MessageBus

public class MessageBus
{
    #region Singleton
    static MessageBus _messageBus = null;
    private MessageBus() { }

    public static MessageBus Instance
    {
        get
        {
            if (_messageBus == null)
            {
                _messageBus = new MessageBus();
            }

            return _messageBus;
        }
    }
    #endregion

    #region Members
    List<Observer> _observers = new List<Observer>();
    List<Observer> _oneTimeObservers = new List<Observer>();
    List<Observer> _waitingSubscribers = new List<Observer>();
    List<Observer> _waitingUnsubscribers = new List<Observer>();

    int _publishingCount = 0;
    #endregion

    public void Subscribe(string message, Action<object> response)
    {
        Subscribe(message, response, _observers);
    }

    public void SubscribeFirstPublication(string message, Action<object> response)
    {
        Subscribe(message, response, _oneTimeObservers);
    }

    public int Unsubscribe(string message, Action<object> response)
    {
        var observers = new List<Observer>(_observers.Where(o => o.Respond == response).ToList());
        observers.AddRange(_waitingSubscribers.Where(o => o.Respond == response));
        observers.AddRange(_oneTimeObservers.Where(o => o.Respond == response));

        if (_publishingCount == 0)
        {
            observers.ForEach(o => _observers.Remove(o));
        }

        else
        {
            _waitingUnsubscribers.AddRange(observers);
        }

        return observers.Count;
    }

    public int Unsubscribe(string subscription)
    {
        var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription).ToList());
        observers.AddRange(_waitingSubscribers.Where(o => o.Subscription == subscription));
        observers.AddRange(_oneTimeObservers.Where(o => o.Subscription == subscription));

        if (_publishingCount == 0)
        {
            observers.ForEach(o => _observers.Remove(o));
        }

        else
        {
            _waitingUnsubscribers.AddRange(observers);
        }

        return observers.Count;
    }

    public void Publish(string message, object payload)
    {
        _publishingCount++;

        Publish(_observers, message, payload);
        Publish(_oneTimeObservers, message, payload);
        Publish(_waitingSubscribers, message, payload);

        _oneTimeObservers.RemoveAll(o => o.Subscription == message);
        _waitingUnsubscribers.Clear();

        _publishingCount--;
    }

    private void Publish(List<Observer> observers, string message, object payload)
    {
        Debug.Assert(_publishingCount >= 0);

        var subscribers = observers.Where(o => o.Subscription.ToLower() == message.ToLower());

        foreach (var subscriber in subscribers)
        {
            subscriber.Respond(payload);
        }
    }

    public IEnumerable<Observer> GetObservers(string subscription)
    {
        var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription));
        return observers;
    }

    public void Clear()
    {
        _observers.Clear();
        _oneTimeObservers.Clear();
    }

    #region Helpers
    private void Subscribe(string message, Action<object> response, List<Observer> observers)
    {
        Debug.Assert(_publishingCount >= 0);

        var observer = new Observer() { Subscription = message, Respond = response };

        if (_publishingCount == 0)
        {
            observers.Add(observer);
        }
        else
        {
            _waitingSubscribers.Add(observer);
        }
    }
    #endregion
}

}

<强>订阅

public class Subscription
{
    #region Members
    List<Observer> _observerList = new List<Observer>();
    #endregion

    public void Unsubscribe(string subscription)
    {
        var observers = _observerList.Where(o => o.Subscription == subscription);

        foreach (var observer in observers)
        {
            MessageBus.Instance.Unsubscribe(observer.Subscription, observer.Respond);
        }

        _observerList.Where(o => o.Subscription == subscription).ToList().ForEach(o => _observerList.Remove(o));
    }

    public void Subscribe(string subscription, Action<object> response)
    {
        MessageBus.Instance.Subscribe(subscription, response);
        _observerList.Add(new Observer() { Subscription = subscription, Respond = response });
    }

    public void SubscribeFirstPublication(string subscription, Action<object> response)
    {
        MessageBus.Instance.SubscribeFirstPublication(subscription, response);
    }
}

答案 1 :(得分:0)

我知道这是愚蠢的事情,而且确实如此。

我正在混合来自https://msdn.microsoft.com/en-us/library/9aackb16%28v=vs.110%29.aspx的两个例子。所以我更新了所有

    "Eventhandler" to "EventHandler<UserInputEventArgs>"

现在正在运作。

抱歉浪费你的时间。

完整的工作代码:

    public interface IViewModelComponent
    {
        event EventHandler<UserInputEventArgs> UserInputError;
    }

    public class VMBase: IViewModelComponent
    {
        public event EventHandler<UserInputEventArgs> UserInputError;
        protected virtual void OnUserInputError(UserInputEventArgs e)
        {
            EventHandler<UserInputEventArgs> handler = UserInputError;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }

    public class UserInputEventArgs: EventArgs
    {
        public string ErrorMessage { get; set; }
    }

    public class AssetForm : VMBase
    {
        private void SomethingBadHappened()
        {
            UserInputEventArgs args = new UserInputEventArgs;
            args.ErrorMessage = "This bad thing hapened";
            OnUserInputError(args);
        }
    }

    public class ViewUcBase : UserControl, IViewComponent
    {
        //EDIT: Forgot the next line
        public IViewModelComponent VM { get; set; }

        //Default implementation of what should be done with the event.
        public virtual void UserInputErrorHandler(object sender, UserInputEventArgs e)
        {
            string header = string.Format("{0} - InputError",DisplayName);
            MessageBox.Show(e.ErrorMessage,header,MessageBoxButton.OK,MessageBoxImage.Exclamation);
        }

        //Subscribing to the event in the base class so it always gets handled.
        public ViewUcBase()
        {
            VM.UserInputError += UserInputErrorHandler; //No overload for 'UserInputErrorHandler' matches delegate 'System.EventHandler'
        }
    }

}