在处理函数期间禁用控件
Disabling controls during processing a function
我在处理函数期间禁用 WPF 应用程序中的控件时遇到问题。这是一个通过串口发送数据的简单应用程序。当端口是 "listening" (SerialPort.ReadChar();) 我希望所有控件都去 gray/disable 它们。
这样:
private void startTransmissionButton_Click(object sender, RoutedEventArgs e)
{
ComboBox.IsEnabled = false;
Button1.IsEnabled = false;
Button2.IsEnabled = false;
Button3.IsEnabled = false;
SerialPort com = new SerialPort("COM1");
com.Open();
c = (char)com.ReadChar();
com.Close();
ComboBox.IsEnabled = true;
Button1.IsEnabled = true;
Button2.IsEnabled = true;
Button3.IsEnabled = true;
}
禁用似乎只在函数内部起作用,所以在 window 中实际上没有发生任何事情。当我在函数结束时取消启用时,所有控件都会变灰,但不是在 *.IsEnabled = false
指令的那一刻,而是在函数结束时。是我做错了什么,还是一切正常,这需要以不同的方式完成吗?
欢迎使用 Whosebug!
由于您的代码是同步的,因此它是阻塞的,因此您会得到这种行为。还需要考虑使用 Dispatcher
但幸运的是你没有遇到这样的问题。
建议:
- 使用 ViewModel
- 将其中的某些属性绑定到 enable/disable 您的 UI
- 这样做可以分散关注点并简化您的总体工作
示例:禁用 UI 的 5 秒工作(非常简单!)
我代码中的兴趣点:
- 通过将所有必须禁用的控件放在
StackPanel
中并绑定它的 IsEnabled
属性 模型的 IsAvailable
属性 我有效地简化了这个过程
- 没有从代码隐藏修改控件
- 视图(您的 window)只是呈现,您所有的逻辑都在一个模型中,该模型与您的 window 无关,可以在其他地方重复使用
XAML:
<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainView"
Width="525"
Height="350"
d:DataContext="{d:DesignInstance wpfApplication1:MainViewModel,
d:IsDesignTimeCreatable=True}"
mc:Ignorable="d">
<Grid>
<StackPanel>
<Button Command="{Binding DoSomeWork}" Content="Do some long work" />
<StackPanel IsEnabled="{Binding IsAvailable}">
<CheckBox Content="Test control 1" />
<RadioButton Content="Test control 2" />
</StackPanel>
<TextBlock Text="Overall progress:" />
<ProgressBar Height="10" Value="{Binding CurrentProgress}" />
</StackPanel>
</Grid>
</Window>
代码隐藏:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
// put classes shown below here
}
您的模型:
internal class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
// set-up environment
DoSomeWork = new DelegateCommand(DoSomeWorkExecute, DoSomeWorkCanExecute);
IsAvailable = true;
}
public int CurrentProgress
{
get { return _currentProgress; }
set
{
_currentProgress = value;
OnPropertyChanged();
}
}
#region IsAvailable
private bool _isAvailable;
private int _currentProgress;
public bool IsAvailable
{
get { return _isAvailable; }
set
{
_isAvailable = value;
OnPropertyChanged();
}
}
#endregion
#region DoSomeWork
public DelegateCommand DoSomeWork { get; private set; }
private bool DoSomeWorkCanExecute(object arg)
{
return true;
}
private async void DoSomeWorkExecute(object o)
{
await Task.Run(() =>
{
IsAvailable = false;
var steps = 20;
var time = 5000;
var length = time/steps;
for (var i = 0; i < steps; i++)
{
Thread.Sleep(length);
var currentProgress = (int) (((((double) i + 1)*length)/time)*100);
CurrentProgress = currentProgress;
}
IsAvailable = true;
});
}
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
以及 DoSomeWork
的简单命令基础:
internal class DelegateCommand : ICommand
{
private readonly Func<object, bool> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public DelegateCommand(Action<object> execute)
: this(execute, s => true)
{
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
}
待办事项
熟悉:
- tasks and asynchronous programming
- 用于分离关注点的 MVVM,我建议 Prism, some would consider this is overkill but it has a very good documentation, there are other players such as MVVM Light
- Dispatcher 还有
第一次使用这些概念时,您会感到有些痛苦,但随着时间的推移,您会发现这些是前进的道路,尤其是。使用 WPF。
如果您对我的回答感到满意,请将其标记为答案,否则如果您需要一些说明,请在下面添加评论,我或其他人会尽力提供进一步帮助。
请阅读 Aybe 提供的完整答案。遵循最佳实践总是好的。但是当涉及到小的快速测试项目时,我认为有时它可能有点矫枉过正。
如果您需要快速解决这个问题,那么您可以尝试使用以下方法:
private async void startTransmissionButton_Click(object sender, RoutedEventArgs e)
{
ComboBox.IsEnabled = false;
Button1.IsEnabled = false;
Button2.IsEnabled = false;
Button3.IsEnabled = false;
await
Task.Factory.StartNew(
() =>
{
SerialPort com = new SerialPort("COM1");
com.Open();
c = (char)com.ReadChar();
com.Close();
}
);
ComboBox.IsEnabled = true;
Button1.IsEnabled = true;
Button2.IsEnabled = true;
Button3.IsEnabled = true;
}
请注意,为 c
变量赋值发生在另一个线程中。
希望我的回答对您有所帮助。
我在处理函数期间禁用 WPF 应用程序中的控件时遇到问题。这是一个通过串口发送数据的简单应用程序。当端口是 "listening" (SerialPort.ReadChar();) 我希望所有控件都去 gray/disable 它们。
这样:
private void startTransmissionButton_Click(object sender, RoutedEventArgs e)
{
ComboBox.IsEnabled = false;
Button1.IsEnabled = false;
Button2.IsEnabled = false;
Button3.IsEnabled = false;
SerialPort com = new SerialPort("COM1");
com.Open();
c = (char)com.ReadChar();
com.Close();
ComboBox.IsEnabled = true;
Button1.IsEnabled = true;
Button2.IsEnabled = true;
Button3.IsEnabled = true;
}
禁用似乎只在函数内部起作用,所以在 window 中实际上没有发生任何事情。当我在函数结束时取消启用时,所有控件都会变灰,但不是在 *.IsEnabled = false
指令的那一刻,而是在函数结束时。是我做错了什么,还是一切正常,这需要以不同的方式完成吗?
欢迎使用 Whosebug!
由于您的代码是同步的,因此它是阻塞的,因此您会得到这种行为。还需要考虑使用 Dispatcher
但幸运的是你没有遇到这样的问题。
建议:
- 使用 ViewModel
- 将其中的某些属性绑定到 enable/disable 您的 UI
- 这样做可以分散关注点并简化您的总体工作
示例:禁用 UI 的 5 秒工作(非常简单!)
我代码中的兴趣点:
- 通过将所有必须禁用的控件放在
StackPanel
中并绑定它的IsEnabled
属性 模型的IsAvailable
属性 我有效地简化了这个过程 - 没有从代码隐藏修改控件
- 视图(您的 window)只是呈现,您所有的逻辑都在一个模型中,该模型与您的 window 无关,可以在其他地方重复使用
XAML:
<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainView"
Width="525"
Height="350"
d:DataContext="{d:DesignInstance wpfApplication1:MainViewModel,
d:IsDesignTimeCreatable=True}"
mc:Ignorable="d">
<Grid>
<StackPanel>
<Button Command="{Binding DoSomeWork}" Content="Do some long work" />
<StackPanel IsEnabled="{Binding IsAvailable}">
<CheckBox Content="Test control 1" />
<RadioButton Content="Test control 2" />
</StackPanel>
<TextBlock Text="Overall progress:" />
<ProgressBar Height="10" Value="{Binding CurrentProgress}" />
</StackPanel>
</Grid>
</Window>
代码隐藏:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
// put classes shown below here
}
您的模型:
internal class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
// set-up environment
DoSomeWork = new DelegateCommand(DoSomeWorkExecute, DoSomeWorkCanExecute);
IsAvailable = true;
}
public int CurrentProgress
{
get { return _currentProgress; }
set
{
_currentProgress = value;
OnPropertyChanged();
}
}
#region IsAvailable
private bool _isAvailable;
private int _currentProgress;
public bool IsAvailable
{
get { return _isAvailable; }
set
{
_isAvailable = value;
OnPropertyChanged();
}
}
#endregion
#region DoSomeWork
public DelegateCommand DoSomeWork { get; private set; }
private bool DoSomeWorkCanExecute(object arg)
{
return true;
}
private async void DoSomeWorkExecute(object o)
{
await Task.Run(() =>
{
IsAvailable = false;
var steps = 20;
var time = 5000;
var length = time/steps;
for (var i = 0; i < steps; i++)
{
Thread.Sleep(length);
var currentProgress = (int) (((((double) i + 1)*length)/time)*100);
CurrentProgress = currentProgress;
}
IsAvailable = true;
});
}
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
以及 DoSomeWork
的简单命令基础:
internal class DelegateCommand : ICommand
{
private readonly Func<object, bool> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public DelegateCommand(Action<object> execute)
: this(execute, s => true)
{
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
}
待办事项
熟悉:
- tasks and asynchronous programming
- 用于分离关注点的 MVVM,我建议 Prism, some would consider this is overkill but it has a very good documentation, there are other players such as MVVM Light
- Dispatcher 还有
第一次使用这些概念时,您会感到有些痛苦,但随着时间的推移,您会发现这些是前进的道路,尤其是。使用 WPF。
如果您对我的回答感到满意,请将其标记为答案,否则如果您需要一些说明,请在下面添加评论,我或其他人会尽力提供进一步帮助。
请阅读 Aybe 提供的完整答案。遵循最佳实践总是好的。但是当涉及到小的快速测试项目时,我认为有时它可能有点矫枉过正。
如果您需要快速解决这个问题,那么您可以尝试使用以下方法:
private async void startTransmissionButton_Click(object sender, RoutedEventArgs e)
{
ComboBox.IsEnabled = false;
Button1.IsEnabled = false;
Button2.IsEnabled = false;
Button3.IsEnabled = false;
await
Task.Factory.StartNew(
() =>
{
SerialPort com = new SerialPort("COM1");
com.Open();
c = (char)com.ReadChar();
com.Close();
}
);
ComboBox.IsEnabled = true;
Button1.IsEnabled = true;
Button2.IsEnabled = true;
Button3.IsEnabled = true;
}
请注意,为 c
变量赋值发生在另一个线程中。
希望我的回答对您有所帮助。