سؤال

أنا أعمل على عنصر تحكم لربط العرض من ListView إلى آخر بحيث يتم تحديث عرض ListView الفرعي ليتوافق مع عرض ListView الرئيسي عند تمرير ListView الرئيسي.

لقد تمكنت حتى الآن من جعل ListViews التابعة تقوم بتحديث طريقة العرض الخاصة بها عند النقر فوق أزرار شريط التمرير الرئيسي.تكمن المشكلة في أنه عند النقر على شريط التمرير نفسه وسحبه، لا يتم تحديث طرق عرض القائمة التابعة.لقد ألقيت نظرة على الرسائل المرسلة باستخدام Spy++ ويتم إرسال الرسائل الصحيحة.

هنا هو قانون بلدي الحالي:

public partial class LinkedListViewControl : ListView
{
    [DllImport("User32.dll")]
    private static extern bool SendMessage(IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

    [DllImport("User32.dll")]
    private static extern bool ShowScrollBar(IntPtr hwnd, int wBar, bool bShow);

    [DllImport("user32.dll")]
    private static extern int SetScrollPos(IntPtr hWnd, int wBar, int nPos, bool bRedraw);

    private const int WM_HSCROLL = 0x114;

    private const int SB_HORZ = 0;
    private const int SB_VERT = 1;
    private const int SB_CTL = 2;
    private const int SB_BOTH = 3;
    private const int SB_THUMBPOSITION = 4;
    private const int SB_THUMBTRACK = 5;
    private const int SB_ENDSCROLL = 8;

    public LinkedListViewControl()
    {
        InitializeComponent();
    }

    private readonly List<ListView> _linkedListViews = new List<ListView>();

    public void AddLinkedView(ListView listView)
    {
        if (!_linkedListViews.Contains(listView))
        {
            _linkedListViews.Add(listView);

            HideScrollBar(listView);
        }
    }

    public bool RemoveLinkedView(ListView listView)
    {
        return _linkedListViews.Remove(listView);
    }

    private void HideScrollBar(ListView listView)
    {
        //Make sure the list view is scrollable
        listView.Scrollable = true;

        //Then hide the scroll bar
        ShowScrollBar(listView.Handle, SB_BOTH, false);
    }

    protected override void WndProc(ref Message msg)
    {
        if (_linkedListViews.Count > 0)
        {
            //Look for WM_HSCROLL messages
            if (msg.Msg == WM_HSCROLL)
            {
                foreach (ListView view in _linkedListViews)
                {
                    SendMessage(view.Handle, WM_HSCROLL, msg.WParam, IntPtr.Zero);
                }
            }
        }
    }
}

مرتكز على هذا المشنور في منتديات MS Tech حاولت التقاط حدث SB_THUMBTRACK ومعالجته:

    protected override void WndProc(ref Message msg)
    {
        if (_linkedListViews.Count > 0)
        {
            //Look for WM_HSCROLL messages
            if (msg.Msg == WM_HSCROLL)
            {
                Int16 hi = (Int16)((int)msg.WParam >> 16);
                Int16 lo = (Int16)msg.WParam;

                foreach (ListView view in _linkedListViews)
                {
                    if (lo == SB_THUMBTRACK)
                    {
                        SetScrollPos(view.Handle, SB_HORZ, hi, true);

                        int wParam = 4 + 0x10000 * hi;
                        SendMessage(view.Handle, WM_HSCROLL, (IntPtr)(wParam), IntPtr.Zero);
                    }
                    else
                    {
                        SendMessage(view.Handle, WM_HSCROLL, msg.WParam, IntPtr.Zero);
                    }
                }
            }
        }

        // Pass message to default handler.
        base.WndProc(ref msg);
    }

سيؤدي هذا إلى تحديث موقع ListView ScrollBar التابع ولكنه لا يغير العرض الفعلي في الطفل.

لذلك أسئلتي هي:

  1. هل من الممكن تحديث طرق عرض القائمة الفرعية عند سحب شريط تمرير ListView الرئيسي؟
  2. إذا كان الأمر كذلك، كيف؟
هل كانت مفيدة؟

المحلول

أردت أن أفعل نفس الشيء، وبعد البحث وجدت الكود الخاص بك هنا، مما ساعدني، لكنه بالطبع لم يحل المشكلة.لكن بعد تجربة الأمر وجدت الحل.

جاء المفتاح عندما أدركت أنه بما أن أزرار التمرير تعمل، فيمكنك استخدام ذلك لجعل شريط التمرير يعمل.بمعنى آخر، عندما يأتي حدث SB_THUMBTRACK، أقوم بإصدار أحداث SB_LINELEFT وSB_LINERIGHT المتكررة حتى يقترب عرض ListView التابع لي من المكان الرئيسي.نعم، هذا ليس مثاليًا، لكنه يعمل بشكل قريب بما فيه الكفاية.

في حالتي، يُسمى ListView الرئيسي الخاص بي "reportView"، بينما يُسمى ListView التابع الخاص بي "summaryView".إليك الكود الخاص بي:

public class MyListView : ListView
{
    public event ScrollEventHandler HScrollEvent;

    protected override void WndProc(ref System.Windows.Forms.Message msg) 
    {
        if (msg.Msg==WM_HSCROLL && HScrollEvent != null)
            HScrollEvent(this,new ScrollEventArgs(ScrollEventType.ThumbTrack, (int)msg.WParam));

        base.WndProc(ref msg);
    }
}

ثم معالج الحدث نفسه:

reportView.HScrollEvent += new ScrollEventHandler((sender,e) => {
    if ((ushort) e.NewValue != SB_THUMBTRACK)
        SendMessage(summaryView.Handle, WM_HSCROLL, (IntPtr) e.NewValue, IntPtr.Zero);
    else {
        int newPos = e.NewValue >> 16;
        int oldPos = GetScrollPos(reportView .Handle, SB_HORZ);                 
        int pos    = GetScrollPos(summaryView.Handle, SB_HORZ);
        int lst;

        if (pos != newPos)
            if      (pos<newPos && oldPos<newPos) do { lst=pos; SendMessage(summaryView.Handle,WM_HSCROLL,(IntPtr)SB_LINERIGHT,IntPtr.Zero); } while ((pos=GetScrollPos(summaryView.Handle,SB_HORZ)) < newPos && pos!=lst);
            else if (pos>newPos && oldPos>newPos) do { lst=pos; SendMessage(summaryView.Handle,WM_HSCROLL,(IntPtr)SB_LINELEFT, IntPtr.Zero); } while ((pos=GetScrollPos(summaryView.Handle,SB_HORZ)) > newPos && pos!=lst);
        }
    });

آسف بشأن التنسيق الغريب للحلقات أثناء وجودها هناك، ولكن هذه هي الطريقة التي أفضل بها ترميز أشياء من هذا القبيل.

كانت المشكلة التالية هي التخلص من أشرطة التمرير في ListView التابع.لقد لاحظت أن لديك طريقة تسمى HideScrollBar.هذا لم ينجح حقًا بالنسبة لي.لقد وجدت حلاً أفضل في حالتي وهو ترك شريط التمرير هناك، ولكن "تغطيته" بدلاً من ذلك.أفعل هذا مع رأس العمود أيضًا.أقوم فقط بتحريك عنصر تحكم الطفل الخاص بي إلى أعلى أسفل عنصر التحكم الرئيسي لتغطية رأس العمود.ثم أقوم بتمديد الطفل ليسقط من اللوحة التي تحتوي عليه.ومن ثم لتوفير جزء من الحدود على طول حافة اللوحة التي تحتوي عليها، أقوم بوضع عنصر تحكم لتغطية الحافة السفلية المرئية لـ ListView التابع لطفلي.وينتهي الأمر بمظهر جميل إلى حد ما.

أضفت أيضًا معالج الأحداث لمزامنة عرض الأعمدة المتغيرة، كما في:

reportView.ColumnWidthChanging += new ColumnWidthChangingEventHandler((sender,e) => {
    summaryView.Columns[e.ColumnIndex].Width = e.NewWidth;
    });         

في حين أن كل هذا يبدو قليلا من الحماقة، فإنه يعمل بالنسبة لي.

نصائح أخرى

هذا تخمين فقط لتدفق العصائر العقلية، لذا خذ الأمر كما تريد:في معالج التمرير للقائمة الرئيسية، هل يمكنك استدعاء معالج التمرير للقائمة الفرعية (تمرير المرسل وeventargs من الرئيسي)؟

أضف هذا إلى تحميل النموذج الخاص بك:

masterList.Scroll += new ScrollEventHandler(this.masterList_scroll);

والذي يشير إلى هذا:

private void masterList_scroll(Object sender, System.ScrollEventArgs e)
{
    childList_scroll(sender, e);
}

private void childList_scroll(Object sender, System.ScrollEventArgs e)
{
   childList.value = e.NewValue
}

سأقوم بإنشاء فصل دراسي خاص بي، وراثة من ListView لكشف أحداث التمرير الرأسي والأفقي.

بعد ذلك سأقوم بإنشاء معالجات تمرير في النموذج الخاص بي لمزامنة عنصري التحكم

هذا هو نموذج التعليمات البرمجية الذي يجب أن يسمح لعرض القائمة بنشر أحداث التمرير:

public class MyListView : System.Windows.Forms.ListView
{
    const int WM_HSCROLL = 0x0114;
    const int WM_VSCROLL = 0x0115;

    private ScrollEventHandler evtHScroll_m;
    private ScrollEventHandler evtVScroll_m;

    public event ScrollEventHandler OnHScroll
    {
        add
        {
            evtHScroll_m += value;
        }
        remove
        {
            evtHScroll_m -= value;
        }
    }

    public event ScrollEventHandler OnHVcroll
    {
        add
        {
            evtVScroll_m += value;
        }
        remove
        {
            evtVScroll_m -= value;
        }
    }

    protected override void WndProc(ref System.Windows.Forms.Message msg) 
    { 
        if (msg.Msg == WM_HSCROLL && evtHScroll_m != null) 
            {
            evtHScroll_m(this,new ScrollEventArgs(ScrollEventType.ThumbTrack, msg.WParam.ToInt32()));
            }

        if (msg.Msg == WM_VSCROLL && evtVScroll_m != null)  
        {
            evtVScroll_m(this, new ScrollEventArgs(ScrollEventType.ThumbTrack, msg.WParam.ToInt32()));
        }
        base.WndProc(ref msg); 
    }

الآن تعامل مع أحداث التمرير في النموذج الخاص بك:

قم بإعداد طريقة PInvoc لتتمكن من إرسال رسالة Windows إلى عنصر التحكم:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int SendMessage(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] int iMsg, int iWParam, int iLParam);

قم بإعداد معالجات الأحداث الخاصة بك (lstMaster وlstChild هما صندوقا قائمة):

lstMaster.OnVScroll += new ScrollEventHandler(this.lstMaster_OnVScroll);
lstMaster.OnHScroll += new ScrollEventHandler(this.lstMaster_OnHScroll);

const int WM_HSCROLL = 0x0114;      
const int WM_VSCROLL = 0x0115;  

private void lstMaster_OnVScroll(Object sender, System.ScrollEventArgs e)
{    
    SendMessage(lstChild.Handle,WM_VSCROLL,(IntPtr)e.NewValue, IntPtr.Zero); 
}

private void  lstMaster_OnHScroll(Object sender, System.ScrollEventArgs e)
{   
    SendMessage(lstChild.Handle,WM_HSCROLL,(IntPtr)e.NewValue, IntPtr.Zero); 
}

يمكن أن يكون الحل البسيط لمشكلتك هو التعامل مع رسالة الطلاء في عرض القائمة الأصلية والتحقق مما إذا كانت طرق عرض القائمة المرتبطة تعرض البيانات الصحيحة.إذا لم يحدث ذلك، فقم بتحديثها لعرض البيانات الصحيحة عن طريق استدعاء الأسلوب EnsureVisible.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top