DataFilters are passed the IDataAccessor, which you can try to cast to an IPropertyDataAccessor. In general, you can also try to cast this to a PropertyDescriptorDataAccessorBase. If you are setting PropertyGrid.SelectedObjects to more than 1 object, then this won't work, as MergedPropertyDataAccessor is used in that case.
Your first option is to cast to IPropertyDataAccessor. In which case, you'd have to use .NET reflection (as shown in the post referenced earlier) using the Target and ValueName properties. If the property is merged (i.e. it's a MergedPropertyDataAccessor), then Target will be an array of objects, otherwise it's the object that contains the given property.
The other option is to cast to PropertyDescriptorDataAccessorBase. In this case, the PropertyDescriptor is exposed and you can get custom attributes using that (i.e. PropertyDescriptor.Attributes). Again, if it's a merged property you'd have to do some more work and try to cast to a MergedPropertyDataAccessor. Then the merged properties are available via the MergedPropertyDataAccessor.PropertyDataAccessors collection.
If you declare the attribute in your source code, then you cannot change it during runtime. You can inject custom attributes dynamically using things like ICustomTypeDescriptor. But you'd have to call PropertyGrid.Refresh to pick up the new sort values.
Also, you can apply sorting using a custom IComparer, by setting PropertyGrid.SortComparer. This will be passed the IDataAccessors as well, but again you'd have to call PropertyGrid.Refresh if you change the sort order.