views:

276

answers:

2

I'm trying to synchronize the vertical scrollbars of two property grids. The idea is when a user scrolls one property grid the other property grid scrolls by the same amount.

My first approach was to handle the scroll event but it seems PropertyGrid doesn't generate this kind of event. I looked into the controls contained inside the PropertyGrid and there is a PropertyGridView, that I bet is the control with the scrollbar.

Does anybody know a workaround to achieve what I want?

Thank you.

A: 

It requires a little trickery, but this should do it:

using System;
using System.Windows.Forms;
using System.Reflection;

namespace WindowsApplication1 {
    public partial class Form1 : Form {
        Control m_pgv = null;

        public Form1 () {
            InitializeComponent ();

            // Set the Property Grid Object to something
            propertyGrid1.SelectedObject = dataGridView1;

            // Loop through sub-controls and find PropertyGridView
            foreach (Control c in propertyGrid1.Controls) {
                if (c.Text == "PropertyGridView")
                {
                    m_pgv = (Control)c;
                    break;
                }
            }

            // Reflection trickery to get a private field,
            // scrollBar in this case
            Type t = m_pgv.GetType ();
            FieldInfo f = t.GetField("scrollBar", 
                BindingFlags.Instance | BindingFlags.NonPublic);

            // Get the scrollBar for our PropertyGrid and add the event handler
            ScrollBar sb = (ScrollBar) f.GetValue(m_pgv);
            sb.Scroll +=  new ScrollEventHandler(propertyGrid1_Scroll);
        }

        private void propertyGrid1_Scroll (object sender, ScrollEventArgs e) {
            System.Console.WriteLine ("Scroll");
        }
    }
}
Jerry Fernholz
How do you then get another controls scroll to match the first?
Dog Ears
Using this code I was able to synchronize the two scrollbars. When I move one the other moves accordingly. The trouble is that the contents doesn't scroll with the scrollbar. I guess is better to abandon my original idea.
jassuncao
You have to call SetScrollPosition() on the neighboring PropertyGridView. See my second answer.
Jerry Fernholz
+3  A: 

This one shows the synchronization with the neighboring PropertyGridView. Note you will have to extend it to handle the user clicking on either control. This version updates propertyGrid2 to match propertyGrid1, but not vice versa.

using System;
using System.Windows.Forms;
using System.Reflection;

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        Control m_pgv_1 = null;
        Control m_pgv_2 = null;
        MethodInfo m_method_info;

        public Form1 ()
        {
            InitializeComponent ();

            // Set the Property Grid Object to something
            propertyGrid1.SelectedObject = dataGridView1;
            propertyGrid2.SelectedObject = dataGridView1;

            // Loop through sub-controlls and find PropertyGridView
            m_pgv_1 = FindControl (propertyGrid1.Controls, "PropertyGridView");
            m_pgv_2 = FindControl (propertyGrid2.Controls, "PropertyGridView");

            // Reflection trickery to get a private/internal field
            // and method, scrollBar and SetScrollOffset in this case
            Type type = m_pgv_1.GetType ();
            FieldInfo f = FindField (type, "scrollBar");
            m_method_info = FindMethod (type, "SetScrollOffset");

            // Get the scrollBar for our PropertyGrid and add the event handler
            ((ScrollBar)f.GetValue (m_pgv_1)).Scroll +=
                new ScrollEventHandler (propertyGrid1_Scroll);
        }

        private void propertyGrid1_Scroll (object sender, ScrollEventArgs e)
        {
            System.Console.WriteLine ("Scroll");

            // Set the new scroll position on the neighboring
            // PropertyGridView
            object [] parameters = { e.NewValue };
            m_method_info.Invoke (m_pgv_2, parameters);
        }

        private static Control FindControl (
            Control.ControlCollection controls, string name)
        {
            foreach (Control c in controls)
            {
                if (c.Text == name)
                    return c;
            }

            return null;
        }

        private static MethodInfo FindMethod (Type type, string method)
        {
            foreach (MethodInfo mi in type.GetMethods ())
            {
                if (method == mi.Name)
                    return mi;
            }

            return null;
        }

        private static FieldInfo FindField (Type type, string field)
        {
            FieldInfo f = type.GetField (field,
               BindingFlags.Instance | BindingFlags.NonPublic);

            return f;
        }
    }
}
Jerry Fernholz