views:

245

answers:

1

I have a control extending PropertyGrid which allows users to set the properties of some of my program objects. These objects have an event which is raised when one of their properties changes, and the PropertyGrid subscribes to this event so that it refreshes itself when a property is changed. My problem occurs when large numbers of objects are selected, and the user sets a property on all of the objects at once. The control gets swamped with Refresh() requests, which take a long time (for example, setting a property on ~300 objects takes about 20 seconds with the auto-refresh feature turned on, and just a fraction of a second when it is turned off).

I would like to prevent the event handler from refreshing the grid while the property grid is in the process of setting a property, but unfortunately I haven't been able to find any way to determine when the grid "starts" and "stops" setting the property. I was hoping there would be methods or something I could override, such as...

override void OnSetPropertyStart()
{
    suppressRefresh = true;
}
override void OnSetPropertyEnd()
{
    suppressRefresh = false;
}

Unfortunately this doesn't seem to be the case. Is there any other way to determine when the property grid is setting a property, or to otherwise achieve this same effect?

+1  A: 

Is the type under your control? You could add a FooUpdating / FooUpdated pair of events? Another option would be to write a custom property-model with TypeDescriptionProvider, but I suspect that would be quite a lot of work. My first attempt would be a before/after pair...

Something like (updated to show 3.5 approach; see history for a 2.0 example):

class MyType : INotifyPropertyChanged, INotifyPropertyChanging
{
    public event PropertyChangedEventHandler PropertyChanged;
    public event PropertyChangingEventHandler PropertyChanging;

    protected void UpdateField<T>(ref T field, T newValue, string propertyName)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            OnPropertyChanging(propertyName);
            field = newValue;
            OnPropertyChanged(propertyName);
        }
    }
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this,
            new PropertyChangedEventArgs(propertyName));
    }
    protected void OnPropertyChanging(string propertyName)
    {
        PropertyChangingEventHandler handler = PropertyChanging;
        if (handler != null) handler(this,
            new PropertyChangingEventArgs(propertyName));
    }
    private string name;

    public string Name
    {
        get { return name; }
        set { UpdateField(ref name, value, "Name"); }
    }
    private DateTime dateOfBirth;
    public DateTime DateOfBirth
    {
        get { return dateOfBirth; }
        set { UpdateField(ref dateOfBirth, value, "DateOfBirth"); }
    }
}

Then just handle the two events and enable / disable updates as appropriate.

Marc Gravell
On .NET 3.5 or above, it would be tidy to implement the framework-defined INotifyPropertyChanging interface rather than creating a custom PropertyChanging event. This would avoid needless proliferation and presumably play more nicely with framework-defined features such as data binding.
itowlson
Very valid point. Editing to update.
Marc Gravell
Thanks for your answer - I will see how much effort this would take (I have a lot of different object types with lots of properties I would have to edit in order to do this).In the meantime I've alleviated the problem by limiting the PropertyGrid refreshes so that they can only happen every 25 ms. This is fast enough that it's not noticeable to the user, and seems to fix the problem with a ton of Refresh() requests "piling up" all at once.
Chris Vig