0

I'm working on a project that is designed to play both audio and video files through a WPF Window through a MediaElement. This is the xaml for the window:

<Window x:Class="HomeSystem_CSharp.MediaWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MediaWindow" MinHeight="480" MinWidth="720" WindowStyle="None" ResizeMode="NoResize" Visibility="Visible" Cursor="None">
    <Grid Background="Black">
        <MediaElement LoadedBehavior="Manual" HorizontalAlignment="Stretch" Name="video" VerticalAlignment="Stretch" Cursor="None" MinHeight="480" MinWidth="720"/>
    </Grid>
</Window>

This creates the window with no borders, that I plan on full-screening in the future. For now though, I want more room on my desktop. Here is my code for controlling my MediaElement:

private bool playing = false;

        public MediaWindow(string dir)
        {
            InitializeComponent();

            video.Source = new Uri(dir);
            play();
        }

        public void play()
        {
            if (playing)
                return;

            if (!this.IsVisible)
                this.Show();

            video.Play();

            playing = true;
        }

This MediaWindow is created outside of the object, just by a simple MediaWindow mw = new MediaWindow("C:\\test.mp4");

No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays. I can hear the video in the background, but there is a broken window on my screen. Just a black box.

The biggest issue is that just the other day it was working fine, and suddenly it broke, and I have no clue what happened. I'm kinda new to c#, so I dont know a TON about what's going on, but I've worked with java for several years so I'm not totally new. Can anyone point out what I'm doing wrong? i can provide any other details but I think i got everything necessary to answer. Thank you for any help, this has been bothering me all day with no fix!

EDIT: Turns out, if I use

public void play()
        {
            if (playing)
                return;

            //if (!this.IsVisible)
            //    this.Show();

            video.Play();
            new Application().Run(this);
            playing = true;
        }

instead, it will run the GUI. However, that hangs up the console. Originally I fixed that hang up by using this.Show(), but now that's not working. I know that moving the whole project into a WPF project would fix this, however I'm really trying not to for other reasons. Only win32 for now. Any ideas why this is happening and how to fix it? I do have [STAThread] over my main function if that makes a difference.

EDIT 2: This video file I'm playing is movie length, and runs perfectly in any other software to prevent that from being an issue with development. As for the MediaWindow creation. What I did is made a win32 console project and set up the user commands there. I then made a new WPF project, and created an xaml gui window. I took those code files, and copied them into the win32 project, and call it to launch in the main method with the MediaWindow mw = new MediaWindow("C:\\test.mp4"); I did it this way because for now I'm trying to keep away from using a pure WPF application, and because I'm kinda new to C# so I wasnt sure how to create the window I wanted without my copy paste method.

6
  • Does the video otherwise play smoothly on your system, say for example in Windows Media Player? Also, I think you need to add more detail about what this is all wrapped in. What code is creating your MediaWindow? Commented Jun 2, 2015 at 23:31
  • @MikeFulton See my second edit. Does that help?
    – PulsePanda
    Commented Jun 2, 2015 at 23:58
  • I'm not sure the concept of a "pure WPF application" is quite as applicable as you think. When you create a new project in Visual Studio using the WPF template, it creates a default window and an Application class that opens it, and adds all the appropriate references to the WPF assemblies. It's automated for convenience. It sounds like what you've done is mainly bypass the automation, and possibly the use of the Application class. Are you creating an Application class or are you opening your MediaWindow directly from the program's main() function? Commented Jun 3, 2015 at 0:56
  • I'm opening my MediaWindow directly from main(). As for "pure WPF" I mean instead of using a GUI for the whole project, I use a console instead, and use a GUI to compliment it.
    – PulsePanda
    Commented Jun 3, 2015 at 1:33
  • I don't know what you mean by "I use a console instead". Do you mean a console-mode app? As in, at some point the user enters a command that prompts you to open a window to play the video, then you return back to the console? Commented Jun 3, 2015 at 2:54

2 Answers 2

1
+50

No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays.

I've managed to reproduce this. One important thing missing in your description is the exact way you create and show the window in your main() method. For example, the following freezes the video leaving the sound playing:

[STAThread]
static void Main(string[] args)
{
    var w = new MediaWindow();
    w.Show();
    Console.ReadLine();
}

The next one "freezes" the console until you close the window:

[STAThread]
static void Main(string[] args)
{
    var w = new MediaWindow();
    w.ShowDialog();
    Console.ReadLine();
}

And this gives you both working:

static void Main(string[] args)
{
    var thread = new Thread(ShowMediaWindow);
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();

    while (true) // Just to test console working
    {
        Console.Write("\r" + DateTime.Now);
        Thread.Sleep(100);
    }
}

static void ShowMediaWindow()
{
    new MediaWindow().ShowDialog();
}

As you can see, the console and the WPF window simply can't work properly in a single thread.

The window class is as simple as this, by the way (the XAML is mostly the same as yours):

public partial class MediaWindow : Window
{
    public MediaWindow()
    {
        InitializeComponent();
        video.Source = new Uri(@"C:\video\test.asf");
        Play();
    }

    public void Play()
    {
        video.Play();
    }
}

I guess that'll do for showing a video player window from console.

3
  • Here's a link to VS2013 solution: drive.google.com/file/d/0B0EEu26dji2mSUlpS0pHQlVRcWM/…
    – Yegor
    Commented Jun 3, 2015 at 5:46
  • I'm at work right now so I cant try this, however with this method there would be no way to call other functions within the MediaWindow class, correct? That being the case, there would need to be a pause and stop method, would that be possible to do by manipulating the thread? Like, would pausing/suspending/closing the thread have that desired effect on the window that's displayed?
    – PulsePanda
    Commented Jun 3, 2015 at 16:14
  • Actually, it's very straightforward: pastebin.com/BuEBDXDp You don't need to abort the thread - close the window (with your mouse or with the Close method) and it will stop.
    – Yegor
    Commented Jun 3, 2015 at 17:08
1

OK, the whole hybrid console/GUI thing is a new one on me but I'm just going to assume there's a real need to do things that way.

The Application.Run method doesn't return until the application closes. That's why your console is locked up.

Don't create the Application object inside the window. Do it externally. Also, spawn another thread to kick off video playback. This will leave your console responsive.

I'm not gonna get heavy-duty into describing threading and delegates and so forth... you can look that up if you want. I'm just gonna go over what you need to do for this specific example. Somewhere in the class that launches the video, but not in a method, define a delegate type like this:

delegate void LaunchVideo(String s);

A delegate is essentially kind of a pointer to a function with a certain definition of return value and parameters. Here we've defined the delegate type as a function that takes a String parameter and returns nothing. Next, at the point in your code where you want to play the video, do this:

LaunchVideo lv = new delegate(String vidfile)
{
    Application app = new Application();
    app.Run(new MediaWindow(vidfile));
};

IAsyncResult result = lv.BeginInvoke( "C:\\vid.mp4", myVideoCompleted, null );

This creates the delegate variable and points it at an anonymous function that creates the app and launch video playback. Then it calls the delegate's BeginInvoke method, which is part of the basic delegate class. This spawns a new thread running in the function pointed to by the delegate.

Note that calling Application.Run with a window parameter like this will open the window but it won't call the play() method. You may want to move that code to the constructor, or add a call to it in the constructor.

Be aware that your main thread cannot safely call methods in objects created in the invoked thread unless you use the lock function to make things thread safe.

If you need "open" and "play" to be separately controlled events which are both invoked by the console then you'll have to figure out a means to pass messages from the console thread to the window thread.

The parameter list for BeginInvoke always starts off with whatever parameters are expected by the function you're invoking. So in this case, that's the string with the video filename. Next is the name of a callback function which will be called when the invoked function exits. It's a void function that takes an AsyncResult parameter, like this:

void myVideoCompleted(AsyncResult result)
{
    // Do whatever here...  Be aware this is still on the other thread
}

You can use 'null' instead of a function name, if you don't need anything called at the end. Be aware that if you do use a callback function, it runs on the new thread started by BeginInvoke, not the thread that called it.

The last parameter to BeginInvoke is an object that will be passed through to the callback function via the AsyncState member of the AsyncResult parameter. You can pass 'null' if you're not using a callback or if you have no parameters which will be needed by the callback.

You can also call the EndInvoke method of the delegate to get back any results that may've been returned by the function. However, be aware that this will block if the invoked function isn't finished yet. In this case you have no results to worry about.

2
  • This is a fantastically detailed answer, thank you :) As soon as I get home from work I'll start working with this. I do have another question, however. With this it seems like it'd be a huge pain to manipulate the GUI window as soon as it's launched, so would just having it all be a GUI as opposed to a hybrid console/GUI be just better in general because of the threading issues?
    – PulsePanda
    Commented Jun 3, 2015 at 16:16
  • It's not really that hard to manipulate the GUI window. Just use SendMessage from the console side, and add a message event handler on the video window. Having said that, yes I think a pure GUI app would be a better idea, but not so much just because of threading issues. Commented Jun 3, 2015 at 18:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.