有没有办法通过单击在 Blazor 中触发两个连续的事件?

Is there a way to trigger two consecutive events in Blazor with a single click?

背景
我在 Blazor WASM 中创建了名为 SET 的纸牌游戏。在这个游戏中,您点击 3 张牌,这可能会导致 SET 成功或错误提交。它运行良好,现在我想添加一个额外的功能来表示提交的结果。

想要的结果
第一种方法:单击第 3 张卡片后,这 3 张卡片应该获得绿色(正确)或红色(错误)背景,并且在 x 时间后(比如 1 秒)第二种方法应该火。
第二种方法:如果设置正确,用3张新卡替换3张卡。如果设置为 false,将背景颜色重置为白色并将边框颜色重置为黑色。

当前实际结果
目前发生的情况是第一种方法(green/red 背景)的结果没有显示,因为只有一个回调事件。所以它确实被执行了,但是直到第二种方法也被执行时它才会变得可见,此时卡片已经被替换,或者 backgroundcolor/bordercolor 已经被重置。

目前已尝试
我试图在@onclick 事件中分离这两个方法,但仍然只有一个 eventcallback,到目前为止我找不到另一种方法来做到这一点,也没有堆栈溢出。

@onclick="() => { ProcessSelection(uniqueCardCombinations[index]); ProcessSetReplacement(); }

The most notable difference is that EventCallback is a single-cast event handler, whereas .NET events are multi-cast. Blazor EventCallback is meant to be assigned a single value and can only call back a single method. https://blazor-university.com/components/component-events/

我也尝试了 Chris Sainty 在这里描述的“状态容器”,但它只是重新渲染它,我无法根据我的情况调整它(不确定这是否可能 tbh): https://chrissainty.com/3-ways-to-communicate-between-components-in-blazor/

代码
我确实打算在代码开始工作后对其进行清理,使其更具描述性并将 ProcessSetReplacement 拆分得更多一些,但我想让它先工作。

如果您需要更多 background/code 信息,请告诉我,或者您可以在此处找到整个存储库:https://github.com/John-Experimental/GamesInBlazor/tree/21_AddSetValidationVisualisation

SetPage.razor:

<div class="cardsContainer">
    @for (int i = 0; i < numberOfCardsVisible; i++)
    {
        var index = i;
        <div class="card @lineClass" style="background-color:@uniqueCardCombinations[index].BackGroundColor; 
                border-color:@uniqueCardCombinations[index].BorderColor;" 
                @onclick="() => { ProcessSelection(uniqueCardCombinations[index]); ProcessSetReplacement(); }">
            @for (int j = 0; j < uniqueCardCombinations[i].Count; j++)
            {
                <div class="@uniqueCardCombinations[i].Shape @uniqueCardCombinations[i].Color @uniqueCardCombinations[i].Border"></div>
            }
        </div>
    }
</div>

SetPage.razor.cs 的相关部分(代码隐藏)

private void ProcessSelection(SetCardUiModel setCard)
    {
        numberOfSelected += _uiHelperService.ProcessCardSelection(setCard);

        if (numberOfSelected == 3)
        {
            var setSubmission = uniqueCardCombinations.Where(card => card.BackGroundColor == "yellow").ToList();
            var potentialSet = _mapper.Map<List<SetCardUiModel>, List<SetCard>>(setSubmission);
            var isSet = _cardHelperService.VerifySet(potentialSet);

            _uiHelperService.SignalSetSubmissionOutcome(setSubmission, isSet);
        };
    }

private void ProcessSetReplacement()
    {
        // If it wasn't a set submission, you do nothing
        if (numberOfSelected == 3)
        {
            var redBorderedCards = uniqueCardCombinations.Where(card => card.BorderColor == "red").ToList();
            var countGreenBorders = uniqueCardCombinations.Count(card => card.BorderColor == "green");

            // The while ensures that the 'ProcessSelection' function, which is also called, has run first
            while (redBorderedCards.Count == 0 && countGreenBorders == 0)
            {
                Thread.Sleep(125);
                redBorderedCards = uniqueCardCombinations.Where(card => card.BorderColor == "red").ToList();
                countGreenBorders = uniqueCardCombinations.Count(card => card.BorderColor == "green");
            }

            // Wait 1.5 seconds so that the user can see the set outcome from 'ProcessSelection' before removing it
            Thread.Sleep(1500);

            if (countGreenBorders == 3)
            {
                // Replace the set by removing the set submission entirely from the list
                uniqueCardCombinations.RemoveAll(card => card.BackGroundColor == "yellow");
                numberOfSelected = 0;

                // Check if the field currently shows more cards than normal (can happen if there was no set previously)
                // If there are more cards, then remove 3 cards again to bring it back down to 'normal'
                numberOfCardsVisible -= numberOfCardsVisible > settings.numberOfCardsVisible ? 3 : 0;

                EnsureSetExistsOnField();
            }
            else
            {
                foreach (var card in redBorderedCards)
                {
                    card.BackGroundColor = "white";
                    card.BorderColor = "black";
                }
            }
        };
    }

不要调用 onclick 中的两种方法。

  • 调用 ProcessSelection,处理结果并在那里设置 red/green。
  • 致电StateHasChanged()
  • ProcessSelection 中将计时器设置为一秒。使用来自 System.Timers.
  • 的计时器
  • 在计时器调用结束时 ProcessSetReplacement

当您需要在一个事件中执行多个定时操作时,async 是您的主要工具。

Blazor 将识别异步事件处理程序:

//private void ProcessSelection(SetCardUiModel setCard) 
private async Task ProcessSelection(SetCardUiModel setCard) 
{
    ... // old code    
    await Task.Delay(1000);    
    ProcessSetReplacement();  
}

当您有多个 Task.Delay() 时,请在它们之前添加 StateHasChanged() 个调用。

在标记部分:

@onclick="() => ProcessSelection(uniqueCardCombinations[index])"