namespace MP3_speler.Threading
{
public class thread : MainWindow
{
public thread()
{
StartThreading();
}
public void StartThreading()
{
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Priority = ThreadPriority.BelowNormal;
thread.Start();
}
public void WorkThreadFunction()
{
try
{
UpdateMyDelegatedelegate UpdateMyDelegate = new UpdateMyDelegatedelegate(UpdateMyDelegateLabel);
timelabel.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, UpdateMyDelegate, (Convert.ToInt32(mp3FileReader.Position / 10000)));
Thread.Sleep(500);
}
catch
{
}
}
private void UpdateMyDelegateLabel(int i)
{
double timeseconds = (double)mp3FileReader.Position / (double)176000;
TimeSpan t = TimeSpan.FromSeconds(timeseconds);
string answer = string.Format("{0:D2}:{1:D2}:{2:D2}",
t.Hours,
t.Minutes,
t.Seconds
);
timelabel.Content = answer;
if (waveOut.PlaybackState != PlaybackState.Paused) Slider2.Value = mp3FileReader.Position;
}
}
}
Code Mainprogram:
public partial class MainWindow : Window
{
public IWavePlayer waveOut;
public Mp3FileReader mp3FileReader;
public delegate void UpdateMyDelegatedelegate(int i);
public MainWindow()
{
InitializeComponent();
//Create a thread
thread thread = new thread();
//Setting up notifyicon
notifyCenter notify = new notifyCenter(this);
//giving values to bars
Slider1.Value = 5;
volumelabel.Content = 50;
}
This code generates an infinite loop and goes back to Initializecomponent all the time.
Maybe the problem is that I made the Thread class inherit the MainWindow but would like to know what the problem is.
Yes, the fact that thread derives from MainWindow is causing the infinite loop.
In MainWindow's constructor, it creates a new thread object. However, because thread derives from MainWindow, it then re-invokes MainWindow's constructor, when creates a thread, repeat ad infinitum.
Remember that when deriving, your constructor will always invoke the base class default constructor unless you explicitly instruct to use a different one.
As an aside, there are no use cases I can think of for ever deriving from MainWindow and this certainly isn't one. You will need to rethink your design.
Related
I'm trying to program a small game where there are a few barriers that you have to jump over, go under and so on. Now, for that i can move the barriers and jump at the same time, I opened a new thread. This is in the Form1.cs:
public partial class Form1 : Form
{
Worker workerObject;
Thread workerThread;
public Form1()
{
InitializeComponent();
workerObject = new Worker();
workerObject.Form1 = this;
workerObject.initialize(this);
workerThread = new Thread(workerObject.DoWork);
}
And:
public class Worker
{
private volatile bool _shouldStop = false;
public volatile Form Form1;
public void initialize(Form pForm)
{
Form1 = pForm;
}
public void DoWork()
{
while (!_shouldStop)
{
_shouldStop = true;
picture_barrier1.Left = picture_barrier1.Left - 1;
}
}
public void RequestStop()
{
_shouldStop = true;
}
}
But I get the following error:
Error 1 Cannot access a non-static member of outer type 'Game.Form1'
via nested type 'Game.Form1.Worker'
Then when I go into the Form1.Designer.cs and make the barriers static (By the way, the objects are volatile), I get 11 errors from the code above containing:
Error 1 Member 'Game.Form1.picture_barrier1' cannot be accessed with
an instance reference; qualify it with a type name instead
How do I call those objects correctly from another thread?
I have application with 1 simple thread:
public partial class MainWindow : Window
{
Thread historyThread = null;
Window historyWindow = null;
public MainWindow()
{
//...
}
#region EVENTS Magazyn
private void btn_K_FinalizujZakup_Click(object sender, RoutedEventArgs e)
{
if (dg_Klient.Items.Count > 0)
{
//Problem with 'Finalizuj' Method
new Historia.Wpis(MainWindowViewModel.klient).Finalizuj(viewModel.historiaWindow_ViewModel.historiaZakupow);
MainWindowViewModel.klient = new Klient.Klient();
dg_Klient.ItemsSource = MainWindowViewModel.klient;
}
}
#region History Thread
private void btn_K_HistoriaOpen_Click(object sender, RoutedEventArgs e)
{
//if(historyThread != null){
// historyThread.Abort();
// historyThread = null;
//}
historyThread = new Thread(new ThreadStart(History_ThreadStart));
historyThread.SetApartmentState(ApartmentState.STA);
historyThread.IsBackground = true;
historyThread.Start();
}
private void History_ThreadStart()
{
historyWindow = new HistoryWindow(viewModel.historiaWindow_ViewModel);
historyWindow.Show();
historyWindow.Activate();
System.Windows.Threading.Dispatcher.Run();
}
#endregion // History Thread
//...
}
and 'Wpis' Class look like:
public class Wpis
{
private DateTime date;
public DateTime Date // normal get/ set
private ObservableCollection<Produkt> listaZakupow;
public ObservableCollection<Produkt> ListaZakupow // normal get/set
public Wpis(ObservableCollection<Produkt> listaZakupow)
{
date = DateTime.Now;
this.listaZakupow = listaZakupow;
}
public void Finalizuj(Historia historia)
{
//NOT! - Thread Safe
// EXCEPTION!
// NotSupportedException
// This type of CollectionView does not support changes SourceCollection collection from a thread other than the Dispatcher.
historia.Add(this);
}
private void DoDispatchedAction(Action action)
{
if (currentDispatcher.CheckAccess())
{
action.Invoke();
}
else
{
currentDispatcher.Invoke(DispatcherPriority.DataBind, action);
}
}
}
And when I don't run thread (historyThread) I can normal do method 'Finalizuj' many times
but when I run Thread I can't add anything to list (can't run method - 'Finalizuj')
And VS show me exception about:
NonSupportedException was unhandled
This type of CollectionView does not support changes SourceCollection collection from a thread other than the Dispatcher.
I dont really know what i do wrong.
What i need to add to my project?
In short:
In main Thread - I have object_1 (typeof: ObservableCollection)
In second Thread - I want add anothrer object (typeof: Wpis : ObservableCollection) to object_1
but I get the abovementioned exception
The Exception is telling you exactly what you need to do. You cannot modify the UI from a different thread. I'm assuming that your Observable collection is bound somewhere in your XAML.
You will need to obtain a reference to your MainWindow class so you can access the Dispatcher thread.
public static MainWindow Current { get; private set; }
public MainWindow()
{
Current = this;
//...
}
Then use the Dispatcher thread to modify your collection:
MainWindow.Current.Dispatcher.Invoke((Action)(() =>
{
historia.Add(this);
}));
Working on a C# project which I would like to implement a "waiting" (throbber) indicator in a separate form. After much research and trial and error it appears as the suggested method of doing this is to load a form using a separate thread from the one from the current form/thread.
The reason I went with this method was because initially using the Show() method on the throbber form produced a transparent form. I cannot use ShowDialog because I need to run some code after the throbber is displayed, after which that completes I would like to close the throbber form.
Anyway .. after trying many different methods to load the throbber form in a separate thread I still get an error about trying to access it from a thread which is different from the one it was created in. Here is a skelton version of the project code that should shed some light on my issue:
the example I was working off of for multithreading was this popular link for creating your own spashscreen in a separate thread ... http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C
public class Main
{
public void CheckData()
{
try
{
ProgressBar pb = new ProgressBar();
pb.ShowProgressBar();
//do data checking here
pb.CloseForm()
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static Thread ms_oThread = null;
public bool shouldStop = false;
static ProgressBar ms_ProgBar = null;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
public void ShowForm()
{
ms_ProgBar = new ProgressBar();
Application.Run(ms_ProgBar);
}
public void CloseForm()
{
ms_ProgBar.Close();
}
public void ShowProgressBar()
{
// Make sure it is only launched once.
if (ms_ProgBar != null)
return;
ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
{
System.Threading.Thread.Sleep(1000);
}
}
}
You are creating your ProgressBar twice. Once in your main function, and once in your new thread. You are also calling your CloseWindow method from your main function (and on the window that is never shown), rather than on your new thread window.
You only want to create ProgressBar and show it using your new thread. Make your static ProgressBar field public so you can call close on it directly from Main, but make sure to use Invoke to do it since it's not on that Window's GUI thread.
Also, ShowProgressBar should be static.
Here's a rewrite attempt:
public class Main
{
public void CheckData()
{
try
{
ProgressBar.ShowProgressBar();
//do data checking here
ProgressBar.CloseForm();
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static ProgressBar _progressBarInstance;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
static void ShowForm()
{
_progressBarInstance = new ProgressBar();
Application.Run(ms_ProgressBar);
}
public static void CloseForm()
{
_progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
_progressBarInstance= null;
}
public static void ShowProgressBar()
{
// Make sure it is only launched once.
if (_progressBarInstance != null)
return;
var ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
}
}
Given the class below, to launch a splash screen on an alternate thread:
public partial class SplashForm : Form
{
private static Thread _splashThread;
private static SplashForm _splashForm;
public SplashForm()
{
InitializeComponent();
}
// Show the Splash Screen (Loading...)
public static void ShowSplash()
{
if (_splashThread == null)
{
// Show the form in a new thread.
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// Called by the thread.
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new SplashForm();
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(_splashForm);
}
// Close the splash (Loading...) screen.
public static void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
This is called and closed with the following respective commands
SplashForm.ShowSplash();
SplashForm.CloseSplash();
Fine.
I am not exactly new to the TPL, of course we can show the form on another thread using something as simple as:
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
My issue is closing this SomeForm down when you are ready. There must be a better way than creating a public static method in the SomeForm class like
private static SomeForm _someForm;
public static void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
My question is, what is the best way to do the same thing as shown using the SplashForm class above using the Task Parrallel Library (TPL)? Specifically, the best way to close the form invoked on another thread from the UI.
Your question does not seem to be so much about a difference between Thread and Task because what you want is to get rid of the "dirty" static state. I suggest you encapsulate it into a class:
class SplashController
{
public void Run() {
_someForm = new SomeForm();
someForm.ShowDialog();
}
private SomeForm _someForm;
public void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
}
You can call Run using whatever threading mechanism you like. CloseSomeForm does not use threading so it is independent of this problem.
You can now store a reference to an instance of SplashController wherever you like. In local variables or indeed in a static variable. The latter makes sense because there is exactly one splash screen.
Because the static state is now well encapsulated I don't see any problem with it being statically held.
You probably shouldn't do something like this
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
because it would require a message loop to be present on the exact thread that creates the Form, which is a ThreadPool thread. But I haven't tested this.
You could try this:
public static Task<SplashForm> ShowSplash()
{
var tcs = new TaskCompletionSource<SplashForm>();
// Show the form in a new thread.
_splashThread = new Thread(() =>
{
var splashForm = new SplashForm();
tcs.SetResult(_splashForm);
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(splashForm);
});
_splashThread.IsBackground = true;
_splashThread.Start();
}
this would allow you to remove the static modifier from CloseSplash:
// Close the splash (Loading...) screen.
public void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
May be used like this:
var form = await SplashForm.ShowSplash();
form.CloseSplash();
I have a FTP proccess that run without UI. and have a winform that use this ftp control. in that window I have a progressbar that show the ftp upload progress. The progress arrives to the window via interfase that is updated on the underliying presenter (I'm using MVP pattern).
My problem is when try to update the progress, it allways throw me this exception.
Through threads illegal operation: control 'prgProgresoSubido' is accessed from a thread other than that in which you created it.
That problem persists even if I use a BackGroundWorker in the Form.
// This is a delegated on presenter when a File finish to upload
void client_FileUploadCompletedHandler(object sender, FileUploadCompletedEventArgs e)
{
string log = string.Format("{0} Upload from {1} to {2} is completed. Length: {3}. ",
DateTime.Now, e.LocalFile.FullName, e.ServerPath, e.LocalFile.Length);
archivosSubidos += 1;
_Publicacion.ProgresoSubida = (int)((archivosSubidos / archivosXSubir) * 100);
//this.lstLog.Items.Add(log);
//this.lstLog.SelectedIndex = this.lstLog.Items.Count - 1;
}
// This is My interfase
public interface IPublicacion
{
...
int ProgresoSubida { set; }
}
/// And Here is the implementartion of the interfase on the form
public partial class PublicarForm : Form ,IPublicacion
{
//Credenciales para conectarse al servicio FTP
public FTPClientManager client = null;
public XmlDocument conf = new XmlDocument();
public string workingDir = null;
public webTalk wt = new webTalk();
private readonly PublicacionesWebBL _Publicador;
public PublicarForm()
{
InitializeComponent();
String[] laPath = { System.AppDomain.CurrentDomain.BaseDirectory};
String lcPath = System.IO.Path.Combine(laPath);
_Publicador = new PublicacionesWebBL(this, lcPath);
}
public int ProgresoSubida
{
set
{
// This is my prograss bar, here it throw the exception.
prgProgresoSubido.Value = value;
}
}
}
How can I do to avoid this problem ?
In general, all updates to the User Interface and Controls has to be done from the main thread (event dispatcher). If you attempt to modify the properties of a control from a different thread you will get an exception.
You must call Control.Invoke to invoke on the event dispatcher the method that updates your UI
Control.Invoke
Here, place a button and a label on a form, then try this
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(TestThread));
t.Start();
}
private void TestThread()
{
for (int i = 0; i < 10; i++)
{
UpdateCounter(i);
Thread.Sleep(1000);
}
}
private void UpdateCounter(int i)
{
if (label1.InvokeRequired)
{
label1.Invoke(new ThreadStart(delegate { UpdateCounter(i); }));
}
else
{
label1.Text = i.ToString();
}
}
}
Realize, that if you are firing an event from a thread, that the event will be on the same Thread. Therefore, if that thread is not the event dispatcher, you'll need to invoke.
Also, there may be mechanisms that BackgroundWorker gives you (As the commentator said) that simplify this for you, but I've never used it before so I'll leave that up to you to investigate.
As Alan has just pointed out, you must do all operations with UI controls in UI thread.
Just modify your property like this:
public int ProgresoSubida
{
set
{
MethodInvoker invoker = delegate
{
prgProgresoSubido.Value = value;
}
if (this.InvokeRequired)
{
Invoke(invoker);
}
else
{
invoker();
}
}
}