Before I start, I'd like to appologize for any... idiocy... that I may show when asking this question, I'm trying to learn C# after coming from Java, and just wanted to jump right into it!
I'm working on a project that is designed to take commands through the console, such as "play batman begins" and it'll start playing the movie Batman Begins. It also takes commands for music, etc. With this setup, my goal is to have it voice controlled, so I'm not really concerned with using a GUI instead of the console. When I have the program play a movie, it loads up a GUI window with no border, no content, and the constructor that it uses is:
public MainWindow(MediaPlayer player)
{
InitializeComponent();
VideoDrawing drawing = new VideoDrawing { Rect = new Rect(0, 0, 800, 600), Player = player };
DrawingBrush brush = new DrawingBrush(drawing);
Background = brush;
}
The MediaPlayer is controlled from the main function (for now). My code to load the video and run it is:
MediaPlayer mp = new MediaPlayer();
mp.Open(new Uri("C:\\test.mp4", UriKind.RelativeOrAbsolute));
VideoDrawing vidDrawing = new VideoDrawing();
vidDrawing.Rect = new Rect(0, 0, 100, 100);
vidDrawing.Player = mp;
DrawingBrush DBrush = new DrawingBrush(vidDrawing);
videoPanel = new MainWindow(mp); // GUI panel to play the video on, constructor is listed above
play();
new System.Windows.Application().Run(videoPanel);
As I'm new to C# and all, the way I got the GUI to work with this is by starting a new WPF project, creating the GUI and writing the code, then copy paste the xaml and cs files into my project (cause I'm a noob, I'm sorry). MAYBE this is causing the problem.
This code works (mostly) fine. When I run my program, it loads the video file, opens the Window, and plays the video on the screen while playing the sound too. However, when it starts the video, the GUI "hangs" and doesnt accept any input's OR outputs, but it's not frozen as the cursor is still flashing. I'm not sure why this is.
I've tried threading almost anything I can think of, but I get errors with that. I've tried localizing the loading to the GUI window itself, however it still hangs the console. I've been searching online for all sorts of things for the past few days and cant find a solution, so I turned here!
My program has a lot more classes and stuff going on, so I had to throw all this stuff together, and double check, so if I'm missing something or made a stupid please let me know and I can fix it. As I'm still kinda new to C# (but not new to programming) I may have missed something in my question details as well.
I'm wondering if the solution is to make it ALL GUI based, instead of hybrid console and GUI? Because of the information I've found online it looks like there can be some annoying... discrepancies... between the two types of threads?
Anyway, any help would be great, I've spent the past 3 days trying to debug this and haven't made any progress. If it helps, there's a link to the repo here! Thanks :)
EDIT: I made some changes while waiting for a response, primarily changing the way it draws to the GUI. Originally I had it painting to the background, but now have made it handle through a MediaElement. This is the whole class for my window now:
public partial class MainWindow : Window
{
public MainWindow(string dir)
{
InitializeComponent();
video.Source = new Uri(dir); //video is the name of my MediaElement
}
public void pause(){
video.Pause();
}
public void resume()
{
video.Play();
}
public void stop()
{
video.Stop();
this.Close();
}
public void volume(int i)
{
video.Volume = i;
}
}
This did NOT fix the console Hang, however this made everything much more centralized so as to make debugging easier. Hope this may help!
Consider converting this to a WPF-application, it will make things easier.
Anyway, you can still work with GUI without locking console, but you must execute GUI-related stuff on a separate dedicated STA thread. Since you are using WPF, this helper should be useful (this code is not production-quality, but works):
static class UIThread
{
private static Dispatcher _dispatcher;
public static void Start()
{
using(var wh = new ManualResetEvent(false))
{
var thread = new Thread(ThreadProc)
{
IsBackground = true,
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start(wh);
wh.WaitOne();
}
}
private static void ThreadProc(object arg)
{
var wh = (EventWaitHandle)arg;
_dispatcher = Dispatcher.CurrentDispatcher;
wh.Set();
Dispatcher.Run();
}
public static Dispatcher Dispatcher
{
get { return _dispatcher; }
}
}
This helper provides you with a reference to a Dispatcher object, which is used to execute methods on UI thread.
All communication with UI thread should be done through Dispatcher.Invoke() or Dispatcher.BeginInvoke() methods. Note that since we now have several threads, MediaPlayer objects must be created on the UI thread. You can still hold it's reference on any thread you want, but any method calls to MediaPlayer object must go through Dispatcher.
Things to change:
add a UIThread.Start(); call before any attempts to use Dispatcher. Beginning of Main() method is a good place to do this.
MediaPlayer objects should be created like this: mediaPlayer = UIThread.Dispatcher.Invoke(() => new Media());
any UI-related code should go to Dispatcher.Invoke() or Dispatcher.BeginInvoke(): UIThread.Dispatcher.BeginInvoke(() => playVideo(#"C:\video.avi"));
Update
Also change new System.Windows.Application().Run(videoPanel); to simple videoPanel.Show(). Application.Run() blocks until the window is closed.
Related
I have a C# WPF application that can open a few synchronised videos in separate windows using MediaElements in Windows.
I was having some performance issues (frame rate) when I had a few videos running all on the same UI thread so I had a go at putting each window in its own thread with its own dispatcher
public void ShowVideoWindow(string source)
{
if (!_videoWindows.ContainsKey(source))
{
Thread videoThread = new Thread(delegate ()
{
VideoWindow videoWindow = new VideoWindow(source);
videoWindow.Closed += VideoWindow_Closed;
videoWindow.Show();
_videoWindows[source] = videoWindow;
System.Windows.Threading.Dispatcher.Run();
});
videoThread.IsBackground = true;
videoThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception
videoThread.Name = source + " - video";
videoThread.Start();
}
}
This works really well. Each video window runs nicely and I just have to be careful that I only access the other windows through invoking calls to the correct dispatcher.
The issue is that I don't want these windows to be completely separate. I would like to use dockable windows as part of AvalonDock. Is there a way for me to get just the video control to run in a separate thread? Should I be using something more advanced than MediaElement? I am only using it because it was easy to get going with.
First of all - I'm very low skilled programmer. I am building the foundation of a simple music app for my bachelor degree project. My question is regarding a internal clock method that is meant to increase an int value by 1 BPM times a minute.
I've created an internalClock class:
public class internalClock
{
// THIS METHOD WILL BE CALLED WHEN THE THREAD IS STARTED
public static void clockMethod()
{
int BPM = 135;
int clockTick = 1;
Form1 clockForm = new Form1();
// infinite loop
while (true)
{
if (clockTick == 8)
{
clockTick = 1;
}
else
{
clockTick++;
}
clockForm.metrobox.Text = clockTick.ToString();
Thread.Sleep(60 * 1000 / BPM);
}
}
}
This is how I managed to get an access to the RichTextBox itself:
public RichTextBox metrobox
{
get { return metroBox; }
set { metroBox = value; }
}
In the main 'Program.cs' I've written what's meant to start a separate thread to run the clockMethod until the program is closed:
// THREADING
// Create a thread
internalClock oClock = new internalClock();
Thread oClockThread = new Thread(new ThreadStart(internalClock.clockMethod));
// Start the internalClock thread
oClockThread.Start();
It's not updating the text in the RichTextBox. Also, if I call the clockMethod() without creating a separate thread for it - the application freezes. Sorry for my amateur question, I'm just getting started with C# (yeah.. my uni is useless). What am I doing wrong in my code?
So the above code has several problems, however I would encourage you to check out the Timer control that you can add to the form you want to do processing at a certain interval or in certain "ticks". MSDN Form Timer
With the timer you can remove that class you have and invoking a new thread, etc etc. I would read up on the Timer class in the given link and think about how you can re-work your application structure to fit that. The concepts for why that thread isn't working, etc, is frankly not that important for where you're at. I think you just need to focus for now on a tool that already does what you want it to do, which I believe is the Timer.
As a general note, you usually don't need to create a raw thread in .NET. As of .NET 4.0 you can access types called Tasks to perform multi-threaded logic and processing. If you find the need to do that later on, check that out. Task Type MSDN
I am creating a game in WPF. To manage sounds I've created a AudioController, which will create MediaPlayers and manage playing, stopping, looping, pausing, etc. I have the base of it working. If i'm in a control, I can call
AudioController.Play("MySound");
And it will decide if it has a MediaPlayer for that sound already, if not it will create one.
A problem appears in the following situations:
Task.Run(() => { DoSomething(); AudioController.Play("MySound"); });
_timer_tick(object sender...) { AudioController.Play("MySound"); }
When using a Task my Sound will play properly, but the MediaEnded event never gets fired, causing me not to be able to manage the MediaPlayers properly.
With the Timer Tick, the Sound will play the first time, but never again, even though its using a new MediaPlayer and also never fires the MediaEnded event
I've tried Dispatching the calls, but it doesn't seem to make a difference.
I can only imagine the problems lies with the idea that the Play() call is being sent from a different thread.
public static void Play(string sound)
{
Debug.WriteLine("Publish called on Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
if (!Application.Current.CheckAccess())
{
Application.Current.Dispatcher.Invoke(() => Play(sound, TimeSpan.Zero), DispatcherPriority.Normal);
return;
}
Play(sound, TimeSpan.Zero);
}
private static void Play(string sound, TimeSpan position)
{
//...some preemptive code that's just searching a list
//reuse
if (ap == null)
{
ap = new AudioPlayer(entry);
_players.Add(ap);
}
ap.Play(position);
}
Note: AudioPlayer is a wrapper class around MediaPlayer so that I can add some additional properties such as LastActive, IsLooping, and handles replaying a sound by calling:
_player.Stop();
_player.Position = position;
_player.Play();
Update:
I just decided to run a different sound in the above scenario's and everything seems to work fine. The only difference between the audio's is that one is 40kb and the other 67kb. Could it be that the file is just so small MediaPlayer doesn't register the NaturalDuration and therefore assumes its 0 which stops the events from being triggered?
I'm currently trying to use a WPF component that makes use of Application.Current from a WPF application, however due to several reasons I never call Application.Run (nor is that an option). The result is a NullReferenceException.
I'm basically trying to display multiple instances of the same WPF window from what would be a console application.
Any advice (and code samples in C#/F#) would be welcome!
Thanks in advance
Just to offer an alternative solution.
It is possible to keep an application running without any windows open. To me this feels less 'hackish'. :) http://msdn.microsoft.com/en-us/library/system.windows.application.shutdownmode.aspx
public class AppCode : Application
{
// Entry point method
[STAThread]
public static void Main()
{
AppCode app = new AppCode();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run();
...
app.Shutdown();
}
}
EDIT:
Ok this got a bit cumbersome. Application.Run will block, so it needs to run in its own thread.
When it does run in its own thread, any interaction between your main thread and your ui thread had best be done by Application.Current.Dispatcher.Invoke. Here is some working code, that assumes you have a class that inherits from Application. I'm using a modified App.xaml/App.xaml.cs that a WPF project template creates for you, to get nice handling of ResourceDictionaries for free.
public class Program
{
// Entry point method
[STAThread]
public static void Main()
{
var thread = new System.Threading.Thread(CreateApp);
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();
// This is kinda shoddy, but the thread needs some time
// before we can invoke anything on the dispatcher
System.Threading.Thread.Sleep(100);
// In order to get input from the user, display a
// dialog and return the result on the dispatcher
var result = (int)Application.Current.Dispatcher.Invoke(new Func<int>(() =>
{
var win = new MainWindow();
win.ShowDialog();
return 10;
}), null);
// Show something to the user without waiting for a result
Application.Current.Dispatcher.Invoke(new Action(() =>
{
var win = new MainWindow();
win.ShowDialog();
}), null);
System.Console.WriteLine("result" + result);
System.Console.ReadLine();
// This doesn't really seem necessary
Application.Current.Dispatcher.InvokeShutdown();
}
private static void CreateApp()
{
App app = new App();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run();
}
}
The following is the intended behavior of Application class:
The first open window is the MainWindow.
The only window in the list becomes the MainWindow (if others are to
be removed).
Application Class is designed to exit if no windows are present in
windows list.
Check this link.
So basically you cannot run an Application, without any window open. Keep a window open but hidden.
If I have misunderstood your problem, then perhaps the following similar cases might help:
Managing Application Resources when WPF is Hosted.
When running unit tests in Visual Studio 2008
In my project, whenever a long process in being executed, a small form is displayed with a small animated gif file. I used this.Show() to open the form and this.Close() to close the form.
Following is the code that I use.
public partial class PlzWaitMessage : Form
{
public PlzWaitMessage()
{
InitializeComponent();
}
public void ShowSpalshSceen()
{
this.Show();
Application.DoEvents();
}
public void CloseSpalshScreen()
{
this.Close();
}
}
When the form opens, the image file do not immediately start animating. And when it does animate, the process is usually complete or very near completion which renders the animation useless. Is there a way I can have the gif animate as soon as I load the form?
Why not using threads? It's always good idea to learn something new.
You could simply put your "long process" in background thread, and use events to report to presentation layer, for example:
// in your "long process" class
public event Action<double> ReportCompletition;
// this method will start long process in separate background thread
public void Start()
{
Thread thread = new Thread(this.LongProcess);
thread.IsBackground = true;
thread.Start();
}
private void LongProcess()
{
// do something
// report 10% completition by raising event
this.ReportCompletition(0.1);
// do something more
this.ReportCompletition(0.5);
// ... and so on
}
This way, all you have to do is implement simple method in your Form/UI, which will consume this information.
public partial class MainApplicationWindow : Form
{
private LongProcessClass _longProcess;
public MainApplicationWindow
{
this.InitializeComponent();
this._longProcess = new LongProcessClass();
// bind UI updating method to long process class event
this._longProcess.ReportCompletition += this.DisplayCompletitionInfo;
}
private void DisplayCompletitionInfo(double completition)
{
// check if control you want to display info in needs to be invoked
// - request is coming from different thread
if (control.InvokeRequired)
{
Action<double> updateMethod = this.DisplayCompletitionInfo;
control.Invoke(updateMethod, new object[] { completition });
}
// here you put code to do actual UI updating,
// eg. displaying status message
else
{
int progress = (int) completition * 10;
control.Text = "Please wait. Long process progress: "
+ progress.ToString() + "%";
}
}
Of course, you can report anything you like from within long process. Be it completition rate, ready to display string messages, anything. You can also use events to report that long process has finished, broke, or any long process data you wish.
For more detailed information on this topic you might want to check MSDN tutorials on Threading and Events.
You should do the "long process" in a separate thread. I advice using the BackgroundWorker.
This will make your code more difficult though. One of the main reasons is, that you cannot communicate with the UI from the background thread.
Also note the warning from the linked page:
Caution
When using multithreading of
any sort, you potentially expose
yourself to very serious and complex
bugs. Consult the Managed Threading
Best Practices before implementing any
solution that uses multithreading.
If this is too difficult, you could call Application.DoEvents very frequent from your "long process" code.
Whatever you choose, this will make it possible for the user to interact with your form. For instance closing it. You should be aware of this.
Use the gif in a PictureBox, and have it open using Form pWait = new Form(); pWait.Show();