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

Posted

技术标签:

【中文标题】有没有办法通过单击触发 Blazor 中的两个连续事件?【英文标题】:Is there a way to trigger two consecutive events in Blazor with a single click? 【发布时间】:2021-06-13 03:42:14 【问题描述】:

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

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

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

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

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

最显着的区别是 EventCallback 是单次转换 事件处理程序,而 .NET 事件是多播的。布雷泽 EventCallback 旨在被分配一个值,并且只能 回调单个方法。 https://blazor-university.com/components/component-events/

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

代码 我确实打算在代码开始工作后对其进行清理,使其更具描述性并进一步拆分 ProcessSetReplacement,但希望首先使其工作。 如果您需要更多背景/代码信息,请告诉我,或者您可以在此处找到整个存储库:https://github.com/John-Experimental/GamesInBlazor/tree/21_AddSetValidationVisualisationSetPage.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";
                
            
        ;
    

【问题讨论】:

下次,请将您的问题提取为更简单的问题。强迫他人了解您的特定问题的不必要细节会使事情变得更复杂(和更长)并减少获得答案的机会! 谢谢,我会在下一篇中考虑到这一点:)感谢帮助 【参考方案1】:

当您需要在一个事件中执行多个定时操作时,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])"

【讨论】:

【参考方案2】:

不要在onclick 中调用这两种方法。

致电ProcessSelection,处理结果并在此处设置红色/绿色。 致电StateHasChanged()ProcessSelection 中将计时器设置为一秒。使用来自System.Timers 的计时器。 在定时器调用 ProcessSetReplacement 结束时。

【讨论】:

要添加到此,组件中的任何事件,也就是您的页面,执行然后触发StateHasChanged 以有效地重新呈现组件。所以你需要在第一步之后拨打StateHasChanged。也忘记Thread.Sleep 并使用await Task.Delay(n millisecs)Thread.Sleep 阻塞正在执行的线程,所以在它执行之前什么都不会发生,即没有 UI 更新。您需要将ProcessSetReplacement 标记为异步才能使用await 谢谢,虽然这确实有效,但我发现 Henk Holterman 的解决方案要好一些。仍然赞成这个答案,因为它是第一个并且绝对有用。设法使它工作。

以上是关于有没有办法通过单击触发 Blazor 中的两个连续事件?的主要内容,如果未能解决你的问题,请参考以下文章

在 Blazor 上单击 div 或元素外部以将其关闭的事件

如何通过按钮单击绑定 Blazor 中的 textarea 值?

有没有办法让文件流下载到 Blazor 中的浏览器?

在 python tkinter 中每次连续单击后都会触发双击事件

两次尝试后触发Javascript

ReactJS - 有没有办法通过按 <input/> 中的“Enter”键来触发方法?