66

WPF defines its own Main() method. How should I go about replacing it with my own Main method that (normally) opens the WPF MainWindow (e.g. to add a non-WPF scripting mode via command-line arguments)?

user7116
  • 63,008
  • 17
  • 141
  • 172
Qwertie
  • 16,354
  • 20
  • 105
  • 148

6 Answers6

69

Some examples depict changing App.xaml's Build Action from ApplicationDefinition to Page and writing your own Main() that instantiates the App class and calls its Run() method, but this can produce some unwanted consequences in the resolution of application-wide resources in App.xaml.

Instead, I suggest making your own Main() in its own class and setting the Startup Object to that class in the project properties:

public class EntryPoint {
    [STAThread]
    public static void Main(string[] args) {
        if (args != null && args.Length > 0) {
            // ...
        } else {
            var app = new App();
            app.InitializeComponent();
            app.Run();
        }
    }
}

I do this to take advantage of some AppDomain events that must be subscribed to before anything else happens (such as AssemblyResolve). The unwanted consequences of setting App.xaml to Page that I experienced included my UserControl Views (M-V-VM) not resolving resources held in App.xaml during design-time.

CodeAngry
  • 12,760
  • 3
  • 50
  • 57
Joel B Fant
  • 24,406
  • 4
  • 66
  • 67
  • Okay, I'm calling App.Main() instead of Run() because Main() calls InitializeComponent(), which installs the Startup event handler. I'm guessing you have to call Run() if you change Build Action to Page (since Main() disappears) but I just left it as ApplicationDefinition. – Qwertie May 31 '11 at 23:53
  • 4
    The generated `Main()` just instantiates `App` and calls `Run()`. The `Startup` event is fired in `System.Windows.Application`'s constructor. `Run()` attaches a `Dispatcher` and begins the message pump. `InitializeComponent()` should be called in `Apps`'s constructor. Is it not? – Joel B Fant Jun 01 '11 at 02:43
  • Nope, the microsoft-generated code for App does not include a constructor, so App.Main() should be called in order to call both InitializeComponent() and Run(). I'm using VS2008/.NET3.5, if that makes a difference. Note that System.Windows.Application itself cannot call InitializeComponent, because InitializeComponent only exists in the derived class and is non-virtual. It also would not fire the Startup event because it is not possible that anyone has subscribed to that event until after the constructor returns. – Qwertie Nov 10 '11 at 23:35
  • 2
    I add a constructor to `App` and call `InitializeComponent()` there to avoid `App.Main()`. The rest I oversimplified for brevity. `Startup` is fired by `Application.OnStartup()` and only the derived `App` class's constructor can subscribe to `Startup` before it is fired. This works because `Application`'s constructor asynchronously invokes a method that calls `OnStartup()`, so it actually runs after base and derived constructors have finished. – Joel B Fant Nov 11 '11 at 04:03
  • 1
    Rather than having to parse the command line arguments within the defined 'Main' entry point, is there some way to instead pass those arguments to the WPF Application instance defined in the method, so that they can be handled by a defined 'Startup' ( or OnStartup ) override ( via the e.Args property )? – Will Sep 26 '16 at 18:36
  • @JoelBFant "[...] but this can produce some unwanted consequences in the resolution of application-wide resources in App.xaml." Are you referring to the phenomena illustrated in this tutorial? https://ludovic.chabant.com/devblog/2010/04/20/writing-a-custom-main-method-for-wpf-applications/ The author states that initializing the App class before the MainWindow will prevent these issues. Are there other problems that this person has overlooked? Can you give further clarification? – Thick_propheT Mar 12 '18 at 21:05
  • 1
    Instead of moving (or duplicating) `App.Main()`, you could just add this and set it as the entry point for your project and call `App.Main()` directly. – Jason Goemaat Jul 31 '19 at 20:58
  • MAN this solved a problem I was having! Why didn't I think just to create a new object? I was trying to graft on my own main method onto the app, but this approach doesn't even load the app! Brilliant (and so freaking simple too!) – Mark A. Donohoe Nov 26 '20 at 02:41
23

Typically I edit App.xaml to add this support:

<Application x:Class="SomeNamespace.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         Startup="Application_Startup">

The relevant part being I changed from StartupUri to Startup with an event handler in App.xaml.cs. Here is an example:

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
    private void Application_Startup(object sender, StartupEventArgs e)
    {
        int verbose = 0;
        var optionSet = new OptionSet
        {
            { "v|verbose", "verbose output, repeat for more verbosity.",   
                    arg => verbose++ }
        };

        var extra = optionSet.Parse(e.Args);
        var mainWindow = new MainWindow(verbose);
        mainWindow.Show();
    }
}
user7116
  • 63,008
  • 17
  • 141
  • 172
  • Although with this approach, unless you run it from a command window you will not see any `Console.*` output. – user7116 May 27 '11 at 19:04
  • This approach lets me pass constructor arguments to the main window, which is nice. I might even combine it with Joel's approach. – Qwertie May 27 '11 at 19:46
  • Thanks for pointing out that it is "Startup" and not "StartupUri"! – Kevin K Feb 01 '17 at 19:24
20

guys The problem is that your program has two static Main() methods, that will cause the compiler to complain between; To resolve this, try one of the following:

  • Tell the compiler that your static Main() method should be the execution entry point—Set your project’s “Startup object” setting to the class containing your static Main() method (right-click on the project in Solution Explorer, choose “Properties,” then look for the “Startup object” setting under the “Application” tab).
  • Turn off auto-generation of App.g.cs’s static Main() method—In Solution Explorer, right click on App.xaml, choose “Properties,” then change the “Build Action” from “ApplicationDefinition” to “Page”.
Rayan Elmakki
  • 1,144
  • 13
  • 7
5

Create new class with your custom static Main method. At the end of this method just call original App.Main() generated by WPF:

public class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        // Your initialization code
        App.Main();
    }
}

Then set your project’s “Startup object” setting to the class containing your static Main().

Vlad Rudenko
  • 2,363
  • 1
  • 24
  • 24
0

Using a custom Main() you might run into problems because StartupUri is not set.

You can use this to set it without headaches in your App class (Don't forget to remove StartupUri from App.xaml and set its Build Action to Page):

[STAThread]
static void Main()
{
    App app = new App();
    app.InitializeComponent();
    app.Run();
}

protected void OnStartup(object sender, StartupEventArgs e)
{
        var toUri = new UriTypeConverter();
        StartupUri = (Uri)toUri.ConvertFrom("MainWindow.xaml");
...
}
ecreif
  • 1,182
  • 1
  • 12
  • 25
0

I am posting this answer as none of the above answers work for me. In my case, StartupUri was removed from App.xaml and I was still getting the error. what I end up doing I added the following code to the project file (Foo.csproj) and it solved the issue

<ItemGroup>
    <ApplicationDefinition Remove="App.xaml" />
    <Page Include="App.xaml" />
</ItemGroup>
Ehsan Aliverdi
  • 143
  • 1
  • 1
  • 6