Ich weiß, es gibt eine Menge Informationen über Stackoverflow, aber finde nichts, das mein Problem gelöst.Update UI von einem anderen Thread arbeiten nur einmal
Ich habe ein Programm erstellt, um ffmpeg mit einigen Videodateien zu verwenden. Dieser Prozess kann einige Minuten dauern. Ich versuche also, einen Fortschrittsbalken für ein anderes Formular zu erstellen.
Grundsätzlich, wenn ich auf eine Schaltfläche auf meinem Hauptformular() klicke, wird ein neues Formular angezeigt. Dieses Formular hat nur eine Fortschrittsanzeige und eine Abbrechen-Taste (rufen Sie FormProgress
).
Um den ffmpeg auszuführen, verwende ich eine andere Klasse (VideoProcessing
), um einen neuen Prozess zu erstellen, ffmpeg auszuführen und den stderror zu überwachen (ffmpeg show progress on stderror). Jedes Mal, wenn ffmpeg einen Fortschritt anzeigt, analysiert diese Klasse die Ausgabe, berechnet den Fortschritt und löst ein Ereignis aus (OnMergeProgress
).
Im Grunde ist der Code:
FormSync:
public partial class FormSync : Form
{
// this form show the progress of job
private FormProgress _formProgress;
// start the ffmpeg when click on button
private void mergeButton_click(object sender, EventArgs e)
{
var files = new List<string>() {"file1.mp4", "file2.mp4"};
MergeFiles(files);
}
// join all video files on a single file (using ffmpeg)
private void MergeFiles(IEnumerable<string> videoFiles)
{
// instantiate the class that execute ffmpeg
VideoProcessing videoProcessing = new VideoProcessing();
// this class has a a event to show current progress
// seconds = total seconds of video (sum length of all video files)
// currentSeconds = current progress
videoProcessing.OnMergeProgress += (seconds, currentSeconds) =>
{
Invoke((MethodInvoker) delegate()
{
// Instantiate the form of progress if not visible
if (_formProgress = null)
{
// define the minimum and maximum value of progressbar on constructor
_formProgress = new FormProgress(0, seconds);
_formProgress.ShowDialog(this);
}
// update the progress bar value
_formProgress.SetProgress(currentSeconds);
}
}
}
}
FormProgress:
public partial class FormProgress : Form
{
public FormProgress(int min, int max)
{
InitializeComponent();
progressBar.Minimum = min;
progressBar.Maximum = max;
}
public void SetProgress(int value)
{
value = (value <= progressBar.Minimum)
? progressBar.Minimum
: (value >= progressBar.Maximum) ? progressBar.Maximum : value;
progressBar.Value = value;
Refresh();
}
}
VideoProcessing:
internal class VideoProcessing
{
// Events
public delegate void MergeProgressHandler(int totalSeconds, int currentSeconds);
public event MergeProgressHandler OnMergeProgress;
private int _totalTimeVideos;
public void MergeFiles(string[] videoFiles)
{
// calculate total time of all videos
_totalTimeVideos = SomeFunctionToCalculateTotalTime();
// create the Process object to execute FFMPEG (with stdout and stderr redirection)
_process = CreateFFMPEGProcess(videoFiles);
}
// capture the stdout and stderr of ffmpeg
private void MergeOutputHandler(object sendingProcess, DataReceivedEventArgs outline)
{
// capture the current progress
// here will go a regex, and some other code to parse the info from ffmpeg
// Raise the event
OnMergeProgress?.Invoke(_totalTimeVideos, progressSeconds);
}
}
Grundsätzlich ist die FFMPEG Ausführung und Capture-Verfahren den folgenden Code verwenden: C# execute external program and capture (stream) the output
Das Problem tritt auf, wenn ich versuche, den Code auszuführen.
Wenn ich auf die Schaltfläche que klicken, wird FormProgress angezeigt, aber danach die Statusleiste "einfrieren". Das Programm funktioniert gut, es hängt hier nicht, aber keine Aktualisierung auf Fortschrittsbalken.
Wenn in FormSync
, auf InvokeMethod
ich den ursprünglichen Code mit folgendem Inhalt ersetzen, ich, dass ffmpeg arbeitet sehen kann, und meine Ereignisse arbeiten zu:
videoProcessing.OnMergeProgress += (seconds, currentSeconds) =>
{
Debug.WriteLine($"{currentSeconds}/{seconds}");
}
das Problem also nicht war ffmpeg oder meine Videoklasse, aber etwas, das die Benutzeroberfläche aktualisiert.
Wenn ich die Invoke
wieder ändern, aber diesmal mit dem Debug
, wie Code unten, die Debug
Druck nur das erste Update, und nichts mehr:
videoProcessing.OnMergeProgress += (seconds, currentSeconds) =>
{
Invoke((MethodInvoker) delegate() {
if (_formProgress == null) {
_formProgress = new FormProgress(Resources.merging_video_files, 0, seconds);
_formProgress.ShowDialog(this);
}
_formProgress.SetProgress(currentSeconds);
});
Debug.WriteLine($"{currentSeconds}/{seconds}");
}
nur einen schnellen Test zu tun, änderte ich 'ShowDialog', zu' show', und alles funktionierte gut. Jetzt Zeit für eine Recherche über Begin Invoke! Tks! (Der Fortschrittsbalken-Maximalwert wird im Formularkonstruktor initialisiert.) –