Calling a WPF Window with resourses from the Tray - c#

I have a Windows Tray project that opens a WPF window when Settings is clicked. The WPF window opens and displays some of the content properly, but I have two lists that are bound to another class that have odd behavior.
These lists are displayed on two different tabs as devices. On one tab, there is a graphical representation from which the device can be started, and the other tab shows the settings for the device. Everything works perfectly when the WPF application is set as the startup project. However, when I start it from the tray, the lists load correctly, and display in the first tab, where they can be started, but the second tab shows no devices present. They are both linked to the same data.
At first, I thought that there was an issue with binding, but after several days of trying to resolve this, I believe the problem is with App.xaml, where there is a reference to a resource. I suspect that since i am not referencing App.xaml, the resource is not loaded, and the list is not being set up properly. The only difference between the project working and not working is that one has the WPF as startup project, and the other uses the tray to call the WPF.
My question, then, is how do I reference App.xaml to ensure that I load the required resource.
Below is some of my code, in case it might help.
App.xaml
<Application x:Class="Sender_Receiver.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Shell.xaml">
<Application.Resources>
<ResourceDictionary Source="Themes\Generic.xaml"/>
</Application.Resources>
Current call to open the WPF
private void settingsEvent_Click(object sender, EventArgs e)
{
gui = new Sender_Receiver.mainWindow(); // mainWindow() located in Shell.xaml
gui.Show();
}
Code to display the devices. A collapsibleSection implements Expander and RepeatControl implements ItemsControl.
<c:CollapsibleSection Header="Senders">
<c:CollapsibleSection.CollapsedContent>
<c:RepeatControl Margin="30,0,0,0" ItemsSource="{Binding SendersList}"
ItemType="{x:Type m:Sender}" List="{Binding SendersList}"
ItemTemplate="{StaticResource SenderSummary}"/>
</c:CollapsibleSection.CollapsedContent>
<Border BorderThickness="1" BorderBrush="Chocolate" Margin="30,0,0,0">
<c:RepeatControl ItemsSource="{Binding SendersList}"
ItemType="{x:Type m:Sender}"
List="{Binding SendersList}"
ItemTemplate="{StaticResource SenderTemplate}"/>
</Border>
</c:CollapsibleSection>
The image below shows how the application is behaving under different conditions.
Any assistance would be greatly appreciated.

I finally figured this one out. Instead of instantiating the UI, the entire WPF application must be called to run. This will cause the App.xaml to load the dictionary, and other WPF forms can then access it. This is done with the following code:
private void settingsEvent_Click(object sender, EventArgs e)
{
if (gui == null)
{
gui = new App();
gui.MainWindow = new mainWindow();
gui.InitializeComponent();
}
else
{
gui.InitializeComponent();
gui.MainWindow.Show();
gui.MainWindow = new mainWindow();
}
}
private static App app = new App();
You must keep adding the mainWindow back to the App, as it seems to be set to null when the window shows.
This was discovered through experimentation, so i am sure it is not the best practice, but it works, and right now, that was what I needed.
EDIT
For my purposes, however, this still was not working. I could either open the Settings window only once, or I could not get an event handler to work on it the first time it was open. Finally, Josh came up with the correct answer:
Process myProcess = new Process();
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.FileName = "C:\\mysettingsapp\\mysettingsapp.exe"; // replace with path to your settings app
myProcess.StartInfo.CreateNoWindow = false;
myProcess.Start();
// the process is started, now wait for it to finish
myProcess.WaitForExit(); // use WaitForExit(int) to establish a timeout
His full explanation can be found here.

Related

Prism Navigation Not Working In New Window

Edit: Please see my comment below - the code itself seems to be correct (at least the Prism region code) but navigation in the new window instance is still not working.
To start off with, here is the issue I'm having...
I have a main window with a menu bar that switches views in the main window to take the user to different screens. This is all working great.
So I thought today that I would add a 'first time user' screen to handle all the initial setup for the application. This is a new window that will pop up if certain first time properties haven't been set and welcome the new user, get the initial setup complete, etc. I wanted to have navigation on this new window in a new region (just next and back buttons that take the user through the setup).
I thought this would be easy but after 3 hours of floundering and searching the darkest corners of the web I am still very confused - I also looked at Brian Lagunas' Pluralsight videos on MVVM but nothing I have tried to apply is working to setup navigation on the new window.
The content region for the main window is named "ContentRegion" and the content region for the new window is named "SetupRegion".
All views are registered in the bootstrapper like so:
// All views must be registered.
Container.RegisterTypeForNavigation<Home>( "Home" );
Container.RegisterTypeForNavigation<Index>( "Index" );
Container.RegisterTypeForNavigation<Settings>( "Settings" );
Container.RegisterTypeForNavigation<FirstTimeSetupWelcomeScreen>( "WelcomeScreen" );
Container.RegisterTypeForNavigation<FirstTimeSetupScreen2>( "FirstTimeSetupScreen2" );
Here is how the new window is being instantiated currently, from the main window:
public MainWindowViewModel(IRegionManager _regionManager, EventAggregator _eventAggregator)
{
eventAggregator = _eventAggregator;
regionManager = _regionManager;
NavigateCommand = new DelegateCommand<string>(Navigate);
// Set the default view to the home screen
regionManager.RegisterViewWithRegion("ContentRegion", typeof(FirstTimeSetupWelcomeScreen));
// Check to see if program is in first time run or not
if ((ConfigurationManager.GetSection("SaveLocationsGroup/Locations") as IndexLocationsSection).SaveLocation.Location == "")
{
var firstTimeWindow = new FirstTime();
firstTimeWindow.Show();
// Set the default view to the welcome screen on new window
regionManager.RegisterViewWithRegion("SetupRegion", typeof(FirstTimeSetupWelcomeScreen));
}
}
In the XAML for the new window, the content control is setup like this:
<Grid>
<ContentControl prism:RegionManager.RegionName="SetupRegion" />
</Grid>
I have tested by replacing the code in the Navigate command on the main window and having it to navigate to the new user controls, showing them in the main window and that works.
However, in the new window they are not and I can't seem to figure out why. I have also tested to see if the button in the first/default user control view model is firing correctly and it is - for reference, here is that Navigate command code:
private void Navigate(string uri)
{
// WriteLine command to test the button firing
Console.WriteLine(uri);
regionManager.RequestNavigate("SetupRegion", uri);
}
Lastly I've placed the first view inside the main window and it seems to fire correctly, changing the content in the main window - I just can't get it to work or anything to work on the new window at all no matter how I try it. I'm assuming that there is something that I don't know that has to do with either navigation on new instances of windows (aside from the main window) or having to do with containers and new windows but I haven't been able to figure out what. Thank you guys for all of your assistance as always.
It turn out Brian Lagunas has a course on multiple shells I should hopefully be able to use to accomplish what I need. I will attempt to use it.

Set WPF Checkbox value on window draw (not application start)

I have been searching for this for ages now and still have no idea how to achieve it, this is my first C#/WPF application so it is entirely possible that I have seen the answer and just not known it, apologies if it is really easy.
I have a winForms application that launches a WPF window (the winForms is because the app is a system tray icon that spawns other windows). The only WPF window I have currently has two checkboxes that I have managed to store the value of in app.config, that value is also successfully applied to the checkboxes when the application first starts. However I cannot work out how to have the value applied to the checkbox every time the window is opened.
The window is opened by this (with an attempt to fix it commented out):
private void notifyIcon1_DoubleClick(object Sender, EventArgs e)
{
var RestartPortal = new RestartPortal();
//RestartPortal.InvalidateVisual();
RestartPortal.Show();
}
The values are applied to the window on application run with this (along with another attempt):
public RestartPortal()
{
InitializeComponent();
alwaysOnTopCheck.InvalidateVisual();
closeWhenCompleteCheck.InvalidateVisual();
alwaysOnTopCheck.IsChecked = bool.Parse(ConfigurationManager.AppSettings.Get("onTopChecked"));
closeWhenCompleteCheck.IsChecked = bool.Parse(ConfigurationManager.AppSettings.Get("autoCloseChecked"));
}
If can help out I will be incredibly appreciative.
Thank you.
Edit: In testing I have noticed that the checkboxes seem to be remembering the value that they were assigned when the application first ran, even stranger they force this value into the app.config file each time the window is loaded. To explain better:
Run application
Load Window
Load config in notepad and check that the values correlate
(close notepad) Uncheck checkboxes
Open config file in notepad again to check that values have been saved correctly.
Close window
Open window
Open config file (again in a fresh notepad) and see that the values correlate and that by opening the window the values have been forced back to the values they held when the application was first run.
To my mind the only way that this can be happening is if somehow something is calling the event handlers for the checkboxes as that is the only place that can save to the app.config file. I am now even more confused than I was before, in case it helps here is the XAML for the checkboxes:
<CheckBox x:Name="closeWhenCompleteCheck" Margin="5" HorizontalAlignment="Left" Content="Close when complete" Checked="closeWindow_Checked" Unchecked="closeWindow_Unchecked"/>
<CheckBox x:Name="alwaysOnTopCheck" Margin="5" HorizontalAlignment="Left" Content="Always on top" Checked="onTop_Checked" Unchecked="onTop_Unchecked"/>
And here is the c# for the checked and uncheckeds (though only for one checkbox as they are identical):
private void onTop_Checked(object sender, RoutedEventArgs e)
{
this.Topmost = true;
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.Settings["onTopChecked"].Value = (alwaysOnTopCheck.IsChecked).ToString();
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("AppSettings");
}
private void onTop_Unchecked(object sender, RoutedEventArgs e)
{
this.Topmost = false;
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.Settings["onTopChecked"].Value = (alwaysOnTopCheck.IsChecked).ToString();
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("AppSettings");
}
Any help will be very much appreciated as ever (and probably save my sanity at this point).
Thank you.
You should try utilizing the Loaded event instead of the constructor. If your window closes, but the variable storing it does not get destroyed, your constructor will not be called again.
public RestartPortal()
{
InitializeComponent();
// Subscribe to the Loaded event
this.Loaded += RestartPortal_Loaded;
}
void RestartPortal_Loaded(object sender, RoutedEventArgs e)
{
// Set your values here
alwaysOnTopCheck.IsChecked = bool.Parse(ConfigurationManager.AppSettings.Get("onTopChecked"));
closeWhenCompleteCheck.IsChecked = bool.Parse(ConfigurationManager.AppSettings.Get("autoCloseChecked"));
}

WPF C# Application I get 2 main windows

I am really new to WPF and c#, and have some questions about how the applications are initialized. I am going through some tutorials on LINQ classes in the hopes that I can bind all of my SQL Server data with observableCollections.
One of the examples I found has a start like so (in the App.xaml.cs file):
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow app = new MainWindow();
ProductViewModel context = new ProductViewModel();
app.DataContext = context;
app.Show();
}
}
When I run the application I get 2 main windows. The commands above execute and open the first one, but then a second one gets called from some system code which I can't see (I can see in dissasembler, but that does not really help). So it seems that the application that VS set up for me has a standard entry, but the sample code (from Rachel Lim) does not do this. I have searched for differences in various files (like App.g.i.cs which has the void Main() call) and both my application and the sample are the same. I am trying to "take control" of the application to handle creating my observablecollections with the LINQ classes. Is anyone familiar enough with VS and C# to give me some hints on what might be happening?
The problem is probably in your App.xaml which often has the following:
Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
If your App.xaml.cs opens a window in one of the events as you have demonstrated, then you need to make sure to remove that StartupUri="MainWindow.xaml" in your Application element.

How to Show an External WPF Before MainWindow?

My WPF application calls upon a separate project to handle a login process BEFORE the Main Window in my application is shown. This creates a problem and causes "Application Shutdown" errors because the FIRST window in the application has closed. How can I handle the login process BEFORE my Main Window is shown? Every search I find comes up with references to Prism or MEF... which I cannot use.
If you want to control everything from the very start of your application, you need to create your own main method and use this as "start object" (see project properties). More details can be found in another SO answer, but this is its essence:
[STAThread]
public static void Main(string[] args)
{
// Do anything you like before running the main window.
// ...
// Proceed with usual application flow.
var app = new MyApplication();
var win = new MyWindow();
app.Run(win);
}
To prevent the application shutdown error, you can change Application.ShutdownMode to OnExplicitShutdown. And explicitly call Application.Shutdown Method to close your application when needed.
Have you tried adding code to the App.xaml.cs file? There are places you can place code in there that runs before the main window is opened. In addition to a constructor, there's the Startup event that you can assign a handler to in the App.xaml file:
<Application x:Class="CarSystem.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DispatcherUnhandledException="App_DispatcherUnhandledException"
Exit="Application_Exit"
Startup="Application_Startup"
StartupUri="MainWindow.xaml">
And, of course there's the Main method in the same file that you could throw code into, as well.

Multiple calls of Application.Run in WPF

I'm trying to run a window, close it, and then run a second window, in a similar way that seems to work with Windows Forms.
namespace WpfApplication1
{
public partial class App : Application
{
[STAThread]
public static void Main()
{
Application app = new Application();
//windowMain.Show();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
MainWindow windowMain = new MainWindow();
app.Run(windowMain);
Window1 window1 = new Window1();
window1.Show();
app.Run(window1);
}
}
}
I've set the Build Action in the App.xaml properties from ApplicationDefinition to Page, but the programme throws an exception when window1 is initialised. What am I doing wrong?
Edit: I've modified the xaml in App.xaml as suggested by first answer and edited main as suggested by the comment.
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
</Application.Resources>
</Application>
but I'm still getting the exception.
OK this is what I've divined so far. The Solution Builder looks for a Main() function. Why its not a WinMain() function I'm still not a hundred per cent clear on. If there is no Main(), you get an error. You can have more than one Main() as long as the Project properties: "Application" page/tab: property: "StartUp Object" is set to point to one of the main()s. This is done from an automatically created drop down list.
When a “WPF Application” project is created, Visual Studio(VS) create an xaml file called “App.xaml”. This is a class declaration where “App” is derived from the “Application” Class. VS also automatically generates hidden files for an xaml file. It creates a “name.g.i.cs” file, when the xaml file is created. It creates a “name.g.cs” file the first time the project is built after the creation of the xaml file. In this case it creates “App.g.cs” and “App.g.i.cs”. These files are hidden by default. To view them, press the “Show all files” button at the top of the Solution Explorer, they can be found in “\ obj\86\Debug” folder. When you delete an xaml file the “name.g.i.cs” and the “name.g.cs” files remain and are not deleted.
The “App.xaml” file’s “build Action” property is set to “Application Definition” when created by VS. When this property is set to “Application Definition” a Main() function is automatically created in “name.g.i.cs”:
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main()
{
WpfApplication8.App app = new WpfApplication8.App();
app.InitializeComponent();
app.Run();
}
When this property is set to “Page”, the Main() function is automatically removed by VS. You can create new “Application” derived classes in code or in xaml. I haven’t found a neat way to do it in xaml. There doesn’t seem to be a template for an xaml “Application” derived class. I created a “.cs” code file and then renamed it to an .xaml file. For some reason VS won’t allow you to have more than one xaml “Application” declaration file set to “Application Build”, it doesn’t even give you the option of choosing one in the "Project: Properties: Application": “Startup Object” property.
As you can see in the hidden Main(), an instance of “App” is instantiated and run. If using your own Main() function: an instance of, the base “Application” class, or an “Application” derived class (whether declared in code or in xaml), can be declared and run. The “Application” class should only be instantiated once and should only be run once. If the “Application” derived class is declared in xaml then a simple application can be run by using the StartUpUri property in the xaml file: StartupUri="Windowname.xaml". Alternatively the top level UI programme logic can be placed in a Startup event handler. If “Startup="Application_Startup" is placed in the “App.xaml” file then an event handler can be written:
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow windowMain = new MainWindow();
windowMain.ShowDialog();
Window1 window1 = new Window1();
window1.ShowDialog();
Shutdown();
}
You have to use ShowDialog() here, because it blocks until the window is closed. If you used Show() instead, it would show one window, then immediately show the other one and shutdown the application. In this case there's no need to call the Run() method yourself, that's done automatically.
The “Application” class instance can be run in code whether its declared in code or in xaml. You can then perform initialisation code prior to calling Run(). This would be placed in the Application_ Startup() event handler using the other way. However, if the “Application.Run” call is ever made in the programme, then no windows should be opened (using show() or ShowDialog()) in Main() or anywhere outside of the Application Class or within events and functions called from those events, called during “Application.Run()”.
The Application class has a ShutdownMode property (Application.ShutdownMode). The default for this is: “OnMainWindowClose”. This property can also be set to “OnLastWindowClose” or “OnExplicitShutdown” in code or in the xaml. You will need to reset this if you don't want the programme to close down when the MainWindow is closed.
I think for my purposes it is better not to use the Application class at all and just call the windows using Show() and “ShowDialog()”. This way I can use WPF pages but I could also call Windows Forms, or DirectX screens, as long as they are not open at the same time, or have no UI at all, if the programme is running remotely. Is there any reason for not doing it this way?
I think your application is shuting down when you close the first window. You need to set Application.ShutdownMode to OnExplicitShutdown.
If all you want to do is to show one window, when that closes, show another and when that closes, shutdown the whole application, you should keep the Build action as ApplicationDefinition, set ShutdownMode to OnExplicitShutdown (probably in App.xaml, but you can it in code-behind too) and put the following code in an event handler of the Startup event of your application:
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow windowMain = new MainWindow();
windowMain.ShowDialog();
Window1 window1 = new Window1();
window1.ShowDialog();
Shutdown();
}
You have to use ShowDialog() here, because it blocks until the window is closed. If you used Show() instead, it would show one window, then immediatelly show the other one and shutdown the application.
There's no need to run the Run() method yourself, that's done automatically.

Categories