Как реализовать многопоточность в WPF? Нужно сделать программу, которая при поступлении данных на COM порт меняет видео в программе на другое, случайно выбранное из доступных. Суть в том, что бы запустить во втором потоке цикл, который будет читать данные с com порта и менять видео в программе, т.к. когда я просто вхожу в цикл программа просто крашается.
Вот мой код:
OptionsWindow optionsWindow;
SerialPort myPort;
int timer;
public MainWindow()
{
InitializeComponent();
myPort = new SerialPort();
playerMediaElement.Source = new Uri(AppDomain.CurrentDomain.BaseDirectory + @"Videos\0.mp4", UriKind.Absolute);
playerMediaElement.LoadedBehavior = MediaState.Manual;
playerMediaElement.UnloadedBehavior = MediaState.Manual;
playerMediaElement.Play();
using (StreamReader sr = new StreamReader("portOptions.txt", Encoding.Default))
{
myPort.PortName = sr.ReadLine();
myPort.BaudRate = int.Parse(sr.ReadLine());
timer = int.Parse(sr.ReadLine());
}
}
private void PlayerMediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
playerMediaElement.Stop();
playerMediaElement.Play();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if(e.Key == Key.P)
{
optionsWindow = new OptionsWindow();
optionsWindow.Show();
}
}
Нужно выполнять эту функцию в новом потоке или асинхронно (главное, что бы программа не крашилась)
void TestOnNewLog()
{
while (true)
{
if (myPort.ReadLine() != null) MessageBox.Show("Пришли новые данные!");
}
}
Я искал в интернете материал на эту тему, но не помогало... Если кто то знает прошу помочь
Сам по себе запуск потока - задача тривиальная. Как я уже скаазал, вы примеры можете найти в документации или просто поиском в интернете.
Однако, вы должны понимать, что в WPF есть понятие главного UI потока и всё взаимодействие с элементами пользовательского интерфейса должно происходить строго через UI поток.
Возникает вопрос: вот запустили вы свой код в фоновом потоке, и вам надо что то обновить в интерфейсе программы. Как это сделать? Для этого существует диспетчер. Вы можете попросить диспетчер выполнить код в UI потоке.
Как это работает: Например, я создам поток, который посчитает до 100, и при этом будет выводить значение счетчика на экран:
public class MyWnd : Window
{
private TextBlock tb;
public MyWnd()
{
tb = new TextBlock() { Width = 300 };
tb.FontSize = 72;
this.Content = tb;
this.SizeToContent = SizeToContent.WidthAndHeight;
var thread = new Thread(Worker) { IsBackground = true };
thread.Start();
}
private void Worker(object state)
{
for (var i = 0; i < 100; i++)
{
var t = i.ToString();
this.Dispatcher.Invoke(() => { tb.Text = t; });
Thread.Sleep(1000);
}
}
}
Как запустить
new MyWnd().ShowDialog();
Результат
Остается только отметить, что описанный здесь подход хорошо работает на небольших приложениях, но если вы что то более-менее серьезное пишете, то вам стоит обратить внимание на MVVM паттерн.
Вот небольшой пример. Определим простую команду
public class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke(parameter) ?? true;
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
Наша ViewModel будет содежать значение и всю логику по управлению потоками
public class MyViewModel : INotifyPropertyChanged
{
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
private int _value;
public int Value
{
get => _value;
set
{
if (value == _value) return;
_value = value;
OnPropertyChanged();
}
}
private Thread _thread;
private CancellationTokenSource _tokenSource;
public MyViewModel()
{
StartCommand = new DelegateCommand((p) =>
{
_tokenSource = new CancellationTokenSource();
_thread = new Thread(Worker) { IsBackground = true };
_thread.Start(_tokenSource.Token);
},
p => _thread == null);
StopCommand = new DelegateCommand(p =>
{
_tokenSource.Cancel();
_tokenSource = null;
_thread = null;
}, p => _thread != null);
}
private void Worker(object state)
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
Value++;
Thread.Sleep(1000);
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Тут все просто - 2 команды, на старт и стоп, и запуск / остановка потока.
Далее, представление будет вот такое
<Window x:Class="RU_1018395.MainWindow"
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:local="clr-namespace:RU_1018395"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="WidthAndHeight" >
<Window.Resources>
<local:MyViewModel x:Key="viewmodel"></local:MyViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource viewmodel}">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Margin="5">
<Button Command="{Binding StartCommand}">Start</Button>
<Button Command="{Binding StopCommand}">Stop</Button>
</StackPanel>
<TextBlock FontSize="72" Grid.Column="1" Text="{Binding Value}"></TextBlock>
</Grid>
</Window>
В представлении я положил ViewModel как ресурс и далее натянул эту ViewModel на грид. Кстати, можно заметить, что я нигде не использовал диспетчер - всё потому, что я использую привязки для передачи данных между ViewModel и View, а они уже сами понимают, как рабоать с UI потоком.
Ну и результат
С одной стороны может показаться, что второй пример более сложный, но на самом деле мы отделили логику от представления и избежали мешанины, которая у вас в примере в конструкторе главной формы.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Развернул свой Nuget Server на удаленном хостеТакже собрал два NuGet пакета и добавил их напрямую в репозиторий сервера, и выкладываю сервак вместе...
я сделал простенькое aspnet mvc приложение используя EF Database First далее залил на гитхаб, он при скачке на другой машине он не работает, как правильно...