【问题标题】:Disabling controls during processing a function在处理函数期间禁用控件
【发布时间】:2015-03-22 19:32:03
【问题描述】:

在处理函数期间,我在禁用 WPF 应用程序中的控件时遇到问题。它是通过串口发送数据的简单应用程序。当端口“侦听”时(SerialPort.ReadChar();)我希望所有控件都变灰/禁用它们。

这边:

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;
    }

禁用似乎只在函数内部起作用,因此窗口中实际上没有发生任何事情。当我在功能结束时删除启用时,所有控件都会变灰,但不是在 *.IsEnabled = false 指令的那一刻,而是在功能结束时。是不是我做错了什么,或者一切正常,需要以不同的方式完成?

【问题讨论】:

  • 如果监听需要时间,那么由于您共享的整个代码都是由同一个线程处理的,所以您遇到的情况是正常的。要解决它,您可能需要以多线程方式执行此操作。应该有一个单独的线程来控制你的 UI 处理,例如更新它的视图状态。
  • 好的,谢谢,我明白了。不幸的是,我是 C# 的初学者,不知道如何正确创建线程,所以如果有任何不同的解决方案,那就太好了。
  • 那么这是一个通过实践了解更多信息的好机会。这是一个可能有用的链接。 stackoverflow.com/questions/661561/…
  • 其实你是对的,不用找临时解决办法,谢谢帮助!

标签: c# .net wpf controls


【解决方案1】:

请阅读 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 变量赋值发生在另一个线程中。

希望我的回答对你有帮助。

【讨论】:

    【解决方案2】:

    欢迎来到 StackOverflow!

    由于您的代码是同步的,因此它是阻塞的,因此您得到的行为。还需要考虑使用Dispatcher,但幸运的是,您没有遇到此类问题。

    建议:

    • 使用 ViewModel
    • 绑定到其中的一些属性以启用/禁用您的 UI
    • 这样做可以分离关注点并总体上简化您的工作

    示例:禁用 UI 的 5 秒工作(真的很简单!)

    我的代码的兴趣点:

    • 通过将所有必须禁用的控件放在StackPanel 中并将其IsEnabled 属性绑定到模型的IsAvailable 属性,我有效地简化了这个过程
    • 没有从代码隐藏修改任何控件
    • 视图(您的窗口)只不过是呈现而已,您的所有逻辑都在一个模型中,该模型与您的窗口无关,可以在其他地方重复使用

    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;
    }
    

    待办事项

    熟悉:

    第一次使用这些概念时,您会感到有些痛苦,但随着时间的推移,您会发​​现这些是要走的路。使用 WPF。

    如果您对我的回答感到满意,请将其标记为答案,否则如果您需要澄清,请在下方添加评论,我或其他人会尽力提供帮助。

    【讨论】:

    • 我经常在 SO 周围看到这些漂亮的 gif,你用什么工具来制作它们? +1 以获得详细答案。
    • 我使用 CamStudio 录制 AVI,然后查看 superuser.com/questions/436056/… 以从中生成一个小 GIF。
    猜你喜欢
    • 2015-12-25
    • 1970-01-01
    • 2016-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 2016-04-19
    相关资源
    最近更新 更多