(version 19.1.20191.82)
A colleague wrote a custom `FilterCondition` a while ago, and when applied to a column, it automatically gets stored as part of the DisplayLayout. That is, if I later on call `grid.DisplayLayout.LoadFromXml()`, I get the same filter settings back. It appears they accomplished this by implementing `ISerializable` as well as having a constructor with `SerializationInfo` and `StreamingContext`.
I now wanted to do the same for a custom `GroupByComparer` and `GroupByEvaluator`. However, the same approach doesn't appear to work — when saving the layout, `ISerializable.GetObjectData` is never called.
Is there a way to store the GroupByComparer and/or GroupByEvaluator in the layout? Failing that, is there a more generalized way to add additional information to the layout (ideally, associated with the respective column)?
Yeah, the distinction is pretty subtle at first glance. But the main reason is that it's an interface vs. a derived class. Another difference is that it's possible to select a custom FilterCondition through the grid's UI. You could handle the BeforeRowFilterDropDown event and add items to the list such that the DataValue is actually an instance of a FilterConditon-derived class so the user can choose it that way. Whereas the GroupByComparer is typically something that the user can't choose - at least not via the grid's UI.
Mike Saltzman said:Well, I still don't see a great deal of value in storing a class that you created in code and that the user cannot really customize as part of the layout. Except insomuch as that it would be convenient to just loading it up. But really, you could just load the layout and then assigned it, just as you did the first time.
Right. I guess I don't quite see how that argument doesn't also apply to filter conditions, though: we wrote a custom class, needed to create a custom GUI for the user to customize it, and it got serialized as part of the layout. So when loading the layout, it gets applied.
Thus, I had intuitively assumed the same approach could be used by something like GroupByComparer.
Mike Saltzman said:
If you really want to store it as part of the layout, then the simplest way to handle this would be to use the Tag property on the column. The Tag gets serialized with the layout. So all you would have to do is store the object you want in the column's Tag right before you save the layout and then restore it again after loading the layout.
Of course, since you have two objects to store there (The GroupByComparer and the GroupByEvaluator), you would need to create an object that is serializable and stores those two object on itself, but that shouldn't be very difficult.
Yeah, I'll look into doing it that way.
Thanks!
Well, I still don't see a great deal of value in storing a class that you created in code and that the user cannot really customize as part of the layout. Except insomuch as that it would be convenient to just loading it up. But really, you could just load the layout and then assigned it, just as you did the first time. If you really want to store it as part of the layout, then the simplest way to handle this would be to use the Tag property on the column. The Tag gets serialized with the layout. So all you would have to do is store the object you want in the column's Tag right before you save the layout and then restore it again after loading the layout.
Of course, since you have two objects to store there (The GroupByComparer and the GroupByEvaluator), you would need to create an object that is serializable and stores those two object on itself, but that shouldn't be very difficult. Or, if you don't want to do that, you could use the Tag of the column to store the Comparer and some other object associated with the column (such as the Header) to store the other one.
Does that make sense?
Hi Mike,
Thanks for getting back to me.
Mike Saltzman said: It's not uncommon for application to implement an interface like this on a Form or other object and it would not be good if you saved your grid layout and it serialized the entire Form along with it.
Right. I understand the concern, but I'm making model classes expressly for these settings.
Mike Saltzman said:But the user can't change the GroupByComparer or GroupByEvaluator on a column - at least not through any UI the grid exposes. So are you exposing a UI for your user to choose different GroupByComparer or GroupByEvaluator options? What exactly is the use case here?
Yes — I'm making a UI to further customize GroupByRows.
The use case arose from two customer requests:
The above scenarios basically already work; I still need to fiddle a bit with how I'm adding a button to show my UI (using a IUIElementCreationFilter), and I'd like the whole thing to be serialized as part of a layout (there's a UI to let users choose between multiple different layouts, so it'd be beneficial if this got restored as part of them).
Mike Saltzman said:If you are saving the grid's layout to Xml, then in theory, you could create a stream, save that layout into that stream, and then save addition information into the stream. You could add any Xml into the stream that you wanted. The only tricky part is that when you read that stream back in, you load the grid layout from the stream first and that will position the stream to the end of the layout so you can then read in your custom data.
That would work (although it might not be possible to do in a backwards-compatible manner), but I was hoping for something that is just part of the existing layout. For example, the layout already has a notion of column context, ("which column do these settings apply to?"), and I'd have to replicate all that.
Mike Saltzman said:If you wanted to serialize your GroupByComparer or GroupByEvaluator, you could do that, although frankly, I think it might be simpler for you to just store some text that indicates which column to apply to and then the minuimum basic information your application needs in order to re-apply the correct object.
Right. I was hoping to reuse the existing structures here.
Hi Soren,
I'm following up on this issue. I've been looking into it, and the problem is that FilterCondition is a concrete class, whereas GroupByComparer and GroupByEvaluator are interfaces. Of course, in your application, there will be some kind of concrete implementation of the GroupByComparer or GroupByEvaluator that could, in theory, be serialized. But since these are interfaces, it's possible for you to implement them on some other class that you really don't want to be serialized - like your Form. It's not uncommon for application to implement an interface like this on a Form or other object and it would not be good if you saved your grid layout and it serialized the entire Form along with it.
So the best we could possibly do here is expose some kind of option which would allow you to opt-in to serializing these interfaces. But it's a pretty dangerous thing to do and someone could easily end up serializing their form or some other object without realizing they are doing it, so I am very wary of going down that road.
I'm curious as to why you would want to serialize these classes, anyway, though. The user can create and change FilterConditions and it's possible for you to add custom FilterConditions into the dropdown list for a column, and so it makes perfect sense to want to serialize these as user-customized options.
But the user can't change the GroupByComparer or GroupByEvaluator on a column - at least not through any UI the grid exposes. So are you exposing a UI for your user to choose different GroupByComparer or GroupByEvaluator options? What exactly is the use case here? If you are saving the grid's layout to Xml, then in theory, you could create a stream, save that layout into that stream, and then save addition information into the stream. You could add any Xml into the stream that you wanted. The only tricky part is that when you read that stream back in, you load the grid layout from the stream first and that will position the stream to the end of the layout so you can then read in your custom data. If you wanted to serialize your GroupByComparer or GroupByEvaluator, you could do that, although frankly, I think it might be simpler for you to just store some text that indicates which column to apply to and then the minuimum basic information your application needs in order to re-apply the correct object.