How can I make a .Net 6 dll understand MessageBox and DialogResult - c#

I am moving some old code up to .Net6 from very old versions and am finding the new project references hard to set up.
I am trying to make a dll that has some knowledge of windows forms using C# in VScommunity 2022 and .net6.0.
I picked the C# WPF Class library as the default project to build from.
I am able to get the DLL to talk to my exe for basic operations but I am unable to get it to compile when it has references to things like MessageBox and DialogResult in it.
I cannot find any way to get VS to recognise these references. It wont let me add using System.Windows.Forms; (error CS0234 Forms does not exist in Windows) and I cant see any way to add via project properties.
What am I missing ?
Here is an example of what I am trying to compile
public class JFIO
{
public static StreamReader? OpenReader(string filepath, bool bQuiet)
{
StreamReader sr = null;
if (!File.Exists(filepath))
{
if (!bQuiet)
MessageBox.Show("Input file not found\n" + filepath);
return sr;
}
DialogResult r = DialogResult.OK;
while (r != DialogResult.Cancel)
{
try
{
sr = new StreamReader(filepath);
break;
}
catch (Exception e)
{
r = MessageBox.Show("Error opening file\n" + filepath + "\n" + e.ToString(), "", MessageBoxButtons.RetryCancel);
}
}
return sr;
}
}
Thanks for the input. This is just a simple dll that I want to use in several of my forms based EXE programs to manage file opening, IO etc.
I found that if, instead of the WPF Class Library, I use the WindowsFormControl Library as a root for the DLL I am able to access the MessageBox etc OK and achieve what I want but it will only allow the .Net framework 4.7.2, no 5.0 offered, Why is this ?

it doesn't answer your question or only partially, but i am also porting old .net code and couldn't understand how to check for dialog result in a wpf .net 6 application.
in doing so i found this:
DialogBoxWithResult dialogBoxWithResult = new DialogBoxWithResult();
// Open dialog box and retrieve dialog result when ShowDialog returns
bool? dialogResult = dialogBoxWithResult.ShowDialog();
switch (dialogResult)
{
case true:
// User accepted dialog box
break;
case false:
// User canceled dialog box
break;
default:
// Indeterminate
break;
}
so instead of checking for DialogResult.OK we now how to check for true/false ...
good luck, Chris

In your C# project go to Properties and under Application--> General you can set Enable Windows Forms for this project.
If you do so, you can only distribute to Windows computers as before (not platform independent).

Related

Precompiled .net 4.0 app wont run on other windows installations (error: trying to load assembly from network address)

Solution: I had to click "Unblock" in the file properties dialog in Windows Explorer.
I made an app in c# because i thought it will work on all windows versions. Today i was at my fathers place and downloaded my precompiled app from github. It was running but totally not as expected! Highlighted rows in a gridview wasnt visible, scollbars on a tabcontrol wasnt working and some other more or less small bugs. I had no time to download visual studio to recompile it but is this usual behavior? My father is running on windows 7 and i saw .NET framework 4.5 was installed - i compiled my app using v4.0 so i thought there should be no compatibility issues. I also created a Windows10 virtual machine at home after that and recognized it was the same behavior. I know highlighted rows or scrollbars are not that important but i also have a plugin system to load and reflect additional libraries and thats a basic feature of my app. And the assembly loader will NOT work either - just on the machine i have compiled on.
Should i compile it on multiple versions to support any "unknown" user and its operationg system? I saw that microsoft say i can determine the users version using https://learn.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed but i can do it after my app is installed so should i create some downloader to download the correct version after installation or how is this meant?
Edit i uploaded a video on youtube to show how it should work and how it accuaally works.. https://www.youtube.com/watch?v=DTQa8WlECa8
Edit I got an error message stating (translated from native german) "It was tried to load an assembly from a network address" but i dont understand this message, i never do so.. I load assemblies using
private void m_btnRegisterModule_Click(object objectSender, EventArgs eventArgs)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = true;
openFileDialog.Title = "Register Stack";
openFileDialog.Filter = "Assemblies (*.exe, *.dll)|*.exe;*.dll|All Files (*.*)|*.*";
if (openFileDialog.ShowDialog(this) == DialogResult.Cancel) return;
Assembly assembly = null;
foreach (String file in openFileDialog.FileNames)
{
try
{
assembly = System.Reflection.Assembly.LoadFile(file);
} catch (Exception) { return; }
Type[] arrayTypes = assembly.GetExportedTypes();
foreach (Type type in arrayTypes)
{
if (!typeof(ScriptStack.Runtime.Stack).IsAssignableFrom(type)) continue;
ConstructorInfo constructorInfo = null;
try
{
constructorInfo = type.GetConstructor(new Type[0]);
}
catch (Exception) { continue; }
try
{
object objectHostModule = constructorInfo.Invoke(new object[0]);
ScriptStack.Runtime.Stack hostModule = (ScriptStack.Runtime.Stack)objectHostModule;
m_scriptManager.RegisterHostStack(hostModule);
}
catch (Exception) { continue; }
}
}
UpdateModuleControl();
pluginFilter.Select();
}
I found .net local assembly load failed with CAS policy and try to load it.. otherwise. lets see how. For now i created a messagebox to see the exact exception.
Edit Solution: I had to click "Unblock" in the file properties dialog in Windows Explorer.
Clicking "Unblock" in the file properties dialog in Windows Explorer solved my issue in some way. By "in some way" i mean i cant expect everybody to know this so someone will just think its not working crap and drop the app.. i guess

Referencing WinRT/UWP libraries in a .NET desktop application while maintaining support for Windows 7

I am trying to reference "Windows.Networking.Connectivity" classes in my desktop application. I am basically interested in handling metered connections in my app.
Basically what I am trying to do is simple:
var connectionCost = NetworkInformation.GetInternetConnectionProfile().GetConnectionCost();
if (connectionCost.NetworkCostType == NetworkCostType.Unknown
|| connectionCost.NetworkCostType == NetworkCostType.Unrestricted)
{
//Connection cost is unknown/unrestricted
}
else
{
//Metered Network
}
The only method I know of that allows a desktop application to reference UWP assemblies is by manually editing the project file and adding the following line to the csproj file:
<TargetPlatformVersion>8.0</TargetPlatformVersion>
Applying the code and "hack" works fine but the problem is that doing so will prevent my app from running on Windows 7 which I need to support.
I was wondering if there is a way to reference UWP assemblies in a desktop application without having to drop support for Windows 7.
And since for the time being I only want to check if a connection is metered, I am open to suggestions about how to get this information without referencing Windows assemblies.
I found a way to use reflection and call UWP methods without having to specify a target platform. For my case this is what I did:
var networkInfoType = Type.GetType("Windows.Networking.Connectivity.NetworkInformation, Windows, ContentType=WindowsRuntime");
var profileType = Type.GetType("Windows.Networking.Connectivity.NetworkInformation, Windows, ContentType=WindowsRuntime");
var profileObj = networkInfoType.GetTypeInfo().GetDeclaredMethod("GetInternetConnectionProfile").Invoke(null, null);
dynamic profDyn = profileObj;
var costObj = profDyn.GetConnectionCost();
dynamic dynCost = costObj;
var costType = (NetworkCostType)dynCost.NetworkCostType;
if (costType == NetworkCostType.Unknown
|| costType == NetworkCostType.Unrestricted)
{
//Connection cost is unknown/unrestricted
}
else
{
//Metered Network
}

MonoDroid evaluation System.UnauthorizedAccessException in Directory.CreateDirectory()

Scenario:
Start MonoDevelop
new Android Application
Replace button click delegate with
string fullPath = "/data/misc.mvvmcross.customermanagement/files/_Caches/Pictures.MvvmCross/";
if (System.IO.Directory.Exists(fullPath))
{
button.Text = "exists";
}
else
{
button.Text = "not found";
Directory.CreateDirectory(fullPath);
}
run and click the button.
Directory.CreateDirectory will fail with
System.UnauthorizedAccessException
Have tried creating a new emulator image with different API levels, but problem is still here
Any thoughts anyone?
Ok, Have created new Android Emulator image and used that.
Everything now works.
Solution:
Create a new android emulator image for the API level you want (don't forget Google API support)
I'm wondering if this is caused by some kind of assembly/package naming issue.
I've tested this code:
string fullPath = Path.Combine(FilesDir.Path, "_Caches2/Pictures.MvvmCross/2/");
if (System.IO.Directory.Exists(fullPath))
{
button.Text = "exists";
}
else
{
button.Text = fullPath;
Directory.CreateDirectory(fullPath);
}
...and it works fine in a 2.3.3 emulator.
The data folder pattern is: /data/ * package name * /files/
And it is correct (I think) for one package not to be able to access the data of another.
So I'm wondering if somehow your package names are wrong - check the manifest tab and the manifest.xml file for your application?

Vista TaskDialog Wrapper: Unable to find an entry point named 'TaskDialogIndirect' in DLL 'ComCtl32'

I'm try to use Vista TaskDialog Wrapper and Emulator and I'm getting the following exception:
"Unable to find an entry point named 'TaskDialogIndirect' in DLL 'ComCtl32'."
...in a simple Console application:
class Program
{
[STAThread]
static void Main(string[] args)
{
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
PSTaskDialog.cTaskDialog.MessageBox(
"MessageBox Title",
"The main instruction text for the message box is shown here.",
"The content text for the message box is shown here and the text willautomatically wrap as needed.",
PSTaskDialog.eTaskDialogButtons.YesNo,
PSTaskDialog.eSysIcons.Information
);
}
}
What am I doing wrong?
UPDATE:
Actually, I'm working on an Excel plugin using excel-dna. How can I control what dll Excel loads?
http://exceldna.codeplex.com/discussions/286990#post728888
I haven't been at Office programming in a while, but my guess is that Excel loads both versions of comctl32, so you may need to use the Activation Context API to direct your code to the version that includes TaskDialog. Some ideas for fixing the problem (not solutions as such):
For test purposes, make a temporary enumeration of all modules in the active process - just to check if 6.10 is actually loaded (see below for a simple example of such an enumeration, albeit with a different intent).
Use the Activation Context API to get to the right version. Example of use from C# (for enabling themes by way of comctl32 6.0) here.
Alternatively (I never actually got this to work reliably in a WPF application I worked on), make a dialog abstraction class, which falls back to MessageDlg depending on the version available to you. There may be better ways of doing the check, but...:
FileVersionInfo version = ProcessUtils.GetLoadedModuleVersion("comctl32.dll");
if (version != null && version.FileMajorPart >= 6 && version.FileMinorPart >= 1)
{
// We can use TaskDialog...
}
else
{
// Use old style MessageBox
}
The enumeration of modules:
internal static FileVersionInfo GetLoadedModuleVersion(string name)
{
Process process = Process.GetCurrentProcess();
foreach (ProcessModule module in process.Modules)
{
if (module.ModuleName.ToLower() == name)
{
return module.FileVersionInfo;
}
return null;
}
}
In addition to what all the others are saying: This error will disappear if you set the ForceEmulationMode on PSTaskDialog to true.

Select folder dialog WPF

I develop a WPF4 application and in my app I need to let the user select a folder where the application will store something (files, generated reports etc.).
My requirements:
Ability to see the standard folder tree
Ability to select a folder
WPF look & feel, this dialog must look like part of a modern application designed for Windows Vista/7 and not Windows 2000 or even Win9x.
As I understand, until 2010 (.Net 4.0) there won't be a standard folder dialog, but maybe there are some changes in version 4.0?
Or the only thing I can do, is to use an old-school WinForms dialog? If it's the only way to do what I need, how can I make it looking closer to Vista/7 style and not Win9x?
Windows Presentation Foundation 4.5 Cookbook by Pavel Yosifovich on page 155 in the section on "Using the common dialog boxes" says:
"What about folder selection (instead of files)? The WPF
OpenFileDialog does not support that. One solution is to use Windows
Forms' FolderBrowseDialog class. Another good solution is to use the
Windows API Code Pack described shortly."
I downloaded the API Code Pack from Windows® API Code Pack for Microsoft® .NET Framework Windows API Code Pack: Where is it?, then added references to Microsoft.WindowsAPICodePack.dll and Microsoft.WindowsAPICodePack.Shell.dll to my WPF 4.5 project.
Example:
using Microsoft.WindowsAPICodePack.Dialogs;
var dlg = new CommonOpenFileDialog();
dlg.Title = "My Title";
dlg.IsFolderPicker = true;
dlg.InitialDirectory = currentDirectory;
dlg.AddToMostRecentlyUsedList = false;
dlg.AllowNonFileSystemItems = false;
dlg.DefaultDirectory = currentDirectory;
dlg.EnsureFileExists = true;
dlg.EnsurePathExists = true;
dlg.EnsureReadOnly = false;
dlg.EnsureValidNames = true;
dlg.Multiselect = false;
dlg.ShowPlacesList = true;
if (dlg.ShowDialog() == CommonFileDialogResult.Ok)
{
var folder = dlg.FileName;
// Do something with selected folder string
}
I wrote about it on my blog a long time ago, WPF's support for common file dialogs is really bad (or at least is was in 3.5 I didn't check in version 4), but it's easy to work around it.
You need to add the correct manifest to your application, that will give you a modern style message boxes and folder browser (WinForms, FolderBrowserDialog) but not WPF file open/save dialogs, this is described in those 3 posts (if you don't care about the explanation and only want the solution go directly to the 3rd):
Why am I Getting Old Style File Dialogs and Message Boxes with WPF
Will Setting a Manifest Solve My WPF Message Box Style Problems?
The Application Manifest Needed for XP and Vista Style File Dialogs and Message Boxes with WPF
Fortunately, the open/save dialogs are very thin wrappers around the Win32 API that is easy to call with the right flags to get the Vista/7 style (after setting the manifest)
Vista style open and save dialogs with WPF (without using the Vista bridge sample)
Add The Windows API Code Pack-Shell to your project
using Microsoft.WindowsAPICodePack.Dialogs;
...
var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
If you don't want to use Windows Forms nor edit manifest files, I came up with a very simple hack using WPF's SaveAs dialog for actually selecting a directory.
No using directive needed, you may simply copy-paste the code below !
It should still be very user-friendly and most people will never notice.
The idea comes from the fact that we can change the title of that dialog, hide files, and work around the resulting filename quite easily.
It is a big hack for sure, but maybe it will do the job just fine for your usage...
In this example I have a textbox object to contain the resulting path, but you may remove the related lines and use a return value if you wish...
// Create a "Save As" dialog for selecting a directory (HACK)
var dialog = new Microsoft.Win32.SaveFileDialog();
dialog.InitialDirectory = textbox.Text; // Use current value for initial dir
dialog.Title = "Select a Directory"; // instead of default "Save As"
dialog.Filter = "Directory|*.this.directory"; // Prevents displaying files
dialog.FileName = "select"; // Filename will then be "select.this.directory"
if (dialog.ShowDialog() == true) {
string path = dialog.FileName;
// Remove fake filename from resulting path
path = path.Replace("\\select.this.directory", "");
path = path.Replace(".this.directory", "");
// If user has changed the filename, create the new directory
if (!System.IO.Directory.Exists(path)) {
System.IO.Directory.CreateDirectory(path);
}
// Our final value is in path
textbox.Text = path;
}
The only issues with this hack are :
Acknowledge button still says "Save" instead of something like "Select directory", but in a case like mines I "Save" the directory selection so it still works...
Input field still says "File name" instead of "Directory name", but we can say that a directory is a type of file...
There is still a "Save as type" dropdown, but its value says "Directory (*.this.directory)", and the user cannot change it for something else, works for me...
Most people won't notice these, although I would definitely prefer using an official WPF way if microsoft would get their heads out of their asses, but until they do, that's my temporary fix.
The FolderBrowserDialog class from System.Windows.Forms is the recommended way to display a dialog that allows a user to select a folder.
Until recently, the appearance and behaviour of this dialog was not in keeping with the other file system dialogs, which is one of the reasons why people were reluctant to use it.
The good news is that FolderBrowserDialog was "modernized" in NET Core 3.0, so is now a viable option for those writing either Windows Forms or WPF apps targeting that version or later.
In .NET Core 3.0, Windows Forms users [sic] a newer COM-based control that was introduced in Windows Vista:
To reference System.Windows.Forms in a NET Core WPF app, it is necessary to edit the project file and add the following line:
<UseWindowsForms>true</UseWindowsForms>
This can be placed directly after the existing <UseWPF> element.
Then it's just a case of using the dialog:
using System;
using System.Windows.Forms;
...
using var dialog = new FolderBrowserDialog
{
Description = "Time to select a folder",
UseDescriptionForTitle = true,
SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
+ Path.DirectorySeparatorChar,
ShowNewFolderButton = true
};
if (dialog.ShowDialog() == DialogResult.OK)
{
...
}
FolderBrowserDialog has a RootFolder property that supposedly "sets the root folder where the browsing starts from" but whatever I set this to it didn't make any difference; SelectedPath seemed to be the better property to use for this purpose, however the trailing backslash is required.
Also, the ShowNewFolderButton property seems to be ignored as well, the button is always shown regardless.
Microsoft.Win32.OpenFileDialog is the standard dialog that any application on Windows uses. Your user won't be surprised by its appearance when you use WPF in .NET 4.0
The dialog was altered in Vista. WPF in .NET 3.0 and 3.5 still used the legacy dialog but that was fixed in .NET 4.0. I can only guess that you started this thread because you are seeing that old dialog. Which probably means you're actually running a program that is targeting 3.5. Yes, the Winforms wrapper did get the upgrade and shows the Vista version. System.Windows.Forms.OpenFileDialog class, you'll need to add a reference to System.Windows.Forms.
MVVM + WinForms FolderBrowserDialog as behavior
public class FolderDialogBehavior : Behavior<Button>
{
public string SetterName { get; set; }
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += OnClick;
}
protected override void OnDetaching()
{
AssociatedObject.Click -= OnClick;
}
private void OnClick(object sender, RoutedEventArgs e)
{
var dialog = new FolderBrowserDialog();
var result = dialog.ShowDialog();
if (result == DialogResult.OK && AssociatedObject.DataContext != null)
{
var propertyInfo = AssociatedObject.DataContext.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanRead && p.CanWrite)
.Where(p => p.Name.Equals(SetterName))
.First();
propertyInfo.SetValue(AssociatedObject.DataContext, dialog.SelectedPath, null);
}
}
}
Usage
<Button Grid.Column="3" Content="...">
<Interactivity:Interaction.Behaviors>
<Behavior:FolderDialogBehavior SetterName="SomeFolderPathPropertyName"/>
</Interactivity:Interaction.Behaviors>
</Button>
Blogpost: http://kostylizm.blogspot.ru/2014/03/wpf-mvvm-and-winforms-folder-dialog-how.html
Based on Oyun's answer, it's better to use a dependency property for the FolderName. This allows (for example) binding to sub-properties, which doesn't work in the original. Also, in my adjusted version, the dialog shows selects the initial folder.
Usage in XAML:
<Button Content="...">
<i:Interaction.Behaviors>
<Behavior:FolderDialogBehavior FolderName="{Binding FolderPathPropertyName, Mode=TwoWay}"/>
</i:Interaction.Behaviors>
</Button>
Code:
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interactivity;
using Button = System.Windows.Controls.Button;
public class FolderDialogBehavior : Behavior<Button>
{
#region Attached Behavior wiring
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += OnClick;
}
protected override void OnDetaching()
{
AssociatedObject.Click -= OnClick;
base.OnDetaching();
}
#endregion
#region FolderName Dependency Property
public static readonly DependencyProperty FolderName =
DependencyProperty.RegisterAttached("FolderName",
typeof(string), typeof(FolderDialogBehavior));
public static string GetFolderName(DependencyObject obj)
{
return (string)obj.GetValue(FolderName);
}
public static void SetFolderName(DependencyObject obj, string value)
{
obj.SetValue(FolderName, value);
}
#endregion
private void OnClick(object sender, RoutedEventArgs e)
{
var dialog = new FolderBrowserDialog();
var currentPath = GetValue(FolderName) as string;
dialog.SelectedPath = currentPath;
var result = dialog.ShowDialog();
if (result == DialogResult.OK)
{
SetValue(FolderName, dialog.SelectedPath);
}
}
}
The Ookii Dialogs for WPF has a VistaFolderBrowserDialog class that provides a complete implementation of a folder browser dialog for WPF.
https://github.com/augustoproiete/ookii-dialogs-wpf
There's also a version that works with Windows Forms.
Only such dialog is FileDialog. Its part of WinForms, but its actually only wrapper around WinAPI standard OS file dialog. And I don't think it is ugly, its actually part of OS, so it looks like OS it is run on.
Other way, there is nothing to help you with. You either need to look for 3rd party implementation, either free (and I don't think there are any good) or paid.
Just to say one thing, WindowsAPICodePack can not open CommonOpenFileDialog on Windows 7 6.1.7600.
A comment on the original question from C. Augusto Proiete suggested Ookii dialogs (https://github.com/ookii-dialogs/ookii-dialogs-wpf). That's what I ended up using in my situation. Here's how I used it in my app.
var dialog = new VistaFolderBrowserDialog()
{
Description = "Select Folder",
RootFolder = Environment.SpecialFolder.Desktop,
ShowNewFolderButton = true,
UseDescriptionForTitle = true
};
var result = dialog.ShowDialog();
if (result.HasValue && result.Value)
{
_mySelectedFolder = dialog.SelectedPath;
}

Categories

Resources