2015-03-22 2 views
1

У меня проблема с отключением элементов управления в приложении 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, но когда функции заканчиваются. Есть ли что-то, что я делаю неправильно, или все в порядке, и это нужно делать по-другому?

+0

Если прослушивание занимает время, так как весь код, который вы использовали, обрабатывается одним и тем же потоком, то, что вы испытываете, является нормальным. Чтобы решить эту проблему, вам, вероятно, нужно сделать это многопоточным способом. Должен быть отдельный поток, который контролирует обработку пользовательского интерфейса, например, обновляет его состояние просмотра. – alainlompo

+0

Хорошо, спасибо, я понимаю. К сожалению, я новичок в C# и не знаю, как правильно создавать потоки, поэтому, если есть какое-то другое решение, было бы здорово. – Kacper

+0

Тогда это отличная возможность узнать больше о некоторых практиках. Вот ссылка, которая может быть полезна. http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c – alainlompo

ответ

1

Добро пожаловать в StackOverflow!

Поскольку ваш код является синхронным, он блокирует, следовательно, поведение вы получаете. Также необходимо рассмотреть возможность использования Dispatcher, но, к счастью, в вашем случае вы не столкнулись с такой проблемой.

Предложения:

  • Используйте ViewModel
  • связывают с некоторыми свойствами в нем, чтобы включить/отключить интерфейс
  • этом разделяет озабоченность и упрощает вашу вещь в общем

Пример: 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> 

Code-за:

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

Разберитесь с:

  • tasks and asynchronous programming
  • MVVM для разделения проблемы, я бы предложить Prism, некоторые будут считать это излишним, но он имеет очень хорошую документацию, есть и другие игроки, такие как MVVM Light
  • Dispatcher а

Вы будете испытывать некоторую боль с этими понятиями первый раз, но со временем вы обнаружите, что это способ пойти esp. с WPF.

Если вы удовлетворены моим ответом, отметьте его как ответ, иначе, если вам нужно уточнение, добавьте комментарий ниже, и либо я, либо кто-то попытается помочь дальше.

+0

Я продолжаю видеть эти хорошие gifs вокруг SO, какой инструмент вы используете для их создания? +1 для подробного ответа. – learningcs

+0

Я использую CamStudio для записи AVI, а затем см. Http://superuser.com/questions/436056/how-can-i-get-ffmpeg-to-convert-a-mov-to-a-gif для создания крошечного GIF из него. – Aybe

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 переменной происходит в другом потоке.

Надеюсь, мой ответ будет полезен.

Смежные вопросы