我在视图中使用多个按钮,每个按钮都会显示自己的弹出页面.同时单击多个按钮时,它一次转到不同的弹出页面.
我创建了一个包含3个按钮的示例内容页面(每个按钮都转到不同的弹出页面)来演示此问题:
XAML页面:
<ContentPage.Content>
<AbsoluteLayout>
<!-- button 1 -->
<Button x:Name="button1" Text="Button 1"
BackgroundColor="White" Clicked="Button1Clicked"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.3, 0.5, 0.1"/>
<!-- button 2 -->
<Button x:Name="button2" Text="Button 2"
BackgroundColor="White" Clicked="Button2Clicked"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.5, 0.1"/>
<!-- button 3 -->
<Button x:Name="button3" Text="Button 3"
BackgroundColor="White" Clicked="Button3Clicked"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.7, 0.5, 0.1"/>
<!-- popup page 1 -->
<AbsoluteLayout x:Name="page1" BackgroundColor="#7f000000" IsVisible="false"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 1.0, 1.0">
<BoxView Color="Red"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.75, 0.3"/>
<Label Text="Button 1 clicked" TextColor="White"
HorizontalTextAlignment="Center"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.45, 0.75, 0.05"/>
<Button Text="Back" BackgroundColor="White" Clicked="Back1Clicked"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.6, 0.5, 0.1"/>
</AbsoluteLayout>
<!-- popup page 2 -->
<AbsoluteLayout x:Name="page2" BackgroundColor="#7f000000" IsVisible="false"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 1.0, 1.0">
<BoxView Color="Green"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.75, 0.3"/>
<Label Text="Button 2 clicked" TextColor="White"
HorizontalTextAlignment="Center"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.45, 0.75, 0.05"/>
<Button Text="Back" BackgroundColor="White" Clicked="Back2Clicked"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.6, 0.5, 0.1"/>
</AbsoluteLayout>
<!-- popup page 3 -->
<AbsoluteLayout x:Name="page3" BackgroundColor="#7f000000" IsVisible="false"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 1.0, 1.0">
<BoxView Color="Blue"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.75, 0.3"/>
<Label Text="Button 3 clicked" TextColor="White"
HorizontalTextAlignment="Center"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.45, 0.75, 0.05"/>
<Button Text="Back" BackgroundColor="White" Clicked="Back3Clicked"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.6, 0.5, 0.1"/>
</AbsoluteLayout>
</AbsoluteLayout>
</ContentPage.Content>
Run Code Online (Sandbox Code Playgroud)
C#事件处理程序:
void Button1Clicked(object sender, EventArgs e)
{
// ... do something first ...
page1.IsVisible = true;
Console.WriteLine("Button 1 Clicked!");
}
void Button2Clicked(object sender, EventArgs e)
{
// ... do something first ...
page2.IsVisible = true;
Console.WriteLine("Button 2 Clicked!");
}
void Button3Clicked(object sender, EventArgs e)
{
// ... do something first ...
page3.IsVisible = true;
Console.WriteLine("Button 3 Clicked!");
}
void Back1Clicked(object sender, EventArgs e)
{
page1.IsVisible = false;
}
void Back2Clicked(object sender, EventArgs e)
{
page2.IsVisible = false;
}
void Back3Clicked(object sender, EventArgs e)
{
page3.IsVisible = false;
}
Run Code Online (Sandbox Code Playgroud)
预期:
单击button1打开page1弹出页面,然后单击弹出窗口中的后退按钮隐藏弹出页面.类似的行为button2和button3.
实际:同时
单击多个按钮(例如.button1和button2)将打开两个弹出页面(page1和page2).快速双击一个按钮也可以两次触发相同的按钮.
关于避免双击的一些研究
通过在stackoverflow中搜索类似的问题(例如this和this),我得出结论,你应该设置一个外部变量来控制事件是否被执行.这是我在Xamarin.forms中的实现:
C#struct作为外部变量,以便我可以在单独的类中访问此变量:
// struct to avoid multiple button click at the same time
public struct S
{
// control whether the button events are executed
public static bool AllowTap = true;
// wait for 200ms after allowing another button event to be executed
public static async void ResumeTap() {
await Task.Delay(200);
AllowTap = true;
}
}
Run Code Online (Sandbox Code Playgroud)
然后像这样修改每个按钮事件处理程序(同样适用于Button2Clicked()和Button3Clicked()):
void Button1Clicked(object sender, EventArgs e)
{
// if some buttons are clicked recently, stop executing the method
if (!S.AllowTap) return; S.AllowTap = false; //##### * NEW * #####//
// ... do something first ...
page1.IsVisible = true;
Console.WriteLine("Button 1 Clicked!");
// allow other button's event to be fired after the wait specified in struct S
S.ResumeTap(); //##### * NEW * #####//
}
Run Code Online (Sandbox Code Playgroud)
这通常很好.双击同一个按钮只能快速触发按钮事件一次,同时单击多个按钮只打开1个弹出页面.
真正的问题
它仍然可能对矫正码(添加一个共享状态变量后打开超过1弹出页AllowTap中struct S),如上所述.例如,如果用户按住button1并button2使用2个手指,释放button1,等待约一秒钟,然后松开button2,都弹出页面page1和page2将被打开.
一个失败的尝试来解决这个问题,
我想如果任一禁用所有按钮button1,button2或者button3点击,并启用所有按钮如果单击后退按钮.
void disableAllButtons()
{
button1.IsEnabled = false;
button2.IsEnabled = false;
button3.IsEnabled = false;
}
void enableAllButtons()
{
button1.IsEnabled = true;
button2.IsEnabled = true;
button3.IsEnabled = true;
}
Run Code Online (Sandbox Code Playgroud)
然后像这样修改每个按钮事件处理程序(同样适用于Button2Clicked()和Button3Clicked()):
void Button1Clicked(object sender, EventArgs e)
{
if (!S.AllowTap) return; S.AllowTap = false;
// ... do something first ...
disableAllButtons(); //##### * NEW * #####//
page1.IsVisible = true;
Console.WriteLine("Button 1 Clicked!");
S.ResumeTap();
}
Run Code Online (Sandbox Code Playgroud)
每个后退按钮事件处理程序都像这样修改(同样适用于Back2Clicked()和Back3Clicked()):
void Back1Clicked(object sender, EventArgs e)
{
page1.IsVisible = false;
enableAllButtons(); //##### * NEW * #####//
}
Run Code Online (Sandbox Code Playgroud)
但是,同样的问题仍然存在(能够按住另一个按钮并稍后释放它们同时触发2个按钮).
在我的应用程序中禁用多点触控不是一个选项,因为我需要在我的应用程序的其他页面中.此外,在弹出的页面还可以包含多个按钮,这会导致其他网页一样,所以简单地使用,在弹出的页面后退按钮设置变量AllowTap的struct S将不会是一种选择为好.
任何帮助将不胜感激.谢谢.
编辑
" 真正的问题 "影响Android和iOS.在Android上,一旦按钮被禁用,当用户按住按钮时,无法激活按钮.这种按住 - 禁用 - 按钮问题不会影响iOS中的按钮.
我的基本视图模型中有这个:
public bool IsBusy { get; set; }
protected async Task RunIsBusyTaskAsync(Func<Task> awaitableTask)
{
if (IsBusy)
{
// prevent accidental double-tap calls
return;
}
IsBusy = true;
try
{
await awaitableTask();
}
finally
{
IsBusy = false;
}
}
Run Code Online (Sandbox Code Playgroud)
命令委托如下所示:
private async Task LoginAsync()
{
await RunIsBusyTaskAsync(Login);
}
Run Code Online (Sandbox Code Playgroud)
...或者如果你有参数:
private async Task LoginAsync()
{
await RunIsBusyTaskAsync(async () => await LoginAsync(Username, Password));
}
Run Code Online (Sandbox Code Playgroud)
登录方法将包含您的实际逻辑
private async Task Login()
{
var result = await _authenticationService.AuthenticateAsync(Username, Password);
...
}
Run Code Online (Sandbox Code Playgroud)
此外,您可以使用内联委托:
private async Task LoginAsync()
{
await RunIsBusyTaskAsync(async () =>
{
// logic here
});
}
Run Code Online (Sandbox Code Playgroud)
不需要 IsEnabled 设置。如果您正在执行的实际逻辑不是异步的,您可以用 Action 替换 Func。
您还可以绑定到 IsBusy 属性以获取诸如 ActivityIndicator 之类的内容
我建议使用 MVVM 方法,并将IsEnabled每个按钮的属性绑定到视图模型中的相同属性,例如AreButtonsEnabled:
MyViewModel.cs:
private bool _areButtonsEnabled = true;
public bool AreButtonsEnabled
{
get => _areButtonsEnabled;
set
{
if (_areButtonsEnabled != value)
{
_areButtonsEnabled = value;
OnPropertyChanged(nameof(AreButtonsEnabled)); // assuming your view model implements INotifyPropertyChanged
}
}
}
Run Code Online (Sandbox Code Playgroud)
MyPage.xaml(仅显示一个按钮的代码):
...
<Button
Text="Back"
BackgroundColor="White"
Clicked="HandleButton1Clicked"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.6, 0.5, 0.1"
IsEnabled={Binding AreButtonsEnabled} />
...
Run Code Online (Sandbox Code Playgroud)
然后对于每个按钮的事件处理程序,您可以将AreButtonsEnabled属性设置为 false 以禁用所有按钮。请注意,您应该首先检查 的值是否AreButtonsEnabled为真,因为在PropertyChanged调用事件之前用户有可能单击两次。但是,由于按钮的单击事件处理程序在主线程上运行,因此AreButtonsEnabled在HandleButtonXClicked调用next 之前,其值将设置为 false 。换句话说,AreButtonsEnabled即使 UI 尚未更新, 的值也会更新。
MyPage.xaml.cs:
HandleButton1Clicked(object sender, EventArgs e)
{
if (viewModel.AreButtonsEnabled)
{
viewModel.AreButtonsEnabled = false;
// button 1 code...
}
}
HandleButton2Clicked(object sender, EventArgs e)
{
if (viewModel.AreButtonsEnabled)
{
viewModel.AreButtonsEnabled = false;
// button 2 code...
}
}
HandleButton3Clicked(object sender, EventArgs e)
{
if (viewModel.AreButtonsEnabled)
{
viewModel.AreButtonsEnabled = false;
// button 3 code...
}
}
Run Code Online (Sandbox Code Playgroud)
然后viewModel.AreButtonsEnabled = true;在您想重新启用按钮时调用。
如果你想要一个“真正的”MVVM 模式,你可以将命令绑定到按钮而不是监听它们的Clicked事件。
MyViewModel.cs:
private bool _areButtonsEnabled = true;
public bool AreButtonsEnabled
{
get => _areButtonsEnabled;
set
{
if (_areButtonsEnabled != value)
{
_areButtonsEnabled = value;
OnPropertyChanged(nameof(AreButtonsEnabled)); // assuming your view model implements INotifyPropertyChanged
}
}
}
public ICommand Button1Command { get; protected set; }
public MyViewModel()
{
Button1Command = new Command(HandleButton1Tapped);
}
private void HandleButton1Tapped()
{
// Run on the main thread, to make sure that it is getting/setting the proper value for AreButtonsEnabled
// And note that calls to Device.BeginInvokeOnMainThread are queued, therefore
// you can be assured that AreButtonsEnabled will be set to false by one button's command
// before the value of AreButtonsEnabled is checked by another button's command.
// (Assuming you don't change the value of AreButtonsEnabled on another thread)
Device.BeginInvokeOnMainThread(() =>
{
if (AreButtonsEnabled)
{
AreButtonsEnabled = false;
// button 1 code...
}
});
}
// don't forget to add Commands for Button 2 and 3
Run Code Online (Sandbox Code Playgroud)
MyPage.xaml(仅显示一个按钮):
<Button
Text="Back"
BackgroundColor="White"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.5, 0.6, 0.5, 0.1"
Command={Binding Button1Command}
IsEnabled={Binding AreButtonsEnabled} />
Run Code Online (Sandbox Code Playgroud)
现在您无需在MyPage.xaml.cs代码隐藏文件中添加任何代码。
将禁用调用(disableAllButtons())移至Pressed按钮事件.
一旦检测到第一次触摸,这将禁用其他按钮.
编辑
为防止意外禁用所有内容,请创建自定义按钮渲染器,并挂钩到本机事件以取消禁用拖出:iOS:UIControlEventTouchDragOutside
编辑
Android已经在这种情况下进行了测试,它有与此问题中描述的相同的问题.
| 归档时间: |
|
| 查看次数: |
4098 次 |
| 最近记录: |