Я пытаюсь использовать FolderBrowserDialog из своего приложения WPF - ничего особенного. Меня не очень волнует, что на него смотрят Windows Forms.
Однако, когда я вызываю ShowDialog, я хочу передать окно владельца, которым является IWin32Window. Как мне получить это из моего элемента управления WPF?
Собственно, какое это имеет значение? Если я запустил этот код и использую перегрузку ShowDialog без параметров, он будет работать нормально. При каких обстоятельствах мне нужно пройти окно владельца?
Спасибо,
Крейг





Если вы укажете «Владелец», вы получите модальное диалоговое окно в указанном окне WPF.
Чтобы получить окно Win32, совместимое с WinForms, создайте класс, реализующий IWin32Window следующим образом
public class OldWindow : System.Windows.Forms.IWin32Window
{
IntPtr _handle;
public OldWindow(IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
И используйте экземпляр этого класса в WinForms
IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle; // 'this' means WPF Window
folderBrowserDialog.ShowDialog(new OldWindow(mainWindowPtr));
Спасибо за это - это почти правильно - я опубликую ответ ниже.
Это было абсолютно правильно и единственное, что работало для меня. System.Windows.PresentationSource.FromVisual (визуальный) возвращал значение null.
Преимущество передачи дескриптора владельца состоит в том, что FolderBrowserDialog не будет модальным для этого окна. Это предотвращает взаимодействие пользователя с вашим главным окном приложения, пока диалоговое окно активно.
Вы должны иметь возможность получить IWin32Window, используя PresentationSource.FromVisual и преобразовав результат в HwndSource, который реализует IWin32Window.
Также в комментариях здесь:
Хорошо, теперь разобрался - спасибо Джоби, чей ответ был близок, но не совсем.
Вот мой работающий код из приложения WPF:
Сначала вспомогательный класс:
private class OldWindow : System.Windows.Forms.IWin32Window
{
IntPtr _handle;
public OldWindow(IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
Затем, чтобы использовать это:
System.Windows.Forms.FolderBrowserDialog dlg = new FolderBrowserDialog();
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
System.Windows.Forms.IWin32Window win = new OldWindow(source.Handle);
System.Windows.Forms.DialogResult result = dlg.ShowDialog(win);
Я уверен, что смогу закончить это лучше, но в основном это работает. Ура! :-)
Попробуйте последние две строки из моего кода вместо этих 4 строк, я думаю, это сработает для вас с нашим вызовом FromVisual.
И вот моя последняя версия.
public static class MyWpfExtensions
{
public static System.Windows.Forms.IWin32Window GetIWin32Window(this System.Windows.Media.Visual visual)
{
var source = System.Windows.PresentationSource.FromVisual(visual) as System.Windows.Interop.HwndSource;
System.Windows.Forms.IWin32Window win = new OldWindow(source.Handle);
return win;
}
private class OldWindow : System.Windows.Forms.IWin32Window
{
private readonly System.IntPtr _handle;
public OldWindow(System.IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
System.IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
}
И чтобы на самом деле его использовать:
var dlg = new FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dlg.ShowDialog(this.GetIWin32Window());
Когда я запускаю этот код, я получаю исключение Win32Exception при закрытии диалогового окна. Кажется, это не проблема, и если я просто поймаю это, все вроде работает. Вы случайно не знаете, почему это может быть брошено?
Кажется, все работает нормально, но я сам не вижу в этом никакого смысла, вызов с 0 аргументами по-прежнему показывает «тот же» модальный диалог.
Работал у меня прекрасно, без исключений (.Net 3.5). Пришлось добавить оператор using для System.Windows.Interop. Легко расширить решение для использования с System.Windows.Forms.OpenFileDialog. Друзья, читатели - не забудьте проверить DialogResult, чтобы убедиться, что пользователь не нажимал кнопку «Отмена».
В моем событии Button это сработало: dlg.ShowDialog (this.GetIWin32Window (this));
Даже не смог бы скомпилировать без предложения Фарруха ... приведенный выше код использования неверен в том, что при вызове GetIWin32Window (????) не указан параметр.
Почему бы не использовать встроенный класс WindowInteropHelper (см. Пространство имен System.Windows.Interop). Этот класс уже реализует IWin32Window;)
Так что вы можете забыть о "классе OldWindow" ... использование остается прежним
Возможно, раньше, но в .NET 4 это не так.
//add a reference to System.Windows.Forms.dll
public partial class MainWindow : Window, System.Windows.Forms.IWin32Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
var fbd = new FolderBrowserDialog();
fbd.ShowDialog(this);
}
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get
{
return ((HwndSource)PresentationSource.FromVisual(this)).Handle;
}
}
}
Это работает с wpf? Я не могу заставить его работать; Разве этот код не работает только в формах Windows?
Перевод VB.net
Module MyWpfExtensions
Public Function GetIWin32Window(this As Object, visual As System.Windows.Media.Visual) As System.Windows.Forms.IWin32Window
Dim source As System.Windows.Interop.HwndSource = System.Windows.PresentationSource.FromVisual(Visual)
Dim win As System.Windows.Forms.IWin32Window = New OldWindow(source.Handle)
Return win
End Function
Private Class OldWindow
Implements System.Windows.Forms.IWin32Window
Public Sub New(handle As System.IntPtr)
_handle = handle
End Sub
Dim _handle As System.IntPtr
Public ReadOnly Property Handle As IntPtr Implements Forms.IWin32Window.Handle
Get
End Get
End Property
End Class
End Module
Я понимаю, что это старый вопрос, но вот подход, который может быть немного более элегантным (и может быть, а может и не быть доступен раньше) ...
using System;
using System.Windows;
using System.Windows.Forms;
// ...
/// <summary>
/// Utilities for easier integration with WinForms.
/// </summary>
public static class WinFormsCompatibility {
/// <summary>
/// Gets a handle of the given <paramref name = "window"/> and wraps it into <see cref = "IWin32Window"/>,
/// so it can be consumed by WinForms code, such as <see cref = "FolderBrowserDialog"/>.
/// </summary>
/// <param name = "window">
/// The WPF window whose handle to get.
/// </param>
/// <returns>
/// The handle of <paramref name = "window"/> is returned as <see cref = "IWin32Window.Handle"/>.
/// </returns>
public static IWin32Window GetIWin32Window(this Window window) {
return new Win32Window(new System.Windows.Interop.WindowInteropHelper(window).Handle);
}
/// <summary>
/// Implementation detail of <see cref = "GetIWin32Window"/>.
/// </summary>
class Win32Window : IWin32Window { // NOTE: This is System.Windows.Forms.IWin32Window, not System.Windows.Interop.IWin32Window!
public Win32Window(IntPtr handle) {
Handle = handle; // C# 6 "read-only" automatic property.
}
public IntPtr Handle { get; }
}
}
Затем из окна WPF вы можете просто ...
public partial class MainWindow : Window {
void Button_Click(object sender, RoutedEventArgs e) {
using (var dialog = new FolderBrowserDialog()) {
if (dialog.ShowDialog(this.GetIWin32Window()) == System.Windows.Forms.DialogResult.OK) {
// Use dialog.SelectedPath.
}
}
}
}
Actually, does it matter?
Я не уверен, имеет ли это значение в этом случае, но, как правило, вы должны сообщить Windows, какова ваша иерархия окон, поэтому, если щелкнуть родительское окно, в то время как дочернее окно является модальным, Windows может предоставить визуальную (и, возможно, звуковую) подсказку для пользователя. .
Кроме того, он гарантирует, что «правое» окно находится наверху, когда есть несколько модальных окон (не то чтобы я защищал такой дизайн пользовательского интерфейса). Я видел пользовательские интерфейсы, разработанные некой многомиллиардной корпорацией (оболочка которой остается безымянной), которая зависала просто потому, что одно модальное диалоговое окно «застряло» под другим, и пользователь даже не подозревал, что он был там, не говоря уже о том, как закрыть Это.
Что вы используете для этого. GetIWin32Window ()?
@StuiterSlurf Я не уверен, что понимаю вопрос. Спрашиваете о директиве using?
да. Моя Visual Studio не говорит, что мне нужно для этого
@StuiterSlurf Посмотрите на первый фрагмент кода - GetIWin32Window определен там как метод расширения, поэтому он автоматически доступен из второго фрагмента без using (при условии, что оба фрагмента находятся в одном проекте).
Вот простой способ.
System.Windows.Forms.NativeWindow winForm;
public MainWindow()
{
winForm = new System.Windows.Forms.NativeWindow();
winForm.AssignHandle(new WindowInteropHelper(this).Handle);
...
}
public showDialog()
{
dlgFolderBrowser.ShowDialog(winForm);
}
Ознакомьтесь с фантастическим Ookii.Dialogs Свена Гроота для WinForms и WPF, который дает вам современные диалоговые окна папок и файлов в стиле Vista.