为什么FolderBrowserDialog对话框不会滚动到所选文件夹?

时间:2011-08-04 13:19:03

标签: c# c folderbrowserdialog

如此屏幕截图所示,所选文件夹不在视图中。需要向下滚动才能查看所选文件夹。

enter image description here

相同的对话框显示在不同计算机上可见的所选文件夹

enter image description here

我在两台都有Windows 7的计算机上运行它。它在一台计算机上正常工作,但在第二台计算机上不正常。它看起来与Windows环境相反,而不是一些代码问题?任何人都可以建议任何修复吗?

代码没有变化。我使用了不同驱动器的较长路径,但结果相同。

private void TestDialog_Click ( object sender, EventArgs e )
        {
            //Last path store the selected path, to show the same directory as selected on next application launch.
            //Properties.Settings.Default.LastPath

            FolderBrowserDialog dlgFolder = new FolderBrowserDialog ();

            dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

            dlgFolder.SelectedPath = Properties.Settings.Default.LastPath;

            if (dlgFolder.ShowDialog () == System.Windows.Forms.DialogResult.OK)
            {

                Properties.Settings.Default.LastPath = dlgFolder.SelectedPath;               

                Properties.Settings.Default.Save ();
            }

        }

14 个答案:

答案 0 :(得分:78)

根本问题是FolderBrowserDialog中的设计决策不佳。首先,我们需要意识到FolderBrowserDialog不是.NET控件,而是Common Dialog并且是Windows的一部分。此对话框的设计者选择在显示对话框并选择初始文件夹后不向TreeView控件发送TVM_ENSUREVISIBLE消息。此消息使TreeView控件滚动,以便在窗口中显示当前选定的项目。

因此,我们需要做的就是发送TreeView作为FolderBrowserDialog TVM_ENSUREVISIBLE消息的一部分,一切都会很棒。对?好吧,不是那么快。这确实是答案,但有些事情阻碍了我们。

  • 首先,因为FolderBrowserDialog实际上不是.NET控件,所以它没有内部Controls集合。这意味着我们不能只从.NET中查找和访问TreeView子控件。

  • 其次,.NET FolderBrowserDialog类的设计者决定 密封 这个类。这个不幸的决定阻止我们从它派生并覆盖窗口消息处理程序。如果我们能够做到这一点,我们可能会在消息处理程序中收到TVM_ENSUREVISIBLE消息时尝试发布WM_SHOWWINDOW消息。

  • 第三个问题是我们无法发送TVM_ENSUREVISIBLE消息,直到Tree View控件实际存在为真实窗口,并且在我们调用ShowDialog方法之前它不存在。但是,此方法会阻止,因此一旦调用此方法,我们就无法发布消息。

为了解决这些问题,我使用单个方法创建了一个静态助手类,可以用来显示FolderBrowserDialog,并使其滚动到所选文件夹。我通过在调用对话框的Timer方法之前启动一个简短的ShowDialog,然后在TreeView处理程序中跟踪Timer控件的句柄(即,显示对话后)并发送我们的TVM_ENSUREVISIBLE消息。

此解决方案并不完美,因为它取决于有关FolderBrowserDialog的一些先验知识。具体来说,我使用其窗口标题找到对话框。这将打破非英语安装。我使用对话项ID来跟踪对话中的子控件,而不是标题文本或类名,因为我觉得这会随着时间的推移更加可靠。

此代码已在Windows 7(64位)和Windows XP上进行了测试。

这是代码: (您可能需要:using System.Runtime.InteropServices;

public static class FolderBrowserLauncher
{
    /// <summary>
    /// Using title text to look for the top level dialog window is fragile.
    /// In particular, this will fail in non-English applications.
    /// </summary>
    const string _topLevelSearchString = "Browse For Folder";

    /// <summary>
    /// These should be more robust.  We find the correct child controls in the dialog
    /// by using the GetDlgItem method, rather than the FindWindow(Ex) method,
    /// because the dialog item IDs should be constant.
    /// </summary>
    const int _dlgItemBrowseControl = 0;
    const int _dlgItemTreeView = 100;

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    /// <summary>
    /// Some of the messages that the Tree View control will respond to
    /// </summary>
    private const int TV_FIRST = 0x1100;
    private const int TVM_SELECTITEM = (TV_FIRST + 11);
    private const int TVM_GETNEXTITEM = (TV_FIRST + 10);
    private const int TVM_GETITEM = (TV_FIRST + 12);
    private const int TVM_ENSUREVISIBLE = (TV_FIRST + 20);

    /// <summary>
    /// Constants used to identity specific items in the Tree View control
    /// </summary>
    private const int TVGN_ROOT = 0x0;
    private const int TVGN_NEXT = 0x1;
    private const int TVGN_CHILD = 0x4;
    private const int TVGN_FIRSTVISIBLE = 0x5;
    private const int TVGN_NEXTVISIBLE = 0x6;
    private const int TVGN_CARET = 0x9;


    /// <summary>
    /// Calling this method is identical to calling the ShowDialog method of the provided
    /// FolderBrowserDialog, except that an attempt will be made to scroll the Tree View
    /// to make the currently selected folder visible in the dialog window.
    /// </summary>
    /// <param name="dlg"></param>
    /// <param name="parent"></param>
    /// <returns></returns>
    public static DialogResult ShowFolderBrowser( FolderBrowserDialog dlg, IWin32Window parent = null )
    {
        DialogResult result = DialogResult.Cancel;
        int retries = 10;

        using (Timer t = new Timer())
        {
            t.Tick += (s, a) =>
            {
                if (retries > 0)
                {
                    --retries;
                    IntPtr hwndDlg = FindWindow((string)null, _topLevelSearchString);
                    if (hwndDlg != IntPtr.Zero)
                    {
                        IntPtr hwndFolderCtrl = GetDlgItem(hwndDlg, _dlgItemBrowseControl);
                        if (hwndFolderCtrl != IntPtr.Zero)
                        {
                            IntPtr hwndTV = GetDlgItem(hwndFolderCtrl, _dlgItemTreeView);

                            if (hwndTV != IntPtr.Zero)
                            {
                                IntPtr item = SendMessage(hwndTV, (uint)TVM_GETNEXTITEM, new IntPtr(TVGN_CARET), IntPtr.Zero);
                                if (item != IntPtr.Zero)
                                {
                                    SendMessage(hwndTV, TVM_ENSUREVISIBLE, IntPtr.Zero, item);
                                    retries = 0;
                                    t.Stop();
                                }
                            }
                        }
                    }
                }

                else
                {
                    //
                    //  We failed to find the Tree View control.
                    //
                    //  As a fall back (and this is an UberUgly hack), we will send
                    //  some fake keystrokes to the application in an attempt to force
                    //  the Tree View to scroll to the selected item.
                    //
                    t.Stop();
                    SendKeys.Send("{TAB}{TAB}{DOWN}{DOWN}{UP}{UP}");
                }
            };

            t.Interval = 10;
            t.Start();

            result = dlg.ShowDialog( parent );
        }

        return result;
    }
}

答案 1 :(得分:7)

我使用了https://www.daniweb.com/software-development/csharp/threads/300578/folderbrowserdialog-expanding-the-selected-directory-

中的解决方法
FolderBrowserDialog^ oFBD = gcnew FolderBrowserDialog;
oFBD->RootFolder = Environment::SpecialFolder::MyComputer;
oFBD->SelectedPath = i_sPathImport;
oFBD->ShowNewFolderButton = false;     // use if appropriate in your application
SendKeys::Send ("{TAB}{TAB}{RIGHT}");  // <<-- Workaround
::DialogResult oResult = oFBD->ShowDialog ();

这不是最好的方式,但它对我有用 如果没有RootFolder,它在第一次调用时不起作用,但在第二次和之后。有了它,它始终有效。

正如其他人所观察到的那样,这种失败取决于操作系统:
我正在使用Win 7 Pro x64 SP1

答案 2 :(得分:7)

我知道这个线程已经过时了,但是使用扩展方法,可以将它添加到FolderBrowserDialog.ShowDialog方法中,然后在需要时重复使用。

示例(下面)只是使用简单的SendKeys方法(我讨厌这样做,但在这种情况下,它运行良好)。使用SendKeys方法跳转到对话框中的选定文件夹时,如果在Visual Studio中进行调试,则SendKeys调用将应用于当前窗口,该窗口将是活动的VS窗口。为了更加万无一失并避免错误的窗口获取SendKeys消息,那么扩展方法将包含外部方法调用以将消息发送到特定窗口,类似于Marc F发布的内容,但转换为C#。

internal static class FolderBrowserDialogExtension
{
    public static DialogResult ShowDialog(this FolderBrowserDialog dialog, bool scrollIntoView)
    {
        return ShowDialog(dialog, null, scrollIntoView);
    }

    public static DialogResult ShowDialog(this FolderBrowserDialog dialog, IWin32Window owner, bool scrollIntoView)
    {
        if (scrollIntoView)
        {
            SendKeys.Send("{TAB}{TAB}{RIGHT}");
        }

        return dialog.ShowDialog(owner);
    }
}

答案 3 :(得分:4)

在VB.Net代码上,只需在显示对话框之前放置这行代码。

SendKeys.Send ("{TAB}{TAB}{RIGHT}")

答案 4 :(得分:3)

我在不同的论坛上看到它可能是由R​​ootFolder引起的,因为SelectedPath和RootFolder是互斥的,这意味着它们不能共存但是使用默认的RootFolder(.Desktop),它至少允许爬树(导航驱动器/文件夹)。

但是,如果将RootFolder更改为Desktop以外的其他模式,则无法导航到UNC路径。

对Hans Passant的回答: 我尝试了这个Dialog扩展,它有TextBox,但没有运气。

Customising the browse for folder dialog to show the path

答案 5 :(得分:3)

我发现:

  1. 如果.SelectedPath以&#34; \&#34;结尾,则对话框将向下滚动以显示路径。
  2. 如果.SelectedPath未以&#34; \&#34;结尾,则路径仍处于选中状态,但无法确保显示。

答案 6 :(得分:2)

我在VB.NET中计算了一些内容,因此很容易将其转换为C#。 我是法国人,我是VB的初学者。 无论如何,你可以尝试我的解决方案。

我的想法是在显示folderBrowserDialog之前启动异步任务。

我自己发现了这个,但我受到布拉德职位的启发。 这是我的代码:

Imports System.Threading.Tasks
Imports Microsoft.VisualBasic.FileIO.FileSystem

Public Enum GW
    HWNDFIRST = 0
    HWNDLAST = 1
    HWNDNEXT = 2
    HWNDPREV = 3
    OWNER = 4
    CHILD = 5
    ENABLEDPOPUP = 6
End Enum

Public Declare Function SendMessageW Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal msg As UInteger, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As IntPtr
Public Declare Function FindWindowExW Lib "user32.dll" (ByVal hWndParent As IntPtr, ByVal hWndChildAfter As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> ByVal lpszClass As String, <MarshalAs(UnmanagedType.LPWStr)> ByVal lpszWindow As String) As IntPtr
Public Declare Function GetWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal wCmd As Long) As Long
Public Declare Function GetDesktopWindow Lib "user32" () As IntPtr
Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As IntPtr, ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer

Private Sub FolderBrowserDialog_EnsureVisible(FB As FolderBrowserDialog, _Owner As IntPtr)
    Dim hwnd As IntPtr
    Dim sClassname As New System.Text.StringBuilder(256)
    Thread.Sleep(50)                                     'necessary to let FolderBrowserDialog construct its window
    hwnd = GetDesktopWindow()                            'Desktop window handle.
    hwnd = GetWindow(hwnd, GW.CHILD)                     'We will find all children.
    Do Until hwnd = 0
        If GetWindow(hwnd, GW.OWNER) = _Owner Then       'If one window is owned by our main window...
            GetClassName(hwnd, sClassname, 255)
            If sClassname.ToString = "#32770" Then       'Check if the class is FolderBrowserDialog.
                Exit Do                                  'Then we found it.
            End If
        End If
        hwnd = GetWindow(hwnd, GW.HWNDNEXT)              'Next window.
    Loop                                                 'If no found then exit.
    If hwnd = 0 Then Exit Sub
    Dim hChild As IntPtr = 0
    Dim hTreeView As IntPtr = 0
    Dim i As Integer = 0
    Do
        i += 1
        If i > 1000 Then Exit Sub                                       'Security to avoid infinite loop.
        hChild = FindWindowExW(hwnd, hChild, Nothing, Nothing)          'Look for children windows of FolderBrowserDialog.
        hTreeView = FindWindowExW(hChild, 0, "SysTreeView32", Nothing)  'Look for treeview of FolderBrowserDialog.
        Thread.Sleep(5)                                                 'delay necessary because FolderBrowserDialog is in construction, then treeview maybe not yet exist.
    Loop While hTreeView = 0
    If SendMessageW(hwnd, &H46A, 1, FB.SelectedPath) = 0 Then           'Send message BFFM_SETEXPANDED to FolderBrowserDialog.
        SendMessageW(hTreeView, &H7, 0, Nothing)                        'Send message WM_SETFOCUS to the treeeview.
    End If
End Sub


Dim My_save_dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\My-Saves"

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim FolderBrowserDialog1 As New FolderBrowserDialog
    FolderBrowserDialog1.Description = "Choose your save files path."
    If Directory.Exists(My_save_dir) Then
        FolderBrowserDialog1.SelectedPath = My_save_dir
    Else
        FolderBrowserDialog1.SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
    End If

    Dim Me_handle = Me.Handle         'Store the main handle to compare after with each windows owner.
    Task.Run(Sub() FolderBrowserDialog_EnsureVisible(FolderBrowserDialog1, Me_handle))      'Here's the trick, run an asynchronous task to modify the folderdialog.
    If FolderBrowserDialog1.ShowDialog(Me) = System.Windows.Forms.DialogResult.OK Then
        My_save_dir = FolderBrowserDialog1.SelectedPath
    End If
End Sub

我在等你的建议。 有人可以将其翻译成C#,因为我不知道C#。

答案 7 :(得分:2)

我在c ++ / mfc中遇到了同样的问题。我可以在BFFM_INITIALIZED回调中使用:: PostMessage而不是:: SendMessage来放置TVM_ENSUREVISIBLE消息

    case BFFM_INITIALIZED: 
{
// select something
::SendMessage(m_hDialogBox, BFFM_SETSELECTION, TRUE, (LPARAM) pszSelection);


// find tree control
m_hTreeCtrl = 0;
HWND hchild = GetWindow(hWnd, GW_CHILD) ;
while (hchild != NULL)
{
  VS_TChar classname[200] ;
  GetClassName(hchild, classname, 200) ;

  if (VS_strcmp(classname, _T("SHBrowseForFolder ShellNameSpace Control")) == 0)
  {
    HWND hlistctrl = GetWindow(hchild, GW_CHILD) ;
    do
    { 
      GetClassName(hlistctrl, classname, 200) ;
      if (lstrcmp(classname, _T("SysTreeView32")) == 0)
      {
        m_hTreeCtrl = hlistctrl;
        break ;   
      }

      hlistctrl = GetWindow(hlistctrl, GW_HWNDNEXT) ;
    } while (hlistctrl != NULL);
  }      
  if (m_hTreeCtrl)
    break;
  hchild = GetWindow(hchild, GW_HWNDNEXT);      
}

if (m_hTreeCtrl)
{
  int item = ::SendMessage(m_hTreeCtrl, TVM_GETNEXTITEM, TVGN_CARET, 0);
  if (item != 0)             
    ::PostMessage(m_hTreeCtrl, TVM_ENSUREVISIBLE,0,item);
}
break;
}

答案 8 :(得分:1)

我已经阅读了上述讨论和解决方案。特别是Brat Oestreicher让我朝着正确的方向前进。实质上,我们必须首先在<!-- This is the shaded artifact --> <dependency> <groupId>com.my</groupId> <artifactId>application-b-client</artifactId> <version>1.0.2-SNAPSHOT</version> <classifier>shaded</classifier> </dependency> 对话框中找到TreeView控件,然后向该窗口发送SHBrowseForFolder消息。以下是在C中执行此操作。

TVM_ENSUREVISIBLE

非常感谢Gary Beene

答案 9 :(得分:0)

此链接有一个简单的答案对我很有用(我有窗口8.1

FolderBrowserDialog: Expanding the selected directory

答案 10 :(得分:0)

这对我有用

folderBrowserDialog1.Reset();  
folderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer;
folderBrowserDialog1.SelectedPath = WorkingFolder;

但仅在第二次使用对话框

之后

答案 11 :(得分:0)

dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;

不同

dlgFolder.RootFolder = Environment.SpecialFolder.Desktop;

What's the difference between SpecialFolder.Desktop and SpecialFolder.DesktopDirectory?

链接的线程表示作为路径,它们确实得到相同的结果。但它们并不相同,因为一条是逻辑路径而另一条是物理路径。

我发现当任何一个被分配到打开文件夹对话框的RootFolder时,结果行为可能会有所不同。

作为.RootFolder分配,某些版本的Windows(如win7)将其中任何一个视为&#34;桌面&#34;。也就是说,你可以看到&#34;计算机&#34;子条目,并打开,以查看单个驱动器号。 .SelectedPath以任一方式被选中,但只有当桌面的逻辑路径被分配给.RootFolder时,才会显示所选路径。

更糟糕的是,在win10预发行版中使用浏览文件夹对话框时,会出现&#34; DesktopDirectory&#34;就这样,只是桌面目录的内容,没有任何链接到逻辑桌面目录。并且不列出其下的任何子项。如果为win7编写的应用程序试图与win10一起使用,那将非常令人沮丧。

我认为OP的问题在于他们应该使用物理桌面作为根,当他们应该使用逻辑桌面时。

我没有解释为什么OP的两台不同机器的反应不同。我推测它们安装了两个不同版本的.NET框架。

win10预发布的事实是&#34; Stuck on Desktop&#34;浏览文件夹对话框的问题可能是由于win10预发行版附带的最新.NET框架。不幸的是,我仍然不知道这个(win10)案件中的所有事实,因为我还没有更新。

P.S。我发现win8也经历过&#34; Stuck on Desktop&#34;症状:

https://superuser.com/questions/869928/windows-8-1-folder-selection-dialog-missing-my-computer-and-sub-items

解决方法是在win8中选择备用GUI。也许类似的东西可以在win10预发布中完成。

答案 12 :(得分:0)

回应Marc F的帖子 - 我已将VB.Net转换为C#

    public enum GW
    {
        HWNDFIRST = 0,
        HWNDLAST = 1,
        HWNDNEXT = 2,
        HWNDPREV = 3,
        OWNER = 4,
        CHILD = 5,
        ENABLEDPOPUP = 6
    }

    [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SendMessageW", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
    public static extern IntPtr SendMessageW(IntPtr hWnd, uint msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
    [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindowExW", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
    public static extern IntPtr FindWindowExW(IntPtr hWndParent, IntPtr hWndChildAfter, [MarshalAs(UnmanagedType.LPWStr)] string lpszClass, [MarshalAs(UnmanagedType.LPWStr)] string lpszWindow);
    [System.Runtime.InteropServices.DllImport("user32", EntryPoint = "GetWindow", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
    public static extern UInt32 GetWindow(IntPtr hwnd, UInt32 wCmd);
    [System.Runtime.InteropServices.DllImport("user32", EntryPoint = "GetDesktopWindow", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
    public static extern IntPtr GetDesktopWindow();
    [System.Runtime.InteropServices.DllImport("user32", EntryPoint = "GetClassNameA", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
    public static extern int GetClassName(IntPtr hwnd, System.Text.StringBuilder lpClassName, int nMaxCount);

    private void FolderBrowserDialog_EnsureVisible(FolderBrowserDialog FB, IntPtr _Owner)
    {
        IntPtr hwnd = System.IntPtr.Zero;
        System.Text.StringBuilder sClassname = new System.Text.StringBuilder(256);
        Thread.Sleep(50); //necessary to let FolderBrowserDialog construct its window
        hwnd = GetDesktopWindow(); //Desktop window handle.
        hwnd = (System.IntPtr)GetWindow(hwnd, (UInt32)GW.CHILD); //We will find all children.
        while (!(hwnd == (System.IntPtr)0))
        {
            if (GetWindow(hwnd, (UInt32)GW.OWNER) == (UInt32)_Owner) //If one window is owned by our main window...
            {
                GetClassName(hwnd, sClassname, 255);
                if (sClassname.ToString() == "#32770") //Check if the class is FolderBrowserDialog.
                {
                    break; //Then we found it.
                }
            }
            hwnd = (System.IntPtr)GetWindow(hwnd, (UInt32)GW.HWNDNEXT); //Next window.
        } //If no found then exit.
        if (hwnd == (System.IntPtr)0)
        {
            return;
        }
        IntPtr hChild = (System.IntPtr)0;
        IntPtr hTreeView = (System.IntPtr)0;
        int i = 0;
        do
        {
            i += 1;
            if (i > 1000) //Security to avoid infinite loop.
            {
                return;
            }
            hChild = FindWindowExW(hwnd, hChild, null, null); //Look for children windows of FolderBrowserDialog.
            hTreeView = FindWindowExW(hChild, (System.IntPtr)0, "SysTreeView32", null); //Look for treeview of FolderBrowserDialog.
            Thread.Sleep(5); //delay necessary because FolderBrowserDialog is in construction, then treeview maybe not yet exist.
        } while (hTreeView == (System.IntPtr)0);
        if (SendMessageW(hwnd, 0x46A, 1, FB.SelectedPath) == (System.IntPtr)0) //Send message BFFM_SETEXPANDED to FolderBrowserDialog.
        {
            SendMessageW(hTreeView, 0x7, 0, null); //Send message WM_SETFOCUS to the treeeview.
        }
    }

测试过,它运行正常。确保您引用System.Runtime.InteropServices,System.Threading,System.Threading.Tasks

答案 13 :(得分:-1)

最好的方法,至少最可靠的方法是创建您自己的浏览器类对话框。滚动树的问题多年来一直很痛苦-它永远不会得到解决!

如果您知道如何在绘画中进行渲染,那么您将不能做太多..在绘画中快速完成是另一回事。

我首先要看的是GitHub上的开源.Net源代码,在您选择的.Net版本中,是您有兴趣改进的对话框类。您可能会感到惊讶,只需付出一点点努力就可以实现。只需复制控件并调试到发生错误并进行修补的地步-这就是Microsoft所做的,您也可以!

由于这是一个旧线程,因此发布样本可能永远不会被读取。如果有要求,它将自发布以来获得更多收益。

对于想解决诸如将树滚动到“ expected”目录的问题的人,这里有一些可靠的建议。如果没有立即解决方案的控件或库存在问题,请创建您自己的版本,并尽可能扩展原始版本并修补问题。我已经将Windows.Form.Control类到Win32库的所有内容进行了改进,其唯一目的是获得可预测和准确的结果。

好消息是,使用C#,可以使用许多低级控制来实现几乎任何合理的目标,并且C也是。

过去,我花了太多时间来寻找问题的解决方案,而我只是重新创建了无法解决的问题,因此可以节省很多时间。