【问题标题】:Passing data from a window back to ShellViewModel将数据从窗口传回 ShellViewModel
【发布时间】:2018-01-28 21:27:44
【问题描述】:

好的,我在解决这个问题时遇到了问题。我发现的大多数教程要么不完整,要么解释得不够好。

我使用 Caliburn.Micro 创建了一个测试 WPF 应用程序,该应用程序的主窗口 (ShellViewModel) 有一个文本框和一个按钮,该按钮会打开一个带有文本框和另一个按钮的第二个窗口。当用户在第二个窗口中添加文本并单击“发送”时,POCO 对象被创建并应该被发送到第一个窗口并显示在 ShellViewModel 的文本框中。

我不确定我哪里出错了,似乎没有很多文章可以帮助解决这个问题。

我尝试使用以下文章寻求帮助: https://claytonone.wordpress.com/2014/06/14/caliburn-micro-part-1-getting-started/

https://caliburnmicro.com/documentation/event-aggregator

******EDIT - 按照中的说明重新编程上述项目 https://caliburnmicro.com/documentation/event-aggregator 下面是这个项目的代码。请注意,我添加了一个 POCO 类来存储我想要发送的数据并将其发送到另一个窗口,这更多的是我正在处理的主要项目中的最终目标。

我现在遇到的问题: 1. 当我运行教程设计的程序时,VS 报错说没有无参数的构造函数。为此,我尝试添加构造函数。现在程序运行了。 2. 当我在第二个窗口中输入文本并单击发送时,我得到一个“空引用错误”,但如果我调试“ToSend”对象,则会创建并填充正确的数据。

AppBootStrapper:

namespace CaliburnMicro
{
    class AppBootstrapper : BootstrapperBase
    {
        private readonly SimpleContainer _container = new SimpleContainer();

        public AppBootstrapper()
        {
            Initialize();
        }

        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            DisplayRootViewFor<ShellViewModel>();
        }

        protected override void Configure()
        {
            _container.Singleton<IEventAggregator, EventAggregator>();
        }
    }
}

ShellViewModel:

namespace CaliburnMicro.ViewModels
{
    class ShellViewModel : Screen, IHandle<EventMessage>
    {
        private string _messageBox;
        private readonly IEventAggregator _eventAggregator;


        public string MessageBox
        {
            get { return _messageBox; }
            set
            {
                _messageBox = value;
                NotifyOfPropertyChange(() => MessageBox);
            }
        }

        public ShellViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public ShellViewModel()
        {

        }

        public void OpenWindow()
        {
            WindowManager wm = new WindowManager();
            SecondWindowViewModel swm = new SecondWindowViewModel(_eventAggregator);
            wm.ShowWindow(swm);
        }

        public void Handle(EventMessage message)
        {
            MessageBox = message.Text;
        }
    }
}

第二个窗口视图模型

namespace CaliburnMicro.ViewModels
{
    class SecondWindowViewModel: Screen
    {

        private string _secondTextBox;
        private readonly IEventAggregator _eventAggregator;
        public EventMessage Tosend = new EventMessage();

        public string SecondTextBox
        {
            get { return _secondTextBox; }
            set
            {
                _secondTextBox = value;
                NotifyOfPropertyChange(() => SecondTextBox);
            }
        }



        public SecondWindowViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
        }

        public void SendBack()
        {
            Tosend.Text = SecondTextBox;
            _eventAggregator.PublishOnUIThread(Tosend);
            Thread.Sleep(1000); //I wanted the app to wait a second before closing
            TryClose();
        }

    }
}

这是我想从第二个发回主窗口的 POCO。

namespace CaliburnMicro.Models
{
    class EventMessage
    {
        public string Text { get; set; }
    }
}

【问题讨论】:

  • 字符串是否到达了Handle 内的ShellViewModel

标签: c# wpf caliburn.micro eventaggregator


【解决方案1】:

好的,这是一个关于如何设置AppBotstraperEventAggregator 的小示例。

AppBootstrapper.cs

 public class AppBootstrapper : BootstrapperBase
{
    private SimpleContainer _container;

    public AppBootstrapper()
    {
        Initialize();
    }

    protected override void Configure()
    {
        _container = new SimpleContainer();
        _container.Singleton<IWindowManager, WindowManager>();
        _container.Singleton<IEventAggregator, EventAggregator>();
        _container.PerRequest<ShellViewModel>(); 
    }
    protected override object GetInstance(Type service, string key)
    {
        var instance = _container.GetInstance(service, key);
        if (instance != null)
            return instance;
        throw new InvalidOperationException("Could not locate any instances.");
    }

    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        return _container.GetAllInstances(service);
    }

    protected override void BuildUp(object instance)
    {
        _container.BuildUp(instance);
    }

    protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
    {
        DisplayRootViewFor<ShellViewModel>();
    }
}

ShellViewModel.cs

public class ShellViewModel : Screen, IScreen, IHandle<EventMessage>
{
    private readonly IEventAggregator _eventAggregator;

    public ShellViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }

    public void OpenWindow()
    {
        WindowManager wm = new WindowManager();
        SecondWindowViewModel swm = new SecondWindowViewModel(_eventAggregator);
        wm.ShowWindow(swm);
    }

    public void Handle(EventMessage message)
    {
        MessageBox.Show(message.Text);
    }
}

注意:如果您不了解Caliburn.Micro 中的导体,我建议您阅读this。当您使用导体时,您可以使用方法ActivateItemUserControl 内启动任何子ViewModel

您的SecondWindowViewModel 保持不变,但EventMessage 类必须是public class EventMessage,否则您会收到错误消息。

【讨论】:

  • 非常感谢,这成功了!!您是否知道有一篇文章解释了此 AppBootstrapper 中使用的每个要点。
  • @DwayneB 我建议检查this 链接。
  • 我一定会阅读你的链接,谢谢你这工作完美!现在我需要了解这里的所有内容,而不仅仅是“它应该说什么”,而且现在我知道什么是正确的,我知道为什么
  • 我正在阅读这个链接,至少可以这么说,我很感兴趣。您的评论是否是一种迂回的说法,说我应该(或可以)使用某种形式的导体而不是 WindowManager?
  • @DwayneB 是的!如果您有一个始终处于活动状态的主窗口,但它不断变化UserControls 或者我们称它们为ChildViews,那么您可以使用非常适合此目的的Conductor。如果您在所有ViewModels 中都使用Screen,那么我也会使用WindowManager
【解决方案2】:

只是想一想,您是否设置了 DataContext 以便您的主窗口知道 ViewModel 是您的数据源?

【讨论】:

  • 如果您谈论返回的数据显示在文本框中,我确实确认文本框已绑定到视图模型中的正确变量。我注释掉了按钮事件的代码,并将变量的文本设置为测试字符串。变量和文本框之间的绑定正在工作。除此之外,我还没有看到任何需要设置 DataContext 的地方。
  • 我想尝试的是: 1. 两个类都实现了 INotifyPropertyChanged 或 Caliburn.Micro 的可比类(例如,PopupViewModel 类:Screen,INotifyPropertyChanged) 2. 按钮正确连接到方法。因此,当用户按下 Send 按钮时,Send 方法实际上在 PopUpViewModel 类中运行。对于调试,你甚至可以做一些事情,比如当你按下发送时让它打印到控制台,所以如果它成功了,你就知道这是一个绑定问题。
  • 我为每个按钮添加了一个消息框,显示文本框的值,因此我知道按钮附加到正确的方法。我确信这与我在 Caliburn Micro 中使用消息传递的方式有关。在我阅读的最后 5 个教程中,每个教程的实现方式都不同,而且越来越复杂。这真的让我的轻度强迫症开始了!
  • 尝试在 PopUpViewModel 中添加一个引用回您的 ShellViewModel 的变量(例如称为 shellViewmodel)怎么样?这样,您可以直接更改 ReadString (我假设它绑定到您的主窗口中的文本框)。换句话说,当用户在第二个窗口的文本框中键入内容并单击发送时,在 PopUpViewModel 中它会调用 shellViewModel.ReadString = textBox2.Text;
  • 如果这样(或类似的东西)有效,则表明存在绑定问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
相关资源
最近更新 更多