【问题标题】:Caliburn Micro Stack Overflow Exception using IEventAggregator使用 IEventAggregator 的 Caliburn Micro Stack 溢出异常
【发布时间】:2020-01-05 10:18:28
【问题描述】:

我正在使用Caliburn.Micro 学习和尝试MVVM。我有一个主要的ShellViewModel 和一个子用户控件Module1ViewModel。我正在尝试使用IEventAggregator 实现他们之间的通信。
现在,我可以从子控件 ShellViewModel 更改 ShellViewModel (它是父级)的属性,并且可以从父级更改子控件的属性并且它可以工作。

问题:我面临的问题是,当我在两个 ViewModel 中启用 events.Subscribe(this); 时,我得到一个异常。

我想要实现的是两种方式的沟通方式。这意味着我想从子项更改父项的某些属性,同时我应该能够从父项更改子项的某些属性。以下是我的代码,请检查这里有什么问题。

子用户控制模块1ViewModel

    namespace IntelliCoreMVVM.ViewModels
    {
        public class Module1ViewModel:Screen , IHandle<string>
        {
            private IEventAggregator _events;
            public Module1ViewModel(IEventAggregator events)
            {
                _events = events;
                events.Subscribe(this);
            }
            private string _firstName;
            public string FirstName
            {
                get { return _firstName; }
                set
                {
                    _firstName = value;
                    NotifyOfPropertyChange(()=>FirstName);
                    _events.PublishOnUIThread(FirstName);
                }
            }

            public void Handle(string message)
            {
                FirstName = message;
            }
        }
    }  

如您所见,我正在发布和处理一个属性。

ShellViewModel


    public class ShellViewModel : Conductor<object> , IHandle<string>
            {
                private Module1ViewModel _module1ViewModel;
                private IEventAggregator _events;
                public ShellViewModel(Module1ViewModel module1ViewModel,IEventAggregator events)
                {
                    _events = events;
                    events.Subscribe(this);
                    _module1ViewModel = module1ViewModel;
                }
                private string _test;
                private string _firstName;
                public string FirstName
              {
               get { return _firstName; }
               set
                 {
                  _firstName = value;
                   NotifyOfPropertyChange(()=>FirstName);
                  _events.PublishOnUIThread(new CustomerModel{FirstName =     FirstName});
                 }
                }
                public string Test
                {
                    get { return _test; }
                    set
                    {
                        _test = value; 
                        NotifyOfPropertyChange(()=>Test);
                    }
                }
                public void Handle(string message)
                {
                    Test = message;
                }
          }

快速观看
堆栈调用

【问题讨论】:

  • @mjwills StackTrace 在异常详细信息的快速观察窗口中为空...??在 Visual Studio 的调用堆栈窗口中,它是一个巨大的列表……我该如何分享呢?
  • @mjwills 附上两张截图,请查收。
  • “两种方式的交流”?那么什么时候应该停止这种交流呢?应该有一个事件的发布者。它可以由多个订阅者处理。但是,如果订阅者每次收到事件时都向发布者发布另一个事件,则发布者无法通过向订阅者发送另一个事件来响应此事件。难怪你会收到StackoverflowException。你想在这里完成什么?
  • 当然他们可以,但他们不能通过在另一个方向发送相同的事件来响应对方的事件,因为它会得到新的事件,并且链会像这样继续下去,直到你用完堆栈空间。
  • Module1ViewModel 正在处理 所有 string 事件,包括它自己的事件。

标签: c# wpf mvvm caliburn.micro


【解决方案1】:

FirstName 引发调用Handle 的事件,该事件再次设置FirstName,然后它在无限循环中继续下去,直到您用完堆栈空间并获得StackOverflowException

问题是Module1ViewModel 处理所有 string 事件,包括它自己引发的事件。

您可能想要做的是定义不同类型的事件,以便区分它们并选择要处理的事件。在下面的示例中,Module1ViewModel 处理 ParentToChildEvent 类型的事件,但它引发了 ChildToParentEvent 类型的事件。 ShellViewModel 应该相反。

public class Module1ViewModel : Screen, IHandle<ParentToChildEvent>
{
    private IEventAggregator _events;
    public Module1ViewModel(IEventAggregator events)
    {
        _events = events;
        events.Subscribe(this);
    }
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            _events.PublishOnUIThread(new ChildToParentEvent(FirstName));
        }
    }

    public void Handle(string message)
    {
        FirstName = message;
    }
}

【讨论】:

  • 现在清晰易懂!如果你能澄清一件事,请向我澄清。 ParentToChildChildToParent 是两种创建错误的类型,将在这种情况下使用。但是,如果假设有两个不同的 Vms ..VM_AVM_A_Child 并且我想要它们之间的通信,那么我必须创建另外两种类型?依此类推,适用于每种不同的情况。因为如果不正确使用ParentToChild,那么它将导致ShellViewModel 监听.. 简单来说,为每个新案例创建新的 ParentChild 类型?不是很糟糕吗?并且会导致我的项目中创建的事件类型过多?
  • 您应该定义多少事件类型取决于您的应用程序的逻辑。您当然可以为多个视图模型重用相同的类型。只是不能创建无限循环。
  • 如果你能指导我从 getter 或 setter 调用 PublishEvent 就再做一件事是不好的???或者没关系?
  • 如果在另一个案例或另一个Vm 中使用相同的ParentToChild 类型,那么它会导致ShellViewModel 监听?并且会再次面临一些问题?
  • 这取决于您提出和处理事件的方式和地点。它与任何其他事件相同,对于事件聚合器而言没有什么特别之处。
【解决方案2】:

问题在于您的虚拟机正在递归地相互更新和自我更新,例如订阅IEventAggregator 并处理字符串消息,而不是更新属性并再次发布相同的,某种无限循环。您可以引入一个类型化的事件来处理从父级到子级,反之亦然,而不仅仅是字符串

public class ChildEvent
{
    public string Name { get; set; }
}

public class ParentEvent
{
    public string Name { get; set; }
}    
public class ShellViewModel : Conductor<object>, IHandle<ChildEvent>
{
    private readonly IEventAggregator _events;
    public ShellViewModel(IEventAggregator events)
    {
        _events = events;
        events.Subscribe(this);
    }
    private string _test;

    public string Test
    {
        get => _test;
        set
        {
            _test = value;
            NotifyOfPropertyChange(() => Test);
        }
    }
    public void Handle(ChildEvent message)
    {
        Test = message.Name;
    }
}

public class Module1ViewModel : Screen, IHandle<ParentEvent>
{
    private readonly IEventAggregator _events;
    public Module1ViewModel(IEventAggregator events)
    {
        _events = events;
        events.Subscribe(this);
    }
    private string _firstName;
    public string FirstName
    {
        get => _firstName;
        set
        {
            _firstName = value;
            NotifyOfPropertyChange(() => FirstName);
        }
    }

    public void Handle(ParentEvent message)
    {
        FirstName = message.Name;
    }
}

顺便说一句,在使用IEventAggregator 的情况下,您不需要ShellViewModel 中的Module1ViewModel 引用。也不要在 setter 中立即触发事件,因为它可以在视图模型之间触发无限事件循环

【讨论】:

  • 所以我不能反过来与strings通信?
  • 是的,尝试使用类型化事件,例如ChildEventParentEvent
  • 您能提供一些示例或资源帮助吗?我不知道该怎么做,也没有找到任何相关的帮助
  • 我可以用一些例子来更新这个问题,但是你不必直接在属性设置器中触发事件,否则你会在虚拟机之间得到一个无限更新循环
  • 好的。如果你能展示一些在视图模型之间实现这种通信的真实方法,我会很高兴!
猜你喜欢
  • 1970-01-01
  • 2016-03-24
  • 1970-01-01
  • 2012-01-29
  • 2012-10-24
  • 2012-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多