Threading lock in WinForms - c#

I have a class that generates images in a thread and invokes an event on every frame. A Form subscribes to this event and displays the image in a PictureBox using Invoke.
If the image generation process is allowed to finish, all is good. However, if the Form is closed while the thread is running, the Form tries to stop the thread but ends up in some sort of a deadlock.
When I try to use the threaded class in without a Form or in a Console app, start the process, wait for a second, then Cancel it, everything works fine.
The question is, something must be wrong with either the Form_Closing method of the Form or the Stop method of the threaded class.
I have kept the code to a minimum and it can be pasted into LinqPad, etc.
An auxiliary question: Should the Process method invoke the Stop method to clean up?
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace VideoMask.WinFormsAppApp
{
internal static class Program
{
[STAThread]
private static void Main ()
{
Program.TestWithoutForm(); // Runs fine.
Program.TestWithForm(); // Deadlocks.
}
private static void TestWithForm ()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormMain());
}
private static void TestWithoutForm ()
{
new TestWithoutForm().Run();
}
}
public class TestWithoutForm
{
private VideoProcessor VideoProcessor = new VideoProcessor();
public TestWithoutForm ()
{
this.VideoProcessor.SourceFrameRead += this.VideoProcessor_SourceFrameRead;
}
public void Run ()
{
this.VideoProcessor.Start();
Thread.Sleep(1000);
this.VideoProcessor.Stop();
MessageBox.Show("Done");
}
private void VideoProcessor_SourceFrameRead (object sender, VideoProcessorEventArgs e)
{
var filename = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Sample.png");
e.Bitmap.Save(filename, ImageFormat.Png);
}
}
public class FormMain: Form
{
private PictureBox PictureBox = new PictureBox();
private VideoProcessor VideoProcessor = new VideoProcessor();
public FormMain ()
{
this.Controls.Add(this.PictureBox);
this.PictureBox.Dock = DockStyle.Fill;
this.Shown += this.FormMain_Shown;
this.FormClosing += this.FormMain_FormClosing;
this.WindowState = FormWindowState.Maximized;
}
private void FormMain_Shown (object sender, EventArgs e)
{
this.VideoProcessor.SourceFrameRead += this.VideoProcessor_SourceFrameRead;
this.VideoProcessor.Start();
}
private void FormMain_FormClosing (object sender, FormClosingEventArgs e)
{
this.VideoProcessor.Stop();
this.VideoProcessor.Dispose();
this.VideoProcessor = null;
}
private void VideoProcessor_SourceFrameRead (object sender, VideoProcessorEventArgs e)
{
this.Invoke
(
new Action
(
() =>
{
using (var bitmap = this.PictureBox.Image)
{
this.PictureBox.Image = new Bitmap(e.Bitmap);
}
}
)
);
}
}
public sealed class VideoProcessor: IDisposable
{
public event EventHandler<ErrorEventArgs> ConversionError = null;
public event EventHandler<VideoProcessorEventArgs> SourceFrameRead = null;
private Font Font = null;
private Bitmap Bitmap = null;
private Thread Thread = null;
private Graphics Graphics = null;
private readonly object SyncRoot = new object();
private CancellationTokenSource CancellationTokenSource = null;
public VideoProcessor ()
{
this.Bitmap = new Bitmap(800, 600, PixelFormat.Format32bppArgb);
this.Graphics = Graphics.FromImage(this.Bitmap);
this.Font = new Font(FontFamily.GenericSansSerif, 48, GraphicsUnit.Point);
}
public bool IsRunning { get; private set; }
public void Start ()
{
lock (this.SyncRoot)
{
if (this.IsRunning) { throw (new Exception("A video conversion process is already running.")); }
this.Stop();
this.CancellationTokenSource = new CancellationTokenSource();
this.Thread = new Thread(new ParameterizedThreadStart(this.Process));
this.Thread.Start(this.CancellationTokenSource);
}
}
public void Stop ()
{
lock (this.SyncRoot)
{
if (!this.IsRunning) { return; }
this.CancellationTokenSource?.Cancel();
this.Thread.Join();
this.Thread = null;
this.CancellationTokenSource?.Dispose();
this.CancellationTokenSource = null;
}
}
private void Process (object cancellationTokenSource)
{
var source = (CancellationTokenSource) cancellationTokenSource ?? throw (new ArgumentNullException(nameof(cancellationTokenSource)));
lock (this.SyncRoot) { if (this.IsRunning) { throw (new Exception("A conversion process is already running.")); } }
this.IsRunning = true;
for (var i = 1; i <= int.MaxValue; i++)
{
if (source.IsCancellationRequested) { break; }
this.Graphics.Clear(Color.White);
this.Graphics.DrawString(i.ToString(), this.Font, Brushes.Black, 10, 10);
this.SourceFrameRead?.Invoke(this, new VideoProcessorEventArgs(this.Bitmap));
Thread.Sleep(33);
}
this.IsRunning = false;
}
public void Dispose () => this.Stop();
}
public class VideoProcessorEventArgs: EventArgs
{
public Bitmap Bitmap { get; private set; }
public VideoProcessorEventArgs (Bitmap bitmap) { this.Bitmap = bitmap; }
}
}

Related

I want to save a video come from camera as mp4 on wpf

I am trying to save the image from the camera as mp4(etc) on WPF application. But so far I have not been successful.
Thanks for your help.
Sorry for my bad english at first.I am receiving the image from the webcam. Below are the codes.
public partial class MainWindow : Window
{
private FilterInfoCollection cihazlar;
private VideoCaptureDevice yakala_resim
;
public MainWindow()
{
InitializeComponent();
cihazlar = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (FilterInfo device in cihazlar)
{
kameralar.Items.Add(device.Name);
kameralar.SelectedIndex = 2;
}
}
private void Başla_Click(object sender, RoutedEventArgs e)
{
yakala_resim = new VideoCaptureDevice(cihazlar[kameralar.SelectedIndex].MonikerString);
yakala_resim.NewFrame += Yakala_NewFrame1;
yakala_resim.Start();
}
private void Yakala_NewFrame1(object sender, NewFrameEventArgs eventArgs)
{
try
{
BitmapImage bi;
using (var bitmap = (Bitmap)eventArgs.Frame.Clone())
{
bi = bitmap.ToBitmapImage();
}
bi.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate { video.Source = bi; }));
}
catch (Exception)
{
}
}
no problem currently. But I don't know what to do when I want to save the image come from webcam as mp4.
You can use from OpenCvSharp
namespace BlackBears.Recording
{
using System;
using System.Drawing;
using System.Threading;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;
public class Recorder : IDisposable
{
private readonly VideoCaptureAPIs _videoCaptureApi = VideoCaptureAPIs.DSHOW;
private readonly ManualResetEventSlim _writerReset = new(false);
private readonly VideoCapture _videoCapture;
private VideoWriter _videoWriter;
private Thread _writerThread;
private bool IsVideoCaptureValid => _videoCapture is not null && _videoCapture.IsOpened();
public Recorder(int deviceIndex, int frameWidth, int frameHeight, double fps)
{
_videoCapture = VideoCapture.FromCamera(deviceIndex, _videoCaptureApi);
_videoCapture.Open(deviceIndex, _videoCaptureApi);
_videoCapture.FrameWidth = frameWidth;
_videoCapture.FrameHeight = frameHeight;
_videoCapture.Fps = fps;
}
/// <inheritdoc />
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
~Recorder()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
StopRecording();
_videoCapture?.Release();
_videoCapture?.Dispose();
}
}
public void StartRecording(string path)
{
if (_writerThread is not null)
return;
if (!IsVideoCaptureValid)
ThrowHelper.ThrowVideoCaptureNotReadyException();
_videoWriter = new VideoWriter(path, FourCC.XVID, _videoCapture.Fps, new Size(_videoCapture.FrameWidth, _videoCapture.FrameHeight));
_writerReset.Reset();
_writerThread = new Thread(AddCameraFrameToRecordingThread);
_writerThread.Start();
}
public void StopRecording()
{
if (_writerThread is not null)
{
_writerReset.Set();
_writerThread.Join();
_writerThread = null;
_writerReset.Reset();
}
_videoWriter?.Release();
_videoWriter?.Dispose();
_videoWriter = null;
}
private void AddCameraFrameToRecordingThread()
{
var waitTimeBetweenFrames = (int)(1_000 / _videoCapture.Fps);
using var frame = new Mat();
while (!_writerReset.Wait(waitTimeBetweenFrames))
{
if (!_videoCapture.Read(frame))
return;
_videoWriter.Write(frame);
}
}
}
}
Recording Video from Webcam with OpenCvSharp - Resulting File playback to fast

pjsip c#, System.AccessViolationException on answering an incoming call on an separated thread

In the overrided void onIncomingCall I open a new Background thread which shows a new (Popup) Form.
I try to pickup or decline the Call in this Form but then I get an System.AccessViolationException.
it seems that the current call object is locked and cannot be accessed from another Form.
If I use the currentcall.answer(prm) in the onIncomingCall void then the call is established successfully (without another Form and thread).
public class myaccount : Account
{
public override void onIncomingCall(OnIncomingCallParam prm)
{
Call call = new Call(this, prm.callId);
CallOpParam param = new CallOpParam();
param.statusCode = pjsip_status_code.PJSIP_SC_RINGING;
param.statusCode = pjsip_status_code.PJSIP_SC_OK;
pjsipfunctions.currentparam = param;
pjsipfunctions.currentcall = call;
var thread = new Thread(() =>
{
ShowPopup(call,prm.callId.ToString());
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
// Thread myCallingThread = new Thread(new ThreadStart(ShowPopup));
//myCallingThread.SetApartmentState(ApartmentState.STA);
//myCallingThread.IsBackground = true;
//myCallingThread.Start();
}
public void ShowPopup(Call myCall,string call_id)
{
IncommingCallPopup win = new IncommingCallPopup(ref myCall, call_id );
win.Show();
System.Windows.Threading.Dispatcher.Run();
}
}
#####################################
public IncommingCallPopup(ref Call info, string callid)
{
currentCall = info;
Callid = callid;
CurrentCall = currentCall;
Prm.statusCode = pjsip_status_code.PJSIP_SC_RINGING;
InitializeComponent();
labelCallId.Content = callid;
}
private void rejectcall(object sender, RoutedEventArgs e)
{
Prm.statusCode = pjsip_status_code.PJSIP_SC_DECLINE;
CurrentCall.hangup(Prm);
}
private void transfercall(object sender, RoutedEventArgs e)
{
}
private void takecall(object sender, RoutedEventArgs e)
{
Prm.statusCode = pjsip_status_code.PJSIP_SC_OK;
try
{
CurrentCall.answer(Prm);
}
catch(Exception ex)
{
}
}
After crazy searching and trying some things i've figured it out.
Every Thread must registered in the Endpoint that you can hangup, pickup or transfer your call in another Class(Window).
All you need is to write this Method in a Public class and call this void everytime you call your void from another window ->
public void callpopup(String number) {
checkThread();
//answer, decline or something else
}
[MethodImpl(MethodImplOptions.Synchronized)]
public static void checkThread()
{
try
{
if (ep != null && !ep.libIsThreadRegistered())
ep.libRegisterThread(Thread.CurrentThread.Name);
}
catch (Exception e)
{
}
}

C# Tasks - am I experiencing a suspicious increasing of number of handles

I am dipping my toes in Tasks (.NET 4.5) and am experiencing increasing handles (in task manager). I have a class doing a simple Play/Stop of audio file using MediaPlayer class (System.Windows.Media namespace). I have a second class which wraps it and exposes sync/async playback.
All is working fine and functionality is OK, but see the number of handles increasing in Task manager which worries me.... Am I doing something wrong here?
Important note: if I comment our the "await Task.Delay(1000);" --> then all is just fine and no leaks are observed... How come??
public partial class Form1 : Form
{
AudioActions syncAction = new AudioActions(#"c:\1.wav", false);
AudioActions asyncAction = new AudioActions(#"c:\1.wav", true);
public Form1()
{
InitializeComponent();
}
private async void syncPlayback(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
await syncAction.Start();
}
}
private async void asyncPlayback(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
await asyncAction.Start();
await Task.Delay(100); //remove this line and all is fine!!!!
asyncAction.Stop();
}
Console.WriteLine("done");
}
}
public class AudioActions
{
private Audio audio = null;
private TaskCompletionSource<bool> tcs = null;
private string pathToWaveFile;
private bool async;
public AudioActions(string pathToWaveFile, bool async)
{
this.pathToWaveFile=pathToWaveFile;
this.async=async;
}
public Task Start()
{
tcs = new TaskCompletionSource<bool>();
audio = new Audio();
audio.mediaPlayerPlaybackStoppedEvent += Audio_wmPlaybackStopped;
if (async)
tcs.TrySetResult(true); //since its async operation, lets return immediately and free the task from waiting
audio.PlayAudioFileInMediaPlayer(pathToWaveFile);
return tcs.Task;
}
private void Audio_wmPlaybackStopped()
{
audio.mediaPlayerPlaybackStoppedEvent -= Audio_wmPlaybackStopped;
tcs.TrySetResult(true); //playback stopped. Lets free the task from waiting
}
public void Stop()
{
audio.StopAudioFilePlaybackInMediaPlayer();
}
}
public class Audio
{
MediaPlayer mediaPlayer = null;
public delegate void MediaPlayerPlaybackStoppedDelegate();
public event MediaPlayerPlaybackStoppedDelegate mediaPlayerPlaybackStoppedEvent;
public void PlayAudioFileInMediaPlayer(string pathToWavFile)
{
mediaPlayer = new MediaPlayer();
mediaPlayer.MediaEnded += mediaPlayer_MediaEnded;
mediaPlayer.Open(new Uri(pathToWavFile));
mediaPlayer.Play();
}
void mediaPlayer_MediaEnded(object sender, EventArgs e)
{
mediaPlayerPlaybackStoppedEvent.Invoke();
MediaPlayer mediaPlayer = (MediaPlayer)sender;
mediaPlayer.MediaEnded -= mediaPlayer_MediaEnded;
mediaPlayer.Close();
mediaPlayer = null;
}
public void StopAudioFilePlaybackInMediaPlayer()
{
mediaPlayer.Stop();
mediaPlayer.Close();
mediaPlayer = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
Adding a strip down code:
private async void asyncPlayback(object sender, EventArgs e)
{
AudioActions asyncAction = new AudioActions();
for (int i = 0; i < 100; i++)
{
await asyncAction.Start();
await Task.Delay(1000); //remove this line and all is fine!!!!
asyncAction.Stop();
}
}
public class AudioActions
{
private Audio audio = null;
private TaskCompletionSource<bool> tcs = null;
public Task Start()
{
tcs = new TaskCompletionSource<bool>();
audio = new Audio();
tcs.TrySetResult(true); //since its async operation, lets return immediately and free the task from waiting
audio.PlayAudioFileInMediaPlayer(#"c:\1.wav");
return tcs.Task;
}
public void Stop()
{
audio.StopAudioFilePlaybackInMediaPlayer();
}
}
public class Audio
{
MediaPlayer mediaPlayer = null;
public void PlayAudioFileInMediaPlayer(string pathToWavFile)
{
mediaPlayer = new MediaPlayer();
mediaPlayer.Open(new Uri(pathToWavFile));
mediaPlayer.Play();
}
public void StopAudioFilePlaybackInMediaPlayer()
{
mediaPlayer.Stop();
mediaPlayer.Close();
mediaPlayer = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
Modified class solving the problem:
public class Audio
{
public delegate void NaudioPlaybackStoppedDelegate();
public event NaudioPlaybackStoppedDelegate naudioPlaybackStoppedEvent;
private WaveOut player = null;
public void PlayAudioFileUsingNaudio(string pathToWavFile)
{
player = new WaveOut();
AudioFileReader waveFileReader = new AudioFileReader(pathToWavFile);
player.Init(waveFileReader);
player.PlaybackStopped += NAudio_Stopped;
player.Play();
}
private void NAudio_Stopped(object sender, StoppedEventArgs e)
{
player.PlaybackStopped -= NAudio_Stopped;
if (naudioPlaybackStoppedEvent!=null)
naudioPlaybackStoppedEvent.Invoke();
player.Dispose();
}
public void StopAudioFilePlaybackInNaudio()
{
player.Stop();
}
}
Replacing MediaPlayer with NAudio 3rd party solved it.
public class Audio
{
public delegate void NaudioPlaybackStoppedDelegate();
public event NaudioPlaybackStoppedDelegate naudioPlaybackStoppedEvent;
private WaveOut player = null;
public void PlayAudioFileUsingNaudio(string pathToWavFile)
{
player = new WaveOut();
AudioFileReader waveFileReader = new AudioFileReader(pathToWavFile);
player.Init(waveFileReader);
player.PlaybackStopped += NAudio_Stopped;
player.Play();
}
private void NAudio_Stopped(object sender, StoppedEventArgs e)
{
player.PlaybackStopped -= NAudio_Stopped;
if (naudioPlaybackStoppedEvent!=null)
naudioPlaybackStoppedEvent.Invoke();
player.Dispose();
}
public void StopAudioFilePlaybackInNaudio()
{
player.Stop();
}
}

Updating UI from background thread issues

I'm coding a class to move and copy files. I'm raising events when the current file progress and the total progress changes. When I test the code on my XP machine, it works fine, but when I run it on my Windows 7 64-Bit machine, the current progress doesn't update the UI correctly. The current progress ProgressBar only gets half way then starts on the next file which does the same. The total progress ProgressBar updates fine. Any ideas why this is happening?
EDIT: The Windows 7 machine is running a quad-core and the XP is running a dual-core. Not sure if that might be what's making a difference. I'm only a hobbyist so excuse my ignorance :)
EDIT: Code added (Background)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;
namespace nGenSolutions.IO
{
public class FileTransporter
{
#region Delegates
public delegate void CurrentFileChangedEventHandler(string fileName);
public delegate void CurrentProgressChangedEventHandler(int percentComplete);
public delegate void CurrentWriteSpeedUpdatedEventHandler(long bytesPerSecond);
public delegate void TotalProgressChangedEventHandler(int percentComplete);
public delegate void TransportCompleteEventHandler(FileTransportResult result);
#endregion
private readonly List<string> _destinationFiles = new List<string>();
private readonly List<string> _sourceFiles = new List<string>();
private long _bytesCopiedSinceInterval;
private FileTransportResult _result;
private Timer _speedTimer;
private long _totalDataLength;
private BackgroundWorker _worker;
public bool TransportInProgress { get; private set; }
public event CurrentFileChangedEventHandler CurrentFileChanged;
public event CurrentProgressChangedEventHandler CurrentProgressChanged;
public event CurrentWriteSpeedUpdatedEventHandler CurrentWriteSpeedUpdated;
public event TotalProgressChangedEventHandler TotalProgressChanged;
public event TransportCompleteEventHandler TransportComplete;
public void AddFile(string sourceFile, string destinationFile)
{
if (!File.Exists(sourceFile))
throw new FileNotFoundException("The specified file does not exist!", sourceFile);
var fileInfo = new FileInfo(sourceFile);
_totalDataLength += fileInfo.Length;
_sourceFiles.Add(sourceFile);
_destinationFiles.Add(destinationFile);
}
public void BeginTransport()
{
// update the write speed every 3 seconds
_speedTimer = new Timer {Interval = 3000};
_speedTimer.Elapsed += SpeedTimerElapsed;
_worker = new BackgroundWorker();
_worker.DoWork += DoTransport;
_worker.RunWorkerCompleted += WorkerCompleted;
_worker.RunWorkerAsync();
_speedTimer.Start();
TransportInProgress = true;
}
private void SpeedTimerElapsed(object sender, ElapsedEventArgs e)
{
InvokeCurrentSpeedUpdated(_bytesCopiedSinceInterval);
_bytesCopiedSinceInterval = 0;
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TransportInProgress = false;
InvokeTransportComplete(_result);
}
public void CancelTransport(bool rollbackChanges)
{
if (TransportInProgress == false)
throw new InvalidOperationException("You tried to stop the transport before you started it!");
_result = FileTransportResult.Cancelled;
_worker.CancelAsync();
while (_worker.IsBusy)
{
// wait for worker to die an 'orrible death
}
// TODO: rollback changes if requested
}
private void DoTransport(object sender, DoWorkEventArgs e)
{
long totalBytesCopied = 0;
int totalPercentComplete = 0;
for (int i = 0; i < _sourceFiles.Count; i++)
{
string sourceFile = _sourceFiles[i];
string destinationFile = _destinationFiles[i];
long currentFileLength = new FileInfo(sourceFile).Length;
InvokeCurrentFileChanged(sourceFile);
using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
{
using (var destinationStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
{
using (var reader = new BinaryReader(sourceStream))
{
using (var writer = new BinaryWriter(destinationStream))
{
int lastPercentComplete = 0;
for (int j = 0; j < currentFileLength; j++)
{
writer.Write(reader.ReadByte());
totalBytesCopied += 1;
_bytesCopiedSinceInterval += 1;
int current = Convert.ToInt32((j/(double) currentFileLength)*100);
int total = Convert.ToInt32((totalBytesCopied/(double) _totalDataLength)*100);
// raise progress events every 3%
if (current%3 == 0)
{
// only raise the event if the progress has increased
if (current > lastPercentComplete)
{
lastPercentComplete = current;
InvokeCurrentProgressChanged(lastPercentComplete);
}
}
if (total%3 == 0)
{
// only raise the event if the progress has increased
if (total > totalPercentComplete)
{
totalPercentComplete = total;
InvokeTotalProgressChanged(totalPercentComplete);
}
}
}
}
InvokeCurrentProgressChanged(100);
}
}
}
}
InvokeTotalProgressChanged(100);
}
private void InvokeCurrentFileChanged(string fileName)
{
CurrentFileChangedEventHandler handler = CurrentFileChanged;
if (handler == null) return;
handler(fileName);
}
private void InvokeCurrentProgressChanged(int percentComplete)
{
CurrentProgressChangedEventHandler handler = CurrentProgressChanged;
if (handler == null) return;
handler(percentComplete);
}
private void InvokeCurrentSpeedUpdated(long bytesPerSecond)
{
CurrentWriteSpeedUpdatedEventHandler handler = CurrentWriteSpeedUpdated;
if (handler == null) return;
handler(bytesPerSecond);
}
private void InvokeTotalProgressChanged(int percentComplete)
{
TotalProgressChangedEventHandler handler = TotalProgressChanged;
if (handler == null) return;
handler(percentComplete);
}
private void InvokeTransportComplete(FileTransportResult result)
{
TransportCompleteEventHandler handler = TransportComplete;
if (handler == null) return;
handler(result);
}
}
}
EDIT: Code added (GUI)
using System;
using System.IO;
using System.Windows.Forms;
using ExtensionMethods;
using nGenSolutions.IO;
namespace TestApplication
{
public partial class ProgressForm : Form
{
public ProgressForm()
{
InitializeComponent();
}
private void ProgressForm_Load(object sender, EventArgs e)
{
var transporter = new FileTransporter();
foreach (string fileName in Directory.GetFiles("C:\\Temp\\"))
{
transporter.AddFile(fileName, "C:\\" + Path.GetFileName(fileName));
}
transporter.CurrentFileChanged += transporter_CurrentFileChanged;
transporter.CurrentProgressChanged += transporter_CurrentProgressChanged;
transporter.TotalProgressChanged += transporter_TotalProgressChanged;
transporter.CurrentWriteSpeedUpdated += transporter_CurrentWriteSpeedUpdated;
transporter.TransportComplete += transporter_TransportComplete;
transporter.BeginTransport();
}
void transporter_TransportComplete(FileTransportResult result)
{
Close();
}
void transporter_CurrentWriteSpeedUpdated(long bytesPerSecond)
{
double megaBytesPerSecond = (double)bytesPerSecond/1024000;
currentSpeedLabel.SafeInvoke(x=> x.Text = string.Format("Transfer speed: {0:0.0} MB/s", megaBytesPerSecond));
}
private void transporter_TotalProgressChanged(int percentComplete)
{
totalProgressBar.SafeInvoke(x => x.Value = percentComplete);
}
private void transporter_CurrentProgressChanged(int percentComplete)
{
currentProgressBar.SafeInvoke(x => x.Value = percentComplete);
}
private void transporter_CurrentFileChanged(string fileName)
{
this.SafeInvoke(x => x.Text = string.Format("Current file: {0}", fileName));
}
}
}
EDIT: SafeInvoke code added
public static void SafeInvoke<T>(this T #this, Action<T> action) where T : Control
{
if (#this.InvokeRequired)
{
#this.Invoke(action, new object[] {#this});
}
else
{
if (!#this.IsHandleCreated) return;
if (#this.IsDisposed)
throw new ObjectDisposedException("#this is disposed.");
action(#this);
}
}
Well, if transporter_CurrentProgressChanged gets correct values, the program works properly. You can try to add some minimal Thread.Sleep call to InvokeCurrentProgressChanged (maybe with 0 parameter) when progress value is 100%, to get UI chance to update itself, but in this case you reduce the program performance. It is possibly better to leave the program unchanged, since it works as expected, and main progress bar is updated.

BindingList not updating bound ListBox

I have a ListBox that is bound to a BindingList. The BindingList is built up when a third party application raises an event. I can see the BindingList being bound correctly... but nothing enters the ListBox. I have used the exact same logic with some of my own custom types and it usually works very well.
Form class
private Facade.ControlFacade _controlFacade;
public UavControlForm()
{
InitializeComponent();
_controlFacade = new UavController.Facade.ControlFacade();
UpdateEntityListBox();
}
private void UpdateEntityListBox()
{
lsbEntities.DataSource = _controlFacade.GetEntityTally();
lsbEntities.DisplayMember = "InstanceName";
}
Facade class
private Scenario _scenario;
public ControlFacade()
{
_scenario = new Scenario();
}
public BindingList<AgStkObject> GetEntityTally()
{
BindingList<AgStkObject> entityTally = _scenario.EntityTally;
return entityTally;
}
Scenario class
private static BindingList<IAgStkObject> _entityTally = new BindingList<AgStkObject>();
public Scenario()
{
if (UtilStk.CheckThatStkIsAvailable())
{
UtilStk.StkRoot.OnStkObjectAdded += new IAgStkObjectRootEvents_OnStkObjectAddedEventHandler(TallyScenarioObjects);
UtilStk.StkRoot.OnStkObjectDeleted += new IAgStkObjectRootEvents_OnStkObjectDeletedEventHandler(TallyScenarioObjects);
}
}
private void TallyScenarioObjects(object sender)
{
List<AgStkObject> tallyOfStkObjects = UtilStk.GetRunningTallyOfAllStkObjects();
List<string> stkObjectNames = UtilStk.GetInstanceNamesOfStkObjects(tallyOfStkObjects);
foreach (string stkObjectName in stkObjectNames)
{
if (!SearchFlightUavTallyByName(stkObjectName))
{
if (!SearchLoiterUavTallyByName(stkObjectName))
{
if (!SearchEntityTallyByName(stkObjectName))
{
int i = stkObjectNames.IndexOf(stkObjectName);
_entityTally.Add(tallyOfStkObjects[i]);
}
}
}
}
}
I can see the event fire from the third-party application - this adds an entity to _entityList as desired, but noothing is added to lsbEntities - why?
(jump right to the last example if you want to see it fixed etc)
Threads and "observer" patterns (such as the data-binding on winforms) are rarely good friends. You could try replacing your BindingList<T> usage with the ThreadedBindingList<T> code I used on a previous answer - but this combination of threads and UI is not an intentional use-case of winforms data-binding.
The listbox itself should support binding via list notification events (IBindingList / IBindingListView), as long as they arrive form the right thread. ThreadedBindingList<T> attempts to fix this by thread-switching on your behalf. Note that for this to work you must create the ThreadedBindingList<T> from the UI thread, after it has a sync-context, i.e. after it has started displaying forms.
To illustrate the point that listbox does respect list-change notifications (when dealing with a single thread):
using System;
using System.ComponentModel;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
using (var timer = new Timer())
{
var data = new BindingList<Foo>();
form.Controls.Add(lst);
lst.DataSource = data;
timer.Interval = 1000;
int i = 0;
timer.Tick += delegate
{
data.Add(new Foo(i++));
};
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
timer.Start();
};
Application.Run(form);
}
}
}
and now with added threading / ThreadedBindingList<T> (it doesn't work with the regular BindingList<T>):
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
{
form.Controls.Add(lst);
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
BindingList<Foo> data = new ThreadedBindingList<Foo>();
lst.DataSource = data;
ThreadPool.QueueUserWorkItem(delegate
{
int i = 0;
while (true)
{
data.Add(new Foo(i++));
Thread.Sleep(1000);
}
});
};
Application.Run(form);
}
}
}
public class ThreadedBindingList<T> : BindingList<T>
{
private readonly SynchronizationContext ctx;
public ThreadedBindingList()
{
ctx = SynchronizationContext.Current;
}
protected override void OnAddingNew(AddingNewEventArgs e)
{
SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
ctx.Send(delegate
{
BaseAddingNew(e);
}, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}

Categories

Resources