Localization in winforms - winforms

Resource files are not getting created for the newly added forms when the localized property is set to true in VS 2012.
When I add a new form to the project, set the Localizable property to true and build the application, .resx files are not getting created.

Carefully follow this walkthrough. The experiment I did below in VS 2012 is working fine.
Step1.
Put a Label onto Form1
Set Form1.Localizable = true
Set Form1.Language = Default
Set label's text = "Hello world!"
Step2.
Set Form1.Language = Russian
Set label's text = "Привет мир!"
After these steps resource files become visible in Solution Explorer
Now add following code into Form1's constructor
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
switch (MessageBox.Show(
"Press 'Yes' for default language, 'No' for Russian.",
"Language Option", MessageBoxButtons.YesNo))
{
case System.Windows.Forms.DialogResult.Yes:
System.Threading.Thread.CurrentThread.CurrentUICulture =
System.Globalization.CultureInfo.CreateSpecificCulture("");
break;
case System.Windows.Forms.DialogResult.No:
System.Threading.Thread.CurrentThread.CurrentUICulture =
System.Globalization.CultureInfo.CreateSpecificCulture("ru");
break;
}
InitializeComponent();
}
}
}
Run the application and see the result.
The main purpose of the code is to show that CurrentUICulture must be set before the method InitializeComponent is called. In real applications, however, setting CurrentUICulture property, usually, takes place on program startup. So the code must be moved to where the program starts.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
switch (MessageBox.Show(
"Press 'Yes' for default language, 'No' for Russian.",
"Language Option", MessageBoxButtons.YesNo))
{
case System.Windows.Forms.DialogResult.Yes:
System.Threading.Thread.CurrentThread.CurrentUICulture =
System.Globalization.CultureInfo.CreateSpecificCulture("");
break;
case System.Windows.Forms.DialogResult.No:
System.Threading.Thread.CurrentThread.CurrentUICulture =
System.Globalization.CultureInfo.CreateSpecificCulture("ru");
break;
}
Application.Run(new Form1());
}
}
}
If you define UI language setting for your application then you can use the value of the setting here and set UI language. It will affect all forms you have defined in your application.

Related

Stubborn IntelliSense keeps flagging false "function X cannot be called with the given argument list"

Environment
Visual Studio 2013 and C++/CLI.
The (ho/e)rror
I faced a situation of IntelliSense giving error on a compiler compliant row. Thus a false positive.
The error is the following:
IntelliSense: function "< full qualified method name >" cannot be
called with the given argument list argument types are: <
expected argument type > object type is: < object type >
What happened
I made a UserControl. There I declared a custom event with relative delegate. I created a form. In form constructor I alloc a my user control instance and try to attach a form method to the control custom event.
Compiler says everything is ok. IntelliSense tells me that event attachment mismatches types.
How to reproduce
I digged in the problem and created an essential context that should reproduce the problem:
Create a solution with two projects: FavaTest (as ClassLibrary) and FavaForm (as Console application...or whatever).
In FavaTest create a UserControl whose name is FavaClass and paste the following in FavaClass.h.
#pragma once
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
namespace FavaTest {
public ref class FavaClass : public System::Windows::Forms::UserControl
{
public:
FavaClass(void)
{
InitializeComponent();
}
// -- here defines very simple event --
delegate void FavaDelegate();
event FavaDelegate^ FavaEvent;
protected:
~FavaClass()
{
if (components)
{
delete components;
}
}
private:
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
this->SuspendLayout();
//
// FavaClass
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->Name = L"FavaClass";
this->Size = System::Drawing::Size(249, 147);
this->ResumeLayout(false);
}
#pragma endregion
};
}
In project FavaForm create a Form whose name is LaForm and paste the following in LaForm.h
namespace FavaForm {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
public ref class LaForm : public System::Windows::Forms::Form
{
public:
LaForm(void)
{
InitializeComponent();
// here simply allocs a FavaClass object and try to attach to FavaEvent event
FavaTest::FavaClass ^item = gcnew FavaTest::FavaClass();
item->FavaEvent += gcnew FavaTest::FavaClass::FavaDelegate(this, &LaForm::onfava);
}
void onfava(){}
protected:
~LaForm()
{
if (components)
{
delete components;
}
}
private:
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
this->SuspendLayout();
//
// LaForm
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(368, 261);
this->Name = L"LaForm";
this->Text = L"LaForm";
this->ResumeLayout(false);
}
#pragma endregion
};
}
Build FavaTest
In FavaForm project common properties add a new reference to FavaTest in order to use its generated dll as a dependencie
Build solution.
Now, while the compiler heralds everything is fine, you should see that IntelliSense complains something on the event attachment row, with the following errore message:
IntelliSense: function "FavaTest::FavaClass::FavaEvent::add" cannot be called with the given argument list
argument types are:
(FavaTest::FavaClass::FavaDelegate ^)
object type is: FavaTest::FavaClass ^
Ready to run package
I packaged all this in a side-test-standalone-solution zip file in order to make it possibile to unzip and run, but unfortunately (IMHO also questionably) I cannot post it here dued to SE guidelines, so it's up to you to make the debug context according to the above.
The question
I could also be missing something, but I used several times this algorithm before and it worked perfectly, now I'm experiencing this on two different machines (VS2013 and VS2015). Does this error apply to you too? And what's wrong with IntelliSense? It is such a simple scenario that I can't imagine I'm the only one experiencing it. I found no clue on the Internet though.
Solved.
It came out that in order to avoid IntelliSense to get crazy, the delegate definition has to be out of the event parent class scope. So in my situation all I had to do to get things right (for IntelliSense) was to move delegate outside the class adding the public keyword, such as:
namespace FavaTest
{
public delegate void FavaDelegate(); // moved out of the class with "public"
public ref class FavaClass : public System::Windows::Forms::UserControl
{
public:
[...]
// -- here defines very simple event --
// delegate row away from here
event FavaDelegate^ FavaEvent;
[...]
}
}

How to Access members of WPF App & ClassLibrary and vice versa

I am into this wpf from last 2 weeks. I am currently developing a wpf application based on MVVM pattern. I have 2 projects inside my Solution in Visual C# 2010. One is a WPF application(lets say MSPBoardControl) and other is a Class Library(lets say ConnectViewComponent). Thus both the MSPBoardControl and ConnectViewComponent have the view, viewmodel, model classes respectively.
I have added the reference of ConnectViewComponent in my MSPBoardControl and I am able to access the member variables of ConnectViewComponent in my MSPBoardControl's View,Viewmodel and model class. My concern is how to access the member variables of MSPBoardControl from my ConnectViewComponent.
ViewModel of MSPBoardControl:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using ConnectViewComponent.Model;
using System.Windows.Input;
using ConnectViewComponent.Commands;
[[[using MSPBoardControl.ViewModel;]]]
namespace ConnectViewComponent.ViewModel
{
public class ConnectViewModel : INotifyPropertyChanged
{
public List<ConnectModel> m_BoardNames;
[[[BoardControlViewModel mBoardVM;]]]
public ConnectViewModel()
{
m_BoardNames = new List<ConnectModel>()
{
new ConnectModel() {Name = "Bavaria", Connection_Status = "Disconnected"},
new ConnectModel() {Name = "Redhook", Connection_Status = "Disconnected"},
};
}
public List<ConnectModel> BoardNames
{
//get set
}
private ConnectModel m_SelectedBoardItem;
public ConnectModel SelectedBoard
{
//get set
}
private ICommand mUpdater;
public ICommand ConnectCommand
{
get
{
if (mUpdater == null)
mUpdater = new DelegateCommand(new Action(SaveExecuted), new Func<bool>(SaveCanExecute));
return mUpdater;
}
set
{
mUpdater = value;
}
}
public bool SaveCanExecute()
{
return true;
}
public void SaveExecuted()
{
if (SelectedBoard.Connection_Status == "Disconnected" && SelectedBoard.Name == "Bavaria")
{
SelectedBoard.Connection_Status = "Connected";
}
else if (SelectedBoard.Connection_Status == "Disconnected" && SelectedBoard.Name == "Redhook")
{
SelectedBoard.Connection_Status = "Connected";
}
}
}
}
[[[ -- ]]] in my code denotes I am not able to access the members of BoardControlViewModel as well as USING Namespace.ViewModel too.
I cannot add the reference of BoardControl in my ConnectComponent project since it will lead to circular dependency. How can I access it? Please Help!!
Having circular dependencies in your project can be a "code smell". There are various ways to remove this "smell". For simplicity lets say that you have project A and project B that has a circular dependency.
Factor out the common types used by both projects and move them into a new project C. Let A and B reference C. This should remove either the dependency from A to B or the opposite dependency or even both dependencies.
If A and B has types that need to interact you need to decouple this interaction into a common set of abstractions (e.g. interfaces or abstract base classes). You should then move these types without any implementation into project C that both A and B references. This will allow the types in A and B to interact but only using the definitions in C. An example could be that A is the main application. It calls in interface IService defined in C but implemented in B and registers a callback IServiceCallback defined in C via IService. B can then call back into A using IServiceCallback without knowing that the implementation is in A.
If the types in A and B are strongly coupled you should merge A and B into a single project.
You can add a some type of "Common" library (project) that will contain those general classes. So both BoardControl and ConnectComponent can reference it.
Also you can check similar question.

PageBreak in flow documents at runtime

I AM able to get a text file on a flow document but now I have to divide the contents in proper pagebreaks at runtime i.e if contents are huge they shud get itself in number of pages that too at runtime.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TextOnFlowDoc
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
Paragraph paragraph = new Paragraph();
paragraph.Inlines.Add(System.IO.File.ReadAllText(#"C:\Lis.txt"));
paragraph.FontFamily = new FontFamily("CourierNew");
FlowDocument document = new FlowDocument(paragraph);
// FlowDocumentReader rdr = new FlowDocumentReader();
FlowDocScl.Document = document;
}
}
}
Now this "FlowDocScl" is now a flow document and needs to be breaked into pages AT RUNTIME.
I am not sure why you want custom page-breaks, if you display it in a FlowDocumentPageViewer for example you get automatic breaks if the content is too large for the viewer.
If you must insert breaks on demand you need to split the document in Blocks, those have a property called BreakPageBefore which when set to true inserts a page break before that block obviously.
Something like this (untested):
private void BreakAndAddText(string text)
{
var pages = text.Split(new string[] { "\\f" }, StringSplitOptions.None);
foreach (var page in pages)
{
document.Blocks.Add(new Paragraph(new Run(page)) { BreakPageBefore = true });
}
}

Design time data in WPF

[using vs2010 & expression blend v4]
Hi - trying to load up some design time data in WPF and Blend, using Josh Smith's concept here: http://joshsmithonwpf.wordpress.com/2010/04/07/assembly-level-initialization-at-design-time/
e.g.
[AttributeUsage(AttributeTargets.Assembly)]
public class DesignTimeBootstrapperAttribute : Attribute
{
public DesignTimeBootstrapperAttribute(Type type)
{
var dep = new DependencyObject();
Debug.WriteLine("here..?");
if (DesignerProperties.GetIsInDesignMode(dep))
{
// TODO: Design-time initialization…
IBootstrapper instance = Activator.CreateInstance(type) as IBootstrapper;
if (instance != null)
{
instance.Run();
}
}
}
}
With my attribute here in AssemblyInfo.cs, where AppBootstrapper extends MefBootstrapper.
[assembly: AssemblyCopyright("Copyright © 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: DesignTimeBootstrapper(typeof(AppBootstrapper))]
I don't want to use the Blend sample data, a) as it doesn't seem to create data for ObservableCollection and b) I'm in design mode by definition, so things will change quite a lot, but my 'generated data' will not.
Anyway, nothing seems to be happening.
Q1: How is it possible to debug the design time initialisation of my bootstrapper?
Q2: Do I need additional blend namespaces/ attributes etc in my View XAML?
(In my bootstrapper I'm just registering a different module where I want to replace RunTimeService with a DesignTimeService, exporting the IService interface).
TIA
To debug this:
Open your project in VS2010
Set a breakpoint in the assembly attribute constructor
Start a new instance of Blend 4
From VS2010 use Debug -> Attach to Process: and choose Blend
Switch to Blend and open your project
Open a XAML file that references your sample data
Also, any Debug.WriteLine should appear in the VS2010 output window.
If you can't get the attribute method to work (I haven't tried it myself), you can use this method (which I have used) from MVVM Light:
private bool? _isInDesignMode;
public bool IsInDesignMode
{
get
{
if (!_isInDesignMode.HasValue)
{
var prop = DesignerProperties.IsInDesignModeProperty;
_isInDesignMode =
(bool)DependencyPropertyDescriptor
.FromProperty(prop, typeof(FrameworkElement))
.Metadata.DefaultValue;
}
return _isInDesignMode.Value;
}
}

Open directory dialog

I want the user to select a directory where a file that I will then generate will be saved. I know that in WPF I should use the OpenFileDialog from Win32, but unfortunately the dialog requires file(s) to be selected - it stays open if I simply click OK without choosing one. I could "hack up" the functionality by letting the user pick a file and then strip the path to figure out which directory it belongs to but that's unintuitive at best. Has anyone seen this done before?
You can use the built-in FolderBrowserDialog class for this. Don't mind that it's in the System.Windows.Forms namespace.
using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
{
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
}
If you want the window to be modal over some WPF window, see the question How to use a FolderBrowserDialog from a WPF application.
EDIT: If you want something a bit more fancy than the plain, ugly Windows Forms FolderBrowserDialog, there are some alternatives that allow you to use the Vista dialog instead:
Third-party libraries, such as Ookii dialogs (.NET 4.5+)
The Windows API Code Pack-Shell:
using Microsoft.WindowsAPICodePack.Dialogs;
...
var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
Note that this dialog is not available on operating systems older than Windows Vista, so be sure to check CommonFileDialog.IsPlatformSupported first.
I created a UserControl which is used like this:
<UtilitiesWPF:FolderEntry Text="{Binding Path=LogFolder}" Description="Folder for log files"/>
The xaml source looks like this:
<UserControl x:Class="Utilities.WPF.FolderEntry"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel>
<Button Margin="0" Padding="0" DockPanel.Dock="Right" Width="Auto" Click="BrowseFolder">...</Button>
<TextBox Height="Auto" HorizontalAlignment="Stretch" DockPanel.Dock="Right"
Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
</DockPanel>
</UserControl>
and the code-behind
public partial class FolderEntry {
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FolderEntry), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(FolderEntry), new PropertyMetadata(null));
public string Text { get { return GetValue(TextProperty) as string; } set { SetValue(TextProperty, value); }}
public string Description { get { return GetValue(DescriptionProperty) as string; } set { SetValue(DescriptionProperty, value); } }
public FolderEntry() { InitializeComponent(); }
private void BrowseFolder(object sender, RoutedEventArgs e) {
using (FolderBrowserDialog dlg = new FolderBrowserDialog()) {
dlg.Description = Description;
dlg.SelectedPath = Text;
dlg.ShowNewFolderButton = true;
DialogResult result = dlg.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK) {
Text = dlg.SelectedPath;
BindingExpression be = GetBindingExpression(TextProperty);
if (be != null)
be.UpdateSource();
}
}
}
}
As stated in earlier answers, FolderBrowserDialog is the class to use for this. Some people have (justifiable) concerns with the appearance and behaviour of this dialog. The good news is that it 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 (you're out of luck if still using NET Framework though).
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.
Ookii folder dialog can be found at Nuget.
PM> Install-Package Ookii.Dialogs.Wpf
And, example code is as below.
var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
if (dialog.ShowDialog(this).GetValueOrDefault())
{
textBoxFolderPath.Text = dialog.SelectedPath;
}
More information on how to use it: https://github.com/augustoproiete/ookii-dialogs-wpf
For those who don't want to create a custom dialog but still prefer a 100% WPF way and don't want to use separate DDLs, additional dependencies or outdated APIs, I came up with a very simple hack using the Save As dialog.
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.
Ookii Dialogs includes a dialog for selecting a folder (instead of a file):
https://github.com/ookii-dialogs
For Directory Dialog to get the Directory Path, First Add reference System.Windows.Forms, and then Resolve, and then put this code in a button click.
var dialog = new FolderBrowserDialog();
dialog.ShowDialog();
folderpathTB.Text = dialog.SelectedPath;
(folderpathTB is name of TextBox where I wana put the folder path, OR u can assign it to a string variable too i.e.)
string folder = dialog.SelectedPath;
And if you wana get FileName/path, Simply do this on Button Click
FileDialog fileDialog = new OpenFileDialog();
fileDialog.ShowDialog();
folderpathTB.Text = fileDialog.FileName;
(folderpathTB is name of TextBox where I wana put the file path, OR u can assign it to a string variable too)
Note: For Folder Dialog, the System.Windows.Forms.dll must be added to the project, otherwise it wouldn't work.
I found the below code on below link... and it worked
Select folder dialog WPF
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'd suggest, to add in the nugget package:
Install-Package OpenDialog
Then the way to used it is:
Gat.Controls.OpenDialogView openDialog = new Gat.Controls.OpenDialogView();
Gat.Controls.OpenDialogViewModel vm = (Gat.Controls.OpenDialogViewModel)openDialog.DataContext;
vm.IsDirectoryChooser = true;
vm.Show();
WPFLabel.Text = vm.SelectedFilePath.ToString();
Here's the documentation:
http://opendialog.codeplex.com/documentation
Works for Files, files with filter, folders, etc
The best way to achieve what you want is to create your own wpf based control , or use a one that was made by other people
why ? because there will be a noticeable performance impact when using the winforms dialog in a wpf application (for some reason)
i recommend this project
https://opendialog.codeplex.com/
or Nuget :
PM> Install-Package OpenDialog
it's very MVVM friendly and it isn't wraping the winforms dialog
The Ookii VistaFolderBrowserDialog is the one you want.
If you only want the Folder Browser from Ooki Dialogs and nothing else then download the Source, cherry-pick the files you need for the Folder browser (hint: 7 files) and it builds fine in .NET 4.5.2. I had to add a reference to System.Drawing. Compare the references in the original project to yours.
How do you figure out which files? Open your app and Ookii in different Visual Studio instances. Add VistaFolderBrowserDialog.cs to your app and keep adding files until the build errors go away. You find the dependencies in the Ookii project - Control-Click the one you want to follow back to its source (pun intended).
Here are the files you need if you're too lazy to do that ...
NativeMethods.cs
SafeHandles.cs
VistaFolderBrowserDialog.cs
\ Interop
COMGuids.cs
ErrorHelper.cs
ShellComInterfaces.cs
ShellWrapperDefinitions.cs
Edit line 197 in VistaFolderBrowserDialog.cs unless you want to include their Resources.Resx
throw new InvalidOperationException(Properties.Resources.FolderBrowserDialogNoRootFolder);
throw new InvalidOperationException("Unable to retrieve the root folder.");
Add their copyright notice to your app as per their license.txt
The code in \Ookii.Dialogs.Wpf.Sample\MainWindow.xaml.cs line 160-169 is an example you can use but you will need to remove this, from MessageBox.Show(this, for WPF.
Works on My Machine [TM]
None of these answers worked for me (generally there was a missing reference or something along those lines)
But this quite simply did:
Using FolderBrowserDialog in WPF application
Add a reference to System.Windows.Forms and use this code:
var dialog = new System.Windows.Forms.FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
No need to track down missing packages. Or add enormous classes
This gives me a modern folder selector that also allows you to create a new folder
I'm yet to see the impact when deployed to other machines
I know this is an old question, but a simple way to do this is use the FileDialog option provided by WPF and using System.IO.Path.GetDirectory(filename).
You could use smth like this in WPF. I've created example method.
Check below.
public string getFolderPath()
{
// Create OpenFileDialog
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = false;
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (openFileDialog.ShowDialog() == true)
{
System.IO.FileInfo fInfo = new System.IO.FileInfo(openFileDialog.FileName);
return fInfo.DirectoryName;
}
return null;
}
It seems that the Microsoft.Win32 .NET library does not support selecting folders (only files), so you are out of luck in WPF (as of 7/2022). I feel the best option now is Ookii for WPF: https://github.com/ookii-dialogs/ookii-dialogs-wpf. It works great and as expected in WPF minus Microsoft support. You can get it as a NuGet package. Code behind XAML View:
public partial class ExportRegionView : UserControl
{
public ExportRegionView()
{
InitializeComponent();
}
private void SavePath(object sender, RoutedEventArgs e)
{
var dialog = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog();
dialog.Description = "SIPAS Export Folder";
dialog.UseDescriptionForTitle = true;
if (dialog.ShowDialog().GetValueOrDefault())
{
ExportPath.Text = dialog.SelectedPath;
}
}
}
XAML: <Button Grid.Row="1" Grid.Column="3" Style="{DynamicResource Esri_Button}" Click="SavePath" Margin="5,5,5,5">Path</Button>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Gearplay
{
/// <summary>
/// Логика взаимодействия для OpenFolderBrows.xaml
/// </summary>
public partial class OpenFolderBrows : Page
{
internal string SelectedFolderPath { get; set; }
public OpenFolderBrows()
{
InitializeComponent();
Selectedpath();
InputLogicalPathCollection();
}
internal void Selectedpath()
{
Browser.Navigate(#"C:\");
Browser.Navigated += Browser_Navigated;
}
private void Browser_Navigated(object sender, NavigationEventArgs e)
{
SelectedFolderPath = e.Uri.AbsolutePath.ToString();
//MessageBox.Show(SelectedFolderPath);
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
}
string [] testing { get; set; }
private void InputLogicalPathCollection()
{ // add Menu items for Cotrol
string[] DirectoryCollection_Path = Environment.GetLogicalDrives(); // Get Local Drives
testing = new string[DirectoryCollection_Path.Length];
//MessageBox.Show(DirectoryCollection_Path[0].ToString());
MenuItem[] menuItems = new MenuItem[DirectoryCollection_Path.Length]; // Create Empty Collection
for(int i=0;i<menuItems.Length;i++)
{
// Create collection depend how much logical drives
menuItems[i] = new MenuItem();
menuItems[i].Header = DirectoryCollection_Path[i];
menuItems[i].Name = DirectoryCollection_Path[i].Substring(0,DirectoryCollection_Path.Length-1);
DirectoryCollection.Items.Add(menuItems[i]);
menuItems[i].Click += OpenFolderBrows_Click;
testing[i]= DirectoryCollection_Path[i].Substring(0, DirectoryCollection_Path.Length - 1);
}
}
private void OpenFolderBrows_Click(object sender, RoutedEventArgs e)
{
foreach (string str in testing)
{
if (e.OriginalSource.ToString().Contains("Header:"+str)) // Navigate to Local drive
{
Browser.Navigate(str + #":\");
}
}
}
private void Goback_Click(object sender, RoutedEventArgs e)
{// Go Back
try
{
Browser.GoBack();
}catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Goforward_Click(object sender, RoutedEventArgs e)
{ //Go Forward
try
{
Browser.GoForward();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void FolderForSave_Click(object sender, RoutedEventArgs e)
{
// Separate Click For Go Back same As Close App With send string var to Main Window ( Main class etc.)
this.NavigationService.GoBack();
}
}
}

Resources