Невозможно заполнить элемент управления TreeView сообщениями Win32 в CSharp - PullRequest
0 голосов
/ 13 марта 2020

Я работаю с TreeView Control в Windows Forms в C#.

Поскольку фактическая часть населения TreeView Control занимает много времени, пользователь замораживает интерфейс. Итак, я пытаюсь выполнить заполнение, используя PostMessage Win32 API из фонового потока, но обнаружил, что Treeview не вставляется с элементами.

Итак, я переместил код из фонового потока в основной поток. Но тогда также не работает код вставки. Я получил похожий код, работающий с TreeView в C++ и пытающийся сделать то же самое с C#, используя процедуры взаимодействия.

Я не пойду обычным C# способом treeView1.Nodes.Add("..."), потому что он замораживает пользовательский интерфейс, даже если я использую метод Delegate и BackgroundWorker для заполнения элементов управления пользовательского интерфейса из другого потока. Я даю код, который я использую ниже. Может кто-нибудь, пожалуйста, помогите найти проблему с кодом.

Также обратите внимание на элемент управления TreeView Я использую свой собственный простой класс, производный от класса TreeView, где я переопределил метод WndProc для проверки потока сообщений Windows и I может видеть, что сообщения (TVM_INSERTITEM) действительно проходят, но все же элемент не заполняется

Также у меня есть подобный код взаимодействия, отлично работающий из Background Thread для ListView Control, но мои попытки с TreeView до сих пор не удалось.

Код класса формы

using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace UpdateTreeViewFromAnotherThread
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct TVITEM
{
    public uint mask;
    public IntPtr hItem;
    public uint state;
    public uint stateMask;
    public IntPtr pszText;
    public int cchTextMax;
    public int iImage;
    public int iSelectedImage;
    public int cChildren;
    public uint lParam;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct TVINSERTSTRUCT
{
    public IntPtr hParent;
    public IntPtr hInsertAfter;
    public TVITEM item;
}

public enum TreeViewInsert
{
    TVI_ROOT = -0x10000,
}

[Flags]
public enum TreeViewItemMask
{
    TVIF_TEXT = 0x0001,
}


public partial class Form1 : Form
{
    const int TV_FIRST = 0x1100;
    IntPtr tvInsItemPtr;
    TVINSERTSTRUCT tvins;
    IntPtr handleTreeView;

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
    public enum TreeViewMessage
    {
        TVM_INSERTITEM = TV_FIRST + 50,        
    }
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        handleTreeView = treeView1.Handle;
        //treeView1.Nodes.Add("hello");
        PopulateTree(handleTreeView);
    }

    public void PopulateTree(IntPtr handle)
    {
        tvins = new TVINSERTSTRUCT();
        tvins.item.mask = (uint)TreeViewItemMask.TVIF_TEXT;

        // Set the text of the item. 
        string productName = "Product";
        string value = productName;
        byte[] buffer = new byte[100];
        buffer = Encoding.Unicode.GetBytes(value + "\0");
        tvins.item.pszText = Marshal.AllocHGlobal(buffer.Length);
        Marshal.Copy(buffer, 0, tvins.item.pszText, buffer.Length);

        tvins.hParent = IntPtr.Zero;
        tvins.hInsertAfter = (IntPtr)(TreeViewInsert.TVI_ROOT);
        tvInsItemPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tvins));
        Marshal.StructureToPtr(tvins, tvInsItemPtr, true);
        PostMessage(treeView1.Handle, (uint)TreeViewMessage.TVM_INSERTITEM, IntPtr.Zero, tvInsItemPtr);
        //SendMessage(treeView1.Handle, (int)TreeViewMessage.TVM_INSERTITEM, 0, tvInsItemPtr);
    }
}
}

Код класса MyTreeView

using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace UpdateTreeViewFromAnotherThread
{
    class MyTreeView:TreeView
    {
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x1132)
            {
                TVINSERTSTRUCT anotherTVInsertStruct;
                anotherTVInsertStruct = (TVINSERTSTRUCT)Marshal.PtrToStructure(m.LParam, typeof(TVINSERTSTRUCT));
                string anotherNodeText = Marshal.PtrToStringAnsi(anotherTVInsertStruct.item.pszText);
            }
            if(m.Msg == 0x113F)
            {
                TVITEM anotherTVItem;
                anotherTVItem = (TVITEM)Marshal.PtrToStructure(m.LParam, typeof(TVITEM));
                string anotherNodeText = Marshal.PtrToStringAnsi(anotherTVItem.pszText);
            }
            base.WndProc(ref m);

            //Trace.WriteLine(m.Msg.ToString() + ", " + m.ToString());
        }
    }
}

Update_1

Запрещено NM_CUSTOMDRAW для просмотра дерева с использованием приведенного ниже кода. Благодаря коду в ссылка .

        protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_REFLECT + WM_NOTIFY:
                {
                    NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
                    switch ((int)nmhdr.code)
                    {
                        case NM_CUSTOMDRAW:
                            NMTVCUSTOMDRAW nmTvDraw = (NMTVCUSTOMDRAW)m.GetLParam(typeof(NMTVCUSTOMDRAW));
                            switch (nmTvDraw.nmcd.dwDrawStage)
                            {
                                case CDDS_ITEMPREPAINT:
                                    m.Result = (IntPtr)CDRF_DODEFAULT;
                                    break;
                            }
                            Marshal.StructureToPtr(nmTvDraw, m.LParam, false);
                            return;
                    }
                    break;
                }
        }
        base.WndProc(ref m);
    }

Так что теперь, если я изменю мою более раннюю функцию PopulateTree (обратите внимание на Thread.Sleep ()) и вызов ее в фоновом потоке, как показано ниже, не заморозить интерфейс во время процесса заполнения

    private void button1_Click(object sender, EventArgs e)
    {
        handleTreeView = treeView1.Handle;
        Thread backgroundThread = new Thread(() => PopulateTree(handleTreeView));
        backgroundThread.Start();
    }

    public void PopulateTree(IntPtr handle)
    {
        for(int i =0; i< 1000; i++)
        { 
            tvins = new TVINSERTSTRUCT();
            tvins.item.mask = (uint)TreeViewItemMask.TVIF_TEXT;

            // Set the text of the item. 
            string productName = "Product_" + i.ToString();
            string value = productName;
            byte[] buffer = new byte[100];
            buffer = Encoding.Unicode.GetBytes(value + "\0");
            tvins.item.pszText = Marshal.AllocHGlobal(buffer.Length);
            Marshal.Copy(buffer, 0, tvins.item.pszText, buffer.Length);

            tvins.hParent = IntPtr.Zero;
            tvins.hInsertAfter = (IntPtr)(TreeViewInsert.TVI_ROOT);
            tvInsItemPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tvins));
            Marshal.StructureToPtr(tvins, tvInsItemPtr, true);
            PostMessage(handle, (uint)TreeViewMessage.TVM_INSERTITEM, IntPtr.Zero, tvInsItemPtr);
            Thread.Sleep(1000);
        }
    }

1 Ответ

0 голосов
/ 17 марта 2020

Благодаря Jimi и MikiD я смог создать такое же поведение незамерзающего интерфейса, используя подходы BeginUpdate и BeginInvoke. Я изменил свой код, как показано ниже

        private async void button1_Click(object sender, EventArgs e)
    {
        await Task.Run(() => PopulateTree());
    }

    private async void PopulateTree()
    {
        for(int i = 0;i< 1000;i++)
        {
            treeView1.BeginInvoke( (MethodInvoker)delegate ()
                {
                    treeView1.BeginUpdate();
                    treeView1.Nodes.Add("Product_" + i.ToString());
                    treeView1.EndUpdate();
                }
            );

            System.Threading.Thread.Sleep(1000);
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...