Handle file opened from outside application to load WPF app? - c#

My WPF app has .xyz files that can open it (used the WIX installer), however, in my WPF app, I'd like to somehow capture this and call some loading functionality of the file that was double-clicked on from the File Explorer before the application was started.
Right now, if you double click the appropriate xyz file from File Explorer, it opens the application but obviously nothing else happens. Is there a way in my WPF code to detect this and call the necessary function passing in the filepath/name?

In Application there is the StartUp event where you can set your arguments.
Follow this example

You should be able to the file path in your App.xaml.cs class:
public partial class App : Application
{
public static string FilePath { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (e.Args != null && e.Args.Length > 0)
FilePath = e.Args[0];
}
}
If you store in a static property like this, you could access it from any class in your app:
string filePath = App.FilePath;

Related

Does webview2 in winui3 not support changing the download location?

I used cefsharp, I need to generate folders according to certain rules and download the web content to the specified directory. I'm going to replace it with webview2. I find that there is no way to specify the default download directory. Do you have any way?
It may evolve in the future, but currently, you must define the WEBVIEW2_USER_DATA_FOLDER environment variable manually as explained here WebView2 Globals, something like this:
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", #"c:\temp\mydata");
MyWebView.CoreWebView2Initialized += MyWebView_CoreWebView2Initialized;
}
private void MyWebView_CoreWebView2Initialized(WebView2 sender, CoreWebView2InitializedEventArgs args)
{
// udf will contain c:\temp\mydata
var udf = sender.CoreWebView2.Environment.UserDataFolder;
}
...
}

Same icon for all windows forms [duplicate]

This question already has answers here:
Set same icon for all my Forms
(8 answers)
Closed 1 year ago.
I am working on a project where my requirement is that i have to use a same icon on all my windows forms . I have atleast 50 windows forms and many more had to be added . So is there a way to set a default icon for all windows forms rathee than doing manually on each form page .
you have multiple solution for this problem
best idea would be to inherit from a common base-Form that sets the Icon in the constructor.
from : Form or : System.Windows.Forms.Form to : MyCustomForm
and then just change MyCustomForm icon with write this line in MyCustomForm Cunstrunctor
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
then you just change project icon from project properties > Application > Icon and Manifest > browse for a *.ico file and add it there.
this approach can change other property in all from for example font,size,anchor,...
If you have many forms like:
public class MyAppForm1 : Form {
...
}
Then instead of deriving from Form, then you create an intermediate MyIconForm:
public class MyIconForm : Form {
public MyIconForm() : base() {
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
}
}
Then you just have to update all your forms to:
public class MyAppForm1 : MyIconForm {
...
}
The short answer is no, there is no central location where you can set the icon to be used by all forms in your project. You have to do it yourself. There are a bunch of methods you can use, including using reflection to set the backing field for Form.DefaultIcon during program initialization.
First step: get an icon.
There are a couple of options here. You can load an icon from a file, a resource or an embedded resource (yes, two different types of resource). Or the application icon, but you said you don't have one.
For standard resources:
Open project properties
Click Add Resource, Add Existing File and browse to your icon.
Icon is added to Properties.Resources with a property named after the file.
For content files (distributed with the application):
Add icon file to project (Add Existing Item)
Set file's Build Action property to Content.
Set file's Copy to Output Directory to Copy Always or Copy if newer.
Load icon using new Icon(iconFilename).
For embedded resources:
Add icon file to project (Add Existing Item)
Set file's Build Action to Embedded Resource.
Use Assembly.CurrentAssembly.GetManifestResourceStream to open the resource as a stream.
Of the three I'd choose standard resource for most things.
Making it work 1: Form Constructor
Add the appropriate loading code to every form's constructor:
public partial class Form1 : Form
{
public Form1()
{
// Using standard resource method
Icon = Properties.Resources.FormIcon;
InitializeComponent();
}
}
Making it work 2: Base Class
This is a fairly simple option, assuming you have control over the source for all of your forms and are OK with going through all of them to change their base class. The base class simply sets the form's icon during construction:
public class DefaultIconForm : Form
{
// Using content file method
private static readonly Icon _defaultIcon = new Icon("FormIcon.ico");
public DefaultIconForm()
{
Icon = _defaultIcon;
}
}
public partial class Form1 : DefaultIconForm
{
public Form1()
{
InitializeComponent();
}
}
You'll need to change all of your forms and remember to inherit from DefaultIconForm for all future forms.
Making it work 3: modify Form.DefaultIcon
This one is a slightly nasty trick that relies on reflection and could fail on you at some point, but it means not having to change any other code in your application.
Open your Program.cs file and add this method to the Program class:
private static void SetDefaultFormIcon()
{
var field = typeof(Form).GetField("defaultIcon", BindingFlags.Static | BindingFlags.NonPublic);
// And for completeness, this is the Embedded Resource method
using (var stream = typeof(Program).Assembly.GetManifestResourceStream($"{Application.ProductName}.FormIcon.ico"))
{
var ico = new Icon(stream);
field?.SetValue(null, ico);
}
}
Now call SetDefaultFormIcon() from main() to initialize.
As written it works on both .NET Framework and .NET 5 WinForms applications. There's no guarantee that the defaultIcon hidden static field won't change in future, so be prepared for it to break at some point.

How can I open a file via click it in windows with a WPF app?

I made a WPF program, just like the notepad in windows.
Now I modified the file associations of extension .txt to my program.
After I clicked a txt file, the problem comes: how can my WPF program get the path of the file? (I need to get the path to open the file.)
I consider it is an easy job. However, it seems there is not any tutorial about this in Google(maybe I miss its keyword).
Would you please help me? Thank you.
Thanks for #Fildor 's tutorial in https://sa.ndeep.me/post/how-to-create-smart-wpf-command-line-arguments/
Others tutorial works also while this one is easier:
public MainWindow()
{
string[] args = Environment.GetCommandLineArgs();
foreach (string i in args)
{
MessageBox.Show(i);
}
}
The first index string in args is the path of your program.
The second index string in args is the path of the file.
Thanks for everyone helping me.
Basically, the associated file that you open will be passed as an argument of the Main function.
For WPF it's slightly different since you don't have a Main method directly, usually. The entry point of your program should be your <Application> class.
In this case, arguments to the program are passed along to the Startup event in Application class:
XAML:
<Application ....
Startup="app_Startup">
<!-- .... --->
</Application>
Codebehind:
public partial class App: Application {
public static string[] Args;
void app_Startup(object sender, StartupEventArgs e) {
// If no command line arguments were provided, don't process them if (e.Args.Length == 0) return;
if (e.Args.Length > 0) {
Args = e.Args;
}
}
}
(Example adapted from: https://www.c-sharpcorner.com/article/wpf-command-line/)

How can a Xamarin.Android application access files that have the Copy to Output Directory set to CopyAlways?

I am in the process of migrating a regular .net framework legacy library into a .net standard library. As shown in the picture below, the legacy library used to have a file that is always copied to the Output folder:
In that library, there is a legacy class that test the existence of the folder before accessing the file on the disk, like this:
namespace Library
{
public class LegacyClass
{
public void AccessFile()
{
string path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
bool directoryExists = Directory.Exists(Path.Combine(path, "Files"));
// It does exist in the Console app. It does not in the Android app.
// ... And then access the file.
}
}
}
While it was working with a Console app, it is not working in a blank Xamarin application:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
//
var legacyClass = new LegacyClass();
legacyClass.AccessFile();
}
The LegacyClass is not able to find the Files folder:
Question
How can a Xamarin.Android application access files that have the Copy to Output Directory set to CopyAlways?
You can't do that, but you can achieve your goal using embedded resources easily.
https://developer.xamarin.com/samples/mobile/EmbeddedResources/
Instead, you've to add file to your csproj, then you can set its build action to Embedded Resource in the properties of that file. (Select file in solution explorer and press F4).
At runtime, you can use the following that returns a Stream to your file:
Assembly.Load("MyProjectWhichContainsTheFile").GetManifestResourceStream("MyFile.txt")

Loading config files / handling errors on startup in a WPF/MVVM app

Disclaimer: I have no prior experience in MVVM/MVC/MVP/MVWhatever, this is my very first try using any UI separation pattern.
On startup, my app needs to load data from a config file, which is needed for the application to work.
At the moment, I'm reading the config file on startup in App.xaml.cs, and I'm passing the content of the file to the view model:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
string configFile = "settings.txt";
string[] config = File.ReadAllLines(configFile);
var window = new MainWindow();
var viewmodel = new MainWindowViewModel(config);
window.DataContext = viewmodel;
window.Show();
}
}
1. Is this the "correct" MVVM way?
I'm sure that this way is better than reading the file directly in the view model (that's how I did it first), but I'm not sure if App.xaml.cs is the right place at all for reading config files.
2. Where/how do I handle errors?
The data from the config file is essential for the application.
So if the file is missing or empty, or if the data in the file is invalid, then I need to display an error message and quit the application.
My first attempt would be to put that in App.xaml.cs as well:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
string configFile = "settings.txt";
if (!File.Exists(configFile))
{
MessageBox.Show("config file missing");
Application.Current.Shutdown();
return;
}
string[] config = File.ReadAllLines(configFile);
if (config.Count() == 0)
{
MessageBox.Show("config file empty");
Application.Current.Shutdown();
return;
}
var window = new MainWindow();
var viewmodel = new MainWindowViewModel(config);
window.DataContext = viewmodel;
window.Show();
}
}
But that doesn't look "right" to me. I already didn't feel comfortable with loading the file there (see first example), so this is even worse.
Note: I know that I probably shouldn't directly call Messagebox.Show here.
Please note that this is not what I'm asking here - I know that I should replace it by something more MVVM-like, but the point is: I would still have to call that (and then close the app) from somewhere.
What I actually want to know is if App.xaml.cs is the right place for this!
3. Plus, I have another kind of error to handle:
The actual parsing of the config file is done by the model (the model is part of an existing library and the WPF app I'm building here is just a nice UI for that).
So the view model creates an instance of the model and calls the parsing method...which throws an exception if the data from the config file is invalid.
How do I handle this the "MVVM way"?
Just catching the exception in the view model and closing the whole application from there feels wrong to me.
EDIT:
To answer Will's comment about why I'm not using app.config:
I'm using a "special" configuration file because I need to load several named "sets" of value pairs from there. Here's an example file.
Basically, it's a Dictionary<string, List<ClassWithTwoStringProperties>>.
AFAIK, app.config doesn't support data like this, so the only thing I could do is save the whole thing as a blob in one property in app.config, and still do the parsing myself.
--> so I'd still have to call the parsing from somewhere in the WPF app.
I wanted a simple, human editable format
the WPF app is just a nice UI for a library, and I need a config file in the library as well
I think, this way is incorrect.
You should think about app.config as about any persisted data (e.g. database, file). If you want to access persisted data in MVVM way, you should write a service (something like IApplicationDataService) and call this service implementation from your MainWindowViewModel code. It will be more MVVMish, if you will locate this service from some ServiceLocator or inject it via IoC container - this helps to write unit tests later.
Implementation of service should return to view model initialized model instance. Something like this:
public ApplicationDataService : IApplicationDataService
{
public ApplicationModel LoadApplicationData()
{
// process app.config here
}
}
public ViewModelBase<TModel>
{
public TModel Model
{
get { return model.Value; }
}
private readonly Lazy<TModel> model = new Lazy(GetModel);
protected abstract TModel GetModel();
}
public MainWindowViewModel<ApplicationModel> : ViewModelBase
{
protected override ApplicationModel GetModel()
{
try
{
var dataService = ServiceLocator.GetService<IApplicationDataService>();
return dataService.LoadApplicationData();
}
catch (AnyException e)
{
// oops!
}
}
}

Categories

Resources