Browse Source

Implemented Win32 version of file dialog

pull/189/head
Nikita Tsukanov 11 years ago
parent
commit
ef65e09b2d
  1. 34
      src/Perspex.Controls/CommonDialog.cs
  2. 2
      src/Perspex.Controls/Perspex.Controls.csproj
  3. 14
      src/Perspex.Controls/Platform/ICommonDialogImpl.cs
  4. 117
      src/Windows/Perspex.Win32/CommonDialogImpl.cs
  5. 52
      src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  6. 3
      src/Windows/Perspex.Win32/Perspex.Win32.csproj
  7. 2
      src/Windows/Perspex.Win32/Win32Platform.cs

34
src/Perspex.Controls/CommonDialog.cs

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Perspex.Controls.Platform;
using Splat;
namespace Perspex.Controls
{
public class CommonDialog
{
public string Title { get; set; }
public List<CommonDialogFilter> Filters { get; set; } = new List<CommonDialogFilter>();
public string InitialFileName { get; set; }
public string InitialDirectory { get; set; }
public bool AllowMultiple { get; set; }
public string DefaultExtension { get; set; }
public CommonDialogAction Action { get; set; }
public Task<string[]> ShowAsync(Window window = null)
=> Locator.Current.GetService<ICommonDialogImpl>().ShowAsync(this, window?.PlatformImpl);
}
public enum CommonDialogAction
{
OpenFile,
SaveFile
}
public class CommonDialogFilter
{
public string Name { get; set; }
public List<string> Extensions { get; set; } = new List<string>();
}
}

2
src/Perspex.Controls/Perspex.Controls.csproj

@ -41,6 +41,7 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="CommonDialog.cs" />
<Compile Include="DockPanel\Aligner.cs" />
<Compile Include="DockPanel\Alignment.cs" />
<Compile Include="DockPanel\Alignments.cs" />
@ -64,6 +65,7 @@
<Compile Include="Margins.cs" />
<Compile Include="MenuItemAccessKeyHandler.cs" />
<Compile Include="Mixins\SelectableMixin.cs" />
<Compile Include="Platform\ICommonDialogImpl.cs" />
<Compile Include="Presenters\IContentPresenter.cs" />
<Compile Include="Primitives\AccessText.cs" />
<Compile Include="Border.cs" />

14
src/Perspex.Controls/Platform/ICommonDialogImpl.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Platform;
namespace Perspex.Controls.Platform
{
public interface ICommonDialogImpl
{
Task<string[]> ShowAsync(CommonDialog dialog, IWindowImpl parent);
}
}

117
src/Windows/Perspex.Win32/CommonDialogImpl.cs

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Perspex.Controls;
using Perspex.Controls.Platform;
using Perspex.Platform;
using Perspex.Win32.Interop;
namespace Perspex.Win32
{
class CommonDialogImpl : ICommonDialogImpl
{
public unsafe Task<string[]> ShowAsync(CommonDialog dialog, IWindowImpl parent)
{
var hWnd = parent?.Handle?.Handle ?? IntPtr.Zero;
return Task.Factory.StartNew(() =>
{
var filters = new StringBuilder();
foreach (var filter in dialog.Filters)
{
var extMask = string.Join(";", filter.Extensions.Select(e => "*." + e));
filters.Append(filter.Name);
filters.Append(" (");
filters.Append(extMask);
filters.Append(")");
filters.Append('\0');
filters.Append(extMask);
filters.Append('\0');
}
if (filters.Length == 0)
filters.Append("All files\0*.*\0");
filters.Append('\0');
var filterBuffer = new char[filters.Length];
filters.CopyTo(0, filterBuffer, 0, filterBuffer.Length);
var buffer = new char[256];
fixed (char* pBuffer = buffer)
fixed (char* pFilterBuffer = filterBuffer)
fixed (char* pDefExt = dialog.DefaultExtension)
fixed (char* pInitDir = dialog.InitialDirectory)
fixed (char* pTitle = dialog.Title)
{
var ofn = new UnmanagedMethods.OpenFileName()
{
hwndOwner = hWnd,
hInstance = IntPtr.Zero,
lCustData = IntPtr.Zero,
nFilterIndex = 0,
Flags =
UnmanagedMethods.OpenFileNameFlags.OFN_EXPLORER |
UnmanagedMethods.OpenFileNameFlags.OFN_HIDEREADONLY,
nMaxCustFilter = 0,
nMaxFile = buffer.Length - 1,
nMaxFileTitle = 0,
lpTemplateName = IntPtr.Zero,
lpfnHook = IntPtr.Zero,
lpstrCustomFilter = IntPtr.Zero,
lpstrDefExt = new IntPtr(pDefExt),
lpstrFile = new IntPtr(pBuffer),
lpstrFileTitle = IntPtr.Zero,
lpstrFilter = new IntPtr(pFilterBuffer),
lpstrInitialDir = new IntPtr(pInitDir),
lpstrTitle = new IntPtr(pTitle),
};
ofn.lStructSize = Marshal.SizeOf(ofn);
if (dialog.AllowMultiple && dialog.Action == CommonDialogAction.OpenFile)
ofn.Flags |= UnmanagedMethods.OpenFileNameFlags.OFN_ALLOWMULTISELECT;
if (dialog.Action == CommonDialogAction.SaveFile)
ofn.Flags |= UnmanagedMethods.OpenFileNameFlags.OFN_NOREADONLYRETURN |
UnmanagedMethods.OpenFileNameFlags.OFN_OVERWRITEPROMPT;
var pofn = &ofn;
var res = dialog.Action == CommonDialogAction.OpenFile
? UnmanagedMethods.GetOpenFileName(new IntPtr(pofn))
: UnmanagedMethods.GetSaveFileName(new IntPtr(pofn));
if (!res)
return null;
}
var cStart = 0;
string dir = null;
var files = new List<string>();
for (var c = 0; c < buffer.Length; c++)
{
if (buffer[c] == 0)
{
//Encountered double zero char
if (cStart == c)
break;
var s = new string(buffer, cStart, c - cStart);
if (dir == null)
dir = s;
else
files.Add(s);
cStart = c + 1;
}
}
if (files.Count == 0)
return new[] {dir};
return files.Select(f => Path.Combine(dir, f)).ToArray();
});
}
}
}

52
src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
// ReSharper disable InconsistentNaming
#pragma warning disable 169
namespace Perspex.Win32.Interop
{
@ -677,7 +678,14 @@ namespace Perspex.Win32.Interop
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GlobalFree(IntPtr hMem);
[DllImport("comdlg32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetSaveFileNameW")]
public static extern bool GetSaveFileName(IntPtr lpofn);
[DllImport("comdlg32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetOpenFileNameW")]
public static extern bool GetOpenFileName(IntPtr lpofn);
[DllImport("comdlg32.dll")]
public static extern int CommDlgExtendedError();
public enum ClipboardFormat
{
@ -745,5 +753,49 @@ namespace Perspex.Win32.Interop
public string lpszClassName;
public IntPtr hIconSm;
}
[Flags]
public enum OpenFileNameFlags
{
OFN_ALLOWMULTISELECT = 0x00000200,
OFN_EXPLORER = 0x00080000,
OFN_HIDEREADONLY = 0x00000004,
OFN_NOREADONLYRETURN = 0x00008000,
OFN_OVERWRITEPROMPT = 0x00000002
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenFileName
{
public int lStructSize;
public IntPtr hwndOwner;
public IntPtr hInstance;
public IntPtr lpstrFilter;
public IntPtr lpstrCustomFilter;
public int nMaxCustFilter;
public int nFilterIndex;
public IntPtr lpstrFile;
public int nMaxFile;
public IntPtr lpstrFileTitle;
public int nMaxFileTitle;
public IntPtr lpstrInitialDir;
public IntPtr lpstrTitle;
public OpenFileNameFlags Flags;
private ushort Unused;
private ushort Unused2;
public IntPtr lpstrDefExt;
public IntPtr lCustData;
public IntPtr lpfnHook;
public IntPtr lpTemplateName;
public IntPtr reservedPtr;
public int reservedInt;
public int flagsEx;
}
}
}

3
src/Windows/Perspex.Win32/Perspex.Win32.csproj

@ -23,6 +23,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Perspex.Win32.XML</DocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -32,6 +33,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Perspex.Win32.XML</DocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -65,6 +67,7 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="ClipboardImpl.cs" />
<Compile Include="CommonDialogImpl.cs" />
<Compile Include="CursorFactory.cs" />
<Compile Include="Input\KeyInterop.cs" />
<Compile Include="Input\WindowsKeyboardDevice.cs" />

2
src/Windows/Perspex.Win32/Win32Platform.cs

@ -8,6 +8,7 @@ using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Disposables;
using System.Runtime.InteropServices;
using Perspex.Controls.Platform;
using Perspex.Input;
using Perspex.Platform;
using Perspex.Shared.PlatformSupport;
@ -41,6 +42,7 @@ namespace Perspex.Win32
private static void InitializeInternal()
{
var locator = Locator.CurrentMutable;
locator.Register(() => new CommonDialogImpl(), typeof (ICommonDialogImpl));
locator.Register(() => new PopupImpl(), typeof(IPopupImpl));
locator.Register(() => new ClipboardImpl(), typeof(IClipboard));
locator.Register(() => WindowsKeyboardDevice.Instance, typeof(IKeyboardDevice));

Loading…
Cancel
Save