Hi all,
I'm currently implementing the Live Preview feature found in Office 2007 - first off for a FontSize selection (for text in a RichTextBox).
I've found a couple of solutions and welcome opinion on which is better.... or is there a better way still?
I've started by creating a FontSizeCombo object which inherits from ComboEditorTool (it will live in the Ribbon). I've also tied it to a default template that I've copied from the base Infragistics one.
Solution 1
I set the ItemsSource to an array of strings, one for each font size that should appear in the drop-down.
I then found the "PART_FocusSite" ComboBox in the main template and set the ItemContainerStyle to a ComboBoxItem template I created as a seperate resource. The default template for a ComboBoxItem comprises a trigger for "IsHighlighted" that changes the background colour of the item the mouse is over. I want to effectively know in code when this happens, so I can fire a command that performs the changing of the text size.
What I did was register an attached property "LivePreviewValue" within my class, with a PropertyChangedCallback that fires the command to do the work. In my ComboBoxItem template I added a second setter to the aforementioned trigger:
<Setter Property="local:FontSizeCombo.LivePreviewValue" Value="{Binding}" />
This works great and has the desired effect, when the item is highlighted, it sets that shared property to the string of the current item.... but this just doesn't feel quite right - I think because I'm now tying the template to a specific property of a specific class.
Solution 2
Once I discovered you could add items to a ComboEditorTool one at a time, by creating a new instance of ComboBoxItemsProvider, I created each item manually. I iterate my array of strings, one at a time and create a TextBlock to represent each item. As I am doing this in code, I can add handlers for each TextBlocks MouseEnter event. In this event, I can detect which item has been 'moused over' and fire the command to change the font size accordingly.
I still needed to override the default template, to get rid of the default padding on the Border surrounding each item and to set the ContentPresenters HorizontalAliignment to Stretch (so the TextBlock occupies all the space and the event gets fired correctly).
This prevents the need for explicitly setting LivePreviewValue in the template, so I think it's my preffered solution so far!
(Possible) Solution 3?
I did try a third solution - to handle the MouseMove event of the ComboEditorTool. I was pleasantly surprised that this event gets raised when 'mousing over' the drop-down portion of the control - I thought it may not even raise the event for the popup section.
That is as far as I got though... I stumbled at the point of trying to return the element under the mouse pointer, within the drop-down portion of the control. I was thinking of using the VisualTreeHelper.HitTest method to return the element - but this method requires a UIElement on which to perform the hit test - I couldn't work out how to get to the root visual of the Popup...
...Is it possible to get to the visual element of the combo's popup? would this be a better solution?
Conclusion
As stated, I've got this working (so may be helpful for anyone else trying to implement this functionality), but I'd be interested to hear any ideas on what you think is the best method of achieving this.... and is it possible to get my third solution working?
Dave
Hi Andrew,
Thanks for the tips, I'd overlooked a couple of good points you raised.
We need to leave the combo editable, so the user can manually enter a font size not in the list, so couldn't go with your last suggestion. I've gone with solution 1, with the style now in the resource, as you recommend. The style sets the LivePreviewValue property when the combo item is highlighted, which in turn fires the command on the RichTextBox to update the selection. The control also sets this value on LostFocus, so the preview doesn't constantly update when the user is manually entering a value, just when they've finished.
I've now got a font size picker on my ribbon, that will target any RichTextBox in our system which has focus, that's 99% the same the one in Word 2007 :-)
With regards to solution 1 & 2, you don't necessarily have to walk down into the template. Actually you probably shouldn't as the template could change after that point (e.g. if the end user changes the OS theme). Instead you could just put a Style that targets ComboBoxItem into the ComboEditorTool's Resources.
With regards to solution 3, the e.OriginalSource would be the element nested within that was under the mouse so you don't have to walk down the tree from the comboeditortool; instead you would walk up the logical/visual tree of the e.OriginalSource. The downside though to using the mouse events (and this applies to solution 2 as well) is that it doesn't cover the scenario where the user used the keyboard to navigate to an item in the dropdown.
If you are leaving IsEditable set to false, there may be another option. As an item is highlighted in the combobox, the comboboxitem will get the keyboard focus (when IsEditable is false only). So you could listen for the GotFocus/LostFocus events on the xamComboEditor and evaluate the element receiving focus and if it is (or is within) a comboboxitem, then treat it as the activated/live item.