【问题标题】:Why is loosely coupled event generated with Prism EventAggregator not received by its subscriber?为什么使用 Prism EventAggregator 生成的松散耦合事件没有被订阅者接收?
【发布时间】:2016-03-07 09:44:43
【问题描述】:

在我的 Prism 6 WPF MVVM 模块化应用程序(使用 Unity DI)中,我想使用松散耦合事件在模块之间进行通信 - 一个模块是发布者,其他模块是订阅者。在 AuthorizationViewModel 类的发布者方面,我特别有以下方法:

public class AuthorizationViewModel : BindableBase
{
    . . . . .
    // This method is called from the command method when user clicks button in the view.
    private void authenticateUser(string userName, string userPassword, Action<UserAuthorizationLevel> successCallback, Action<string> failureCallback)
    {
        Task task = Task.Run(() =>
              this.getUsers((users) =>
              {
                  // Get authenticated user information.
                  var userAuthenticated = GetUserByNameAndPassword(userName, userPassword, users);
                  // Call method publishing loosely coupled event if the user exists. Else display the error message.
                  if (userAuthenticated != null)
                      successCallback(userAuthenticated.AuthorizationLevel);
                  else
                      failureCallback("Authentification failed.");
              }));
    }
    . . . . .
}

下面是 AuthorizationViewModel 类中的 successCalback 定义:

private void successCalback(UserAuthorizationLevel authorizationLevel)
{
    // Publish loosely coupled event.
    this._eventAggregator.GetEvent<UserAuthorizationLevelDeterminedEvent>().Publish(authorizationLevel);
}

这里的UserAuthorizationLevel 是在我的应用程序解决方案的一个公共位置定义的枚举类型,我不在这里显示它。 UserAuthorizationLevelDeterminedEvent 是在我的应用程序解决方案的公共位置也定义的事件类型。下面我展示一下:

public class UserAuthorizationLevelDeterminedEvent : PubSubEvent<UserAuthorizationLevel>
{
}

successCalback 方法在必要时运行及其代码行

this._eventAggregator.GetEvent<UserAuthorizationLevelDeterminedEvent>().Publish(authorizationLevel);

执行得很好,所以事件被发布,但订阅者根本不对事件做出反应!订阅者端没有对事件的响应!下面我在订阅者端显示代码:

public class CalibrationNavigationItemViewModel : BindableBase
{
    . . . . .
    private IEventAggregator _eventAggregator;
    . . . . .
    // The constructor; creates instance of CalibrationNavigationItemViewModel.
    public CalibrationNavigationItemViewModel(IRegionManager regionManager, IEventAggregator eventAggregator)
    {
        . . . . .
        this._eventAggregator = eventAggregator;
        this._eventAggregator.GetEvent<UserAuthorizationLevelDeterminedEvent>().Subscribe(this.setRadiobuttonVisualStatus, ThreadOption.BackgroundThread);
        . . . . .
    }
    . . . . .
    // Changes visual status of Radiobutton in the View.
    private void setRadiobuttonVisualStatus(UserAuthorizationLevel userAuthorizationLevel)
    {
        if (userAuthorizationLevel == UserAuthorizationLevel.Manufacturer)
            this.IsVisible = Visibility.Visible;
        else
            this.IsVisible = Visibility.Collapsed;
    }

    // Controls visual status of Radiobutton in the View; the Visibility property
    // of Radiobutton in the View is bound to this property.
    public Visibility IsVisible
    {
        get { return this._isVisible; }
        set { this.SetProperty(ref this._isVisible, value); }
    }
}

(请原谅,我将在这里做一个坏事:我正在控制模块中 Radiobutton 的可见性状态,而不是动态加载模块本身,因为我的应用程序必须有机会在同一个用户中更改用户会话。Prism 模块在初始化后无法卸载。)现在,回到我们的羊身上;我在订阅者端设置了 ThreadOption.BackgroundThread,因为发布者在 TPL 任务中发布事件,而不是在 UI 线程中。我想知道:为什么订阅者对发布的事件完全没有反应?我做错了什么?请帮帮我。

【问题讨论】:

  • 您的CalibrationNavigationItemViewModel 在事件触发时还活着吗?单独的订阅不会阻止它被 gc'ed。
  • 加载并初始化到触发事件的时刻。
  • 然后我看不到任何阻止事件触发的东西。抓住一根稻草,eventtaggregator 是同一个实例吗?在奇怪的情况下,它可能没有注册为单例或使用不同的容器来解析。
  • 我在 AuthorizationViewModel 构造函数(发布者端)和 CalibrationNavigationItemViewModel 构造函数(订阅者端)中创建了一个 EventAggregator 实例。在这两个地方初始化值都是构造函数参数。
  • 订阅者看起来不错,你没有在那里创建一个事件聚合器,但你得到了它应该被注入的东西。如果发布方以相同的方式进行操作,它应该可以工作。你能在两个构造函数中断点并检查事件聚合器实例是否真的相同吗?

标签: c# wpf events prism


【解决方案1】:

这里的问题是事件是在共享项目中定义的,而不是在类库中。

这样事件被复制到引用项目中,实际上存在两个不同的事件(同名),一个被订阅,另一个被发布。

【讨论】:

    【解决方案2】:

    尚未对此进行测试,但您似乎是在单独的线程上发布该事件。因此,该事件不会自动冒泡到订阅者正在侦听的 UI 线程。

    您可以尝试等待您的任务,然后在任务完成后发布事件,或者您可以尝试在后台线程上订阅事件。

    编辑: 我看到了问题。您正在使用共享项目。这行不通。 SharedProject 不生成程序集,而是将类视为在引用的程序集中定义并在其中编译。您必须使用 PCL。

    【讨论】:

    • 我尝试在后台线程订阅:this._eventAggregator.GetEvent().Subscribe(this.setRadiobuttonVisualStatus, ThreadOption.BackgroundThread, true);但这无济于事。
    • 然后尝试等待任务然后发布
    • 我试过了,从单独的线程发布对我来说很好,无论我在订阅时是否使用ThreadOption.BackgroundThread。无论如何都应该没关系,因为 PropertyChanged 最终会被编组到 UI 线程,即使 BackgroundEventSubscription 使用 Task.Run 运行处理程序
    • 不,这很重要。 PropertyChanged 不会对您的事件订阅做任何事情。您可以从任何您想要的线程发布,但如果您的订阅者不在该线程上,他们将不会监听该事件。
    • 在 AuthorizationViewModel(发布者)我定义:TaskFactory uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());然后在 authenticateUser 方法中,我更改了 successCallback(userAuthenticated.AuthorizationLevel);到 uiFactory.StartNew(() => successCallback(userAuthenticated.AuthorizationLevel));但这无济于事。
    猜你喜欢
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 2021-01-23
    • 2010-12-21
    • 1970-01-01
    • 2011-02-03
    • 2019-09-05
    相关资源
    最近更新 更多