Hi
I am getting data to my ultracombo from a webservice.
There are some 50,000 records but we sensibly provide a most recently used subset and dynamically
filter as the user is typing.
If the user does not see the entry they want, we want a mechanism whereby they can reqest more data.
I've tried a few options none of which i cant get working
1. Catch the scroll event and if the scrollbar is at the bottom more data will be requested similar to when you search for images in google and start scrolling down.
this doesnt work because there doesnt seem to be any event to figure this out and SPY++ shows that the WM_VSCROLL event isnt being sent.. Adding a WndProc handler shows the same.
2.Add a fake summary row with a hard coded "Click for more" text and catch the mouse click event.
Again there is no event handler and SPY++ shows no mouse click events are being sent
3. Add a fake row at the end with hard coded "Click for more"
Still no mouse click events so the only (hacky) way I can find is to catch the value changed event and
see if the text is "Click for more" but after that the drop down closes. calling Toggle drop down in that event handler is too early and it closes afterwards.
Also for this solution I would need to merge the multiple columns in this row somehow or do a custom paint over it.
I hope you get what I am tryong to acheive, any nice solution will do.
thanks
Michael
Hi Michael,
Your idea of using a fake summary row (or data row) is not bad. But as you noticed, you cannot catch mouse events on the dropdown portion of the combo - at least not easily.
I found a way to do this using a CreationFilter to insert a custom button UIElement into the dropdown.
So I add a fake summary to the combo:
private void ultraCombo1_InitializeLayout(object sender, InitializeLayoutEventArgs e) { UltraGridLayout layout = e.Layout; UltraGridBand band = layout.Bands[0]; UltraGridOverride ov = layout.Override; band.Summaries.Add("Fake", null); ov.SummaryFooterCaptionVisible = DefaultableBoolean.False; }
And I create a class which inherits from ButtonUIElement and override the OnClick method.
internal class ComboLoadMoreDataButtonUIElement : ButtonUIElement { public ComboLoadMoreDataButtonUIElement(UIElement parent) : base(parent) { } protected override void OnClick() { base.OnClick(); } }
Then I create a CreationFilter class that watches for the SummaryFooterUIElement and replaces the entire contents of this element with my custom button element.
internal class ComboLoadMoreDataCreationFilter : IUIElementCreationFilter { #region IUIElementCreationFilter Members void IUIElementCreationFilter.AfterCreateChildElements(UIElement parent) { // do nothing } bool IUIElementCreationFilter.BeforeCreateChildElements(UIElement parent) { SummaryFooterUIElement summaryFooterUIElement = parent as SummaryFooterUIElement; if (summaryFooterUIElement != null) { ComboLoadMoreDataButtonUIElement comboLoadMoreDataButtonUIElement = null; // See if there is an existing element we can re-use. if (parent.ChildElements.Count > 0) { foreach (UIElement childElement in parent.ChildElements) { if (childElement is ComboLoadMoreDataButtonUIElement) { comboLoadMoreDataButtonUIElement = (ComboLoadMoreDataButtonUIElement)childElement; break; } } } parent.ChildElements.Clear(); if (comboLoadMoreDataButtonUIElement == null) comboLoadMoreDataButtonUIElement = new ComboLoadMoreDataButtonUIElement(parent); comboLoadMoreDataButtonUIElement.Rect = parent.Rect; parent.ChildElements.Add(comboLoadMoreDataButtonUIElement); return true; } return false; } #endregion }
And you just have to attach the CreationFilter to the combo up front. I'd do this in Form_Load.
this.ultraCombo1.CreationFilter = new ComboLoadMoreDataCreationFilter();
Hi Mike
That worked a treat:
Few points though.
I need a mechanism to communicate bewteen this UIElement and the UltraCombo for things like,
At the moment I can do all of the above by tweaking the constructors of the CreationFilter and uielement to pass in the UltraCombo - it just feels a little bit of a hack.
Also the fake button doesnt actually behave like a button, i.e. I don't see it looking pressed when I click on it. I don't need that functionality (in fact i may make mine look like a hyperlink), but is more for completeness for anyone else reading this.
I have used your suggestion for a CreationFilter and like Michael found it works well for us too.
The only issue i have now is i scroll through the combo and when i hit the bottom the BeforeCreateChildElements gets invoked where the parent is a SummaryFooterEleement. this in turn performs the adding of our "get more records" button. Once added it invokes the overriden InitAppearance which sets the text on our button.
My problem is that as I am scrolling the rendering does not happen until i let go of the mouse. So the button has no text and therefore users do not know that the button is present unless they let go of the mouse button. Our users don't have that kind of patience :)
Can you help with how the button can be rendered while scrolling, perhaps invoking a different event?
Hi,
It's been a while, so I don't remember everything that you are doing here, exactly. But it sounds like you need to force the CreationFilter to be called at a certain point.
If that is the case, then you need to call DirtyChildElements and/or VerifyChildElements on the element. You have to be very careful about this. You don't want to call either of these methods from within your CreationFilter because that would cause an infinite loop. But perhaps you can use the Before/AfterRowRegionScroll event to detect when you are at the bottom and dirty your button element.
I found a solution of doing a repaint with DirtyChildElements when I'm setting the text but only if the text changed in Initappearance - I see what you mean by setting in there, without the check, CPU quickly pops up to 50%
It works but I think its still a bit of a waste doing the check each time (even though CPU happily sits s 0%) and would like to do it how you suggested but there is no scroll event.
Sorry, I forgot we were talking about UltraCombo and not UltraGrid here. There are no events for scrolling the combo, unfortunately.
HI
Still on this with some tweaks needed:
1. When there are no records the drop down area of the parent combo is too big so I get grey a rectangle:
I have access to the parent combo in the initappearance, but in any case I don't see a property to allow be to shrink the combo's height.
The Height property only sets the height os the dit portion as opposed to the drop down portion and there is nothing I can find on DisplayLayout.Bands[0].
2. When there are records I would like to change the colour of the white line above the UIelement
so its the same as the grid separators.
Again I have access to the parent combo (as I guess its the bottom border of the drop down), I tried this
parentCombo.Appearance.BorderColor =
Color.Green;
but it seems to create a green rectanlge that doesnt quite fit and also only paints when the mouse is over it.
This thread is getting kinda long, so I don't remember all of it, but if you are using a CreationFilter here, my guess is that you are not clearing out the ChildElements collection of whatever element is containing those rows.
Normally, the grid will do this for you, but if you are handling BeforeCreatChildElement and you return true, then the grid will not do any of it's own normal processing, so you are responsible for everything, including removing any elements that are no longer needed.
Another problem with this now:
We tried making the summary row a nice button by overriding ButtonStyle on the ButtonUIElement
// only show a button if the parent has some summary text:
public
{
get
parentCombo.DisplayLayout.Bands[0].Summaries.Clear();
parentCombo.DisplayLayout.Bands[0].Summaries.Add(
}
The buttons shows just fine but we now get a redraw problem:
As you may remeber we are rebinding a data source from a webservice on each key stroke. When the filtered data gets smaller, the old data is still visible while we are typing.
e.g. here shows there was lots of data, then we hit "o" and the filtered list drops to just 2 items but the old data still shows at the bottom.
All combinations of Invalidate, DoEvents don't help. The also the GUi isnt locked, we can still type and clicking on say the (real) scrollbar causes the old data to dissapear.
If I completely get rid of the above override and don't have a button then there is no problem and the old data dissapears.
1. Did you try PreferredDropDownSize, then? If you don't have that property or it does not work, then I don't beleive there is any way to set the size of the dropdown.
2. What UIElement are you using to add the text? If the border is showing up only when the mouse is over it, then it seems to me that you are probably using a ButtonUIElement or some element derived from it and the border you are getting is the border of the button. So your button element must be using a style that pops up the button on a mouse over.
If that's right, then the ButtonUIElement should pick up the style from the control's ButtonStyle or ButtonDisplayStyle property. If not, then you can still change the style by deriving your own class from ButtonUIElement and overriding the ButtonStyle property.
Perhaps there is another button style that shows a single border all the time that you can use.
It's also possible that the white line you are seeing here is not coming from the ButtonUIElement, but from whatever element is above it. But there's no way to tell that just from a screen shot.
1. MaxDropDownItem set to 1 still leaves a gray box.
2. I just want the white line in the picture above coloured. Just realised I had a typo (+ intellisense) it was
supposed to be appearance.BorderColor = green,
here is a screen shot of how it looks (when the mouse is over it)
Hope you can see green lines (just one pixel) just inside the white borders. In any case this was a first try, I had thoughts of showing/hiding top/left/right borders.
Looks like DrawFilter is the way, can you give me a pointer as to how to isolate the bottom grid using this method
1. In older versions, there was no way to apply an explicit size to the dropdown area of the combo. In the latest version, you can do it using the PreferredDropDownSize property. But I'm not sure if this property is honored unless you also turn on the DropDownResizeHandleStyle.
Another option would be to use the MaxDropDownItems. You cannot set an explicit size with this property, but you could try setting the property to 1 so there's only room for one dropdown item and that might work for what you need.
2. I'm not sure what you mean by "a green rectanlge that doesnt quite fit". If I understand correctly, you want a border around the "Click here for more..." area, right? There is already a white border around it, so what exactly doesn't fit? Are you saying the green border is not replacing the white border that is already there?
Setting combo.Appearance.BackColor doesn't seem like the right thing to do, anyway, since this will affect all of the borders in the entire control, not just the one around the text in the dropdown.
I think you will probably need to use a DrawFilter or derive your own UIElement hand override InitAppearance if you want to control the appearances on such a detailed level.