Caliburn.Micro - 更新 BindableCollection 中的项目字段时调用方法保护

Caliburn.Micro - Calling method guard when updating field of item in BindableCollection

我有一个 属性 BindableCollection<Cat> { get; private set; },其中有许多 Cat 个对象绑定到我的 WPF XAML.

中的 ItemsControl

然后我将以下代码附加到此 ItemsControl 内的按钮。

XAML 片段

<Button Content="Pat Cat" cal:Message.Attach="PatCat($dataContext)" />

在 ViewModel 中...

BindableCollection<Cat> { get; private set; }

public bool CanPatCat(Cat cat)
{
   return cat.Pattable && cat.Alive;
}

public void PatCat(Cat cat)
{
    // pat the cat
    cat.Alive = false;
}

鉴于这是一个方法守卫而不是 属性,我如何通知 UI 以便为 BindableCollection 中的每个 Cat 评估 CanPat 方法,因为现在守卫方法将 return false 一旦猫被拍了?

虽然对于属性,存在特定且复杂的 UI 感知机制(我说的是 Binding),但对于方法,情况则完全不同。 在我看来,在这种情况下使用事件是不合适的,所以我想到了一个基于扩展 Caliburn Micro 框架的解决方案。

首先,我们需要创建一个特定的 ActionMessage,用于在调用后重新执行 guard 方法。我叫它 RefreshableActionMessage.

public class RefreshableActionMessage : ActionMessage
{
    private bool refreshAfterInvoke = true;

    public bool RefreshAfterInvoke
    {
        get { return refreshAfterInvoke; }
        set { refreshAfterInvoke = value; }
    }
}

现在我们必须在 "guarded" 按钮中使用它(是的,我们需要使用冗长的语法):

<Button Content="Pat Cat">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <local:RefreshableActionMessage MethodName="PatCat">
                <cal:Parameter Value="{Binding}" />
            </local:RefreshableActionMessage>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

只是缺少了负责重新执行guard方法的代码,但是我们可以在Bootstrapper class:[=20中的Configure方法中添加它=]

protected override void Configure()
{
    ActionMessage.InvokeAction = delegate(ActionExecutionContext context)
    {
        RefreshableActionMessage refreshableActionMessage;

        object[] parameters = MessageBinder.DetermineParameters(context, context.Method.GetParameters());
        object obj = context.Method.Invoke(context.Target, parameters);
        Task task = obj as Task;
        if (task != null)
        {
            obj = task.AsResult();
        }
        IResult result = obj as IResult;
        if (result != null)
        {
            obj = new IResult[]
            {
                result
            };
        }
        IEnumerable<IResult> enumerable = obj as IEnumerable<IResult>;
        if (enumerable != null)
        {
            obj = enumerable.GetEnumerator();
        }
        IEnumerator<IResult> enumerator = obj as IEnumerator<IResult>;
        if (enumerator != null)
        {
            Coroutine.BeginExecute(enumerator, new CoroutineExecutionContext
            {
                Source = context.Source,
                View = context.View,
                Target = context.Target
            }, null);
        }

        refreshableActionMessage = context.Message as RefreshableActionMessage;
        if (refreshableActionMessage != null && refreshableActionMessage.RefreshAfterInvoke)
        {
            refreshableActionMessage.UpdateAvailability();
        }
    };
}

如您所见,如果自定义 ActionMessage 需要刷新,则通过调用 UpdateAvailability 方法执行。

希望这个解决方案能帮到你。

我最终为每只 Cat 创建了一个单独的 ViewModel,即 CatViewModel 和相应的 CatView。然后我能够通知可绑定集合已更改,并且一切正常。

请注意,我错误地将 ViewModel 逻辑放入了我的 Cat 模型中。这些是在我的模型上发生变化的属性。我将它们移到了 CatViewModel 中。