在处理函数期间禁用控件

时间:2015-03-22 19:32:03

标签: c# .net wpf controls

我在处理函数期间在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指令时,而是函数结束时。有什么我做错了或一切都好,这需要以不同的方式完成吗?

2 个答案:

答案 0 :(得分:1)

欢迎使用StackOverflow!

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

<强>建议:

  • 使用ViewModel
  • 绑定到其中的某些属性以启用/禁用您的UI
  • 这样做可以分散顾虑并简化你的事情

示例:禁用用户界面的5秒钟工作(非常简单!)

enter image description here

我的代码中的兴趣点:

  • 将所有必须在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;
}

<强> TODO

熟悉:

第一次使用这些概念时会遇到一些痛苦,但随着时间的推移,你会发现这些是特别的方法。与WPF。

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

答案 1 :(得分:0)

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

我希望我的回答很有帮助。