Skip to main content

Microsoft Silverlight

[RTW] ComboBox ScaleTransform, Sizing bugsRSS Feed

(0)

simon.ferquel
simon.fe...

Member

Member

212 points

61 Posts

[RTW] ComboBox ScaleTransform, Sizing bugs

Hi,

When you put a scale transform on a ComboBox (or on an element containing a ComboBox), its popup sizing logic does not work well : If its itemsource contains too much elements, its maximum height does not take care of the ScaleTransform currently applied, and some data get out of the Window.

See the sample below to have a repro :

 

<UserControl x:Class="BugComboboxSizing.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    >
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.5*" />
        </Grid.ColumnDefinitions>
        <Border HorizontalAlignment="Center"
                  VerticalAlignment="Center">
            <ComboBox >
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
            </ComboBox>
        </Border>
        <Border HorizontalAlignment="Center"
                  VerticalAlignment="Center" Grid.Column="1">
          
            <ComboBox >
                <ComboBox.RenderTransform>
                    <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2" ScaleY="2" />
                </ComboBox.RenderTransform>
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
            </ComboBox>
        </Border>
    </Grid>
</UserControl>
 

 

After checks with reflector, the problem seems to be in in the method: ComboBox::ArrangePopup() which get the offsetX and offsetY of the general transform, but not ScaleX and ScaleY.

I have authored a SuggestBox control (that has the same kind of logic for sizing the suggestions area). Here is it's sizing code (that works well):

 

var gt = _popup.TransformToVisual(page) as MatrixTransform;
double yOffset = 0.0;
double yScale = 1.0;
if (gt != null)
{
    yOffset = gt.Matrix.OffsetY;
    yScale = gt.Matrix.M22;
}

var pageHeight = page.ActualHeight;
var availableHeight = pageHeight - yOffset;
var scaledMaxHeight = availableHeight / yScale;

 One other thing that does not make good behavior is the following actions on the previous sample :

  • Resize the window to have a small client area height
  • Click the left Combobox to see its items and force a sizing computation
  • Resize the window to go full screen
  • Click the left combobox : its items popup keep the last computed size.

This behavior is kind of weird and can be reproduced in a more blocking way in a business environment

  • Bind the ItemsSource of a ComboBox to an observable collection containing only one item
  • Let the user open the ComboBox
  • Add new items with longer DisplayText than the first one to the observable collection
  • Then, the user open the combobox, and he cannot see any item : the combobox keep the last computed size, and put some ScrollBars that have the size of one item, and hiding the content.

Sorry to not have detect these bugs before RTW, I just figured out of them last night.

Simon Ferquel

Simon Ferquel
MVP Client App Dev
http://www.simonferquel.net/blog

simon.ferquel
simon.fe...

Member

Member

212 points

61 Posts

Re: [RTW] ComboBox ScaleTransform, Sizing bugs

For those who are facing these bugs, I have a quick workaround not deeply tested but that seems to work efficiently :

public class FixedComboBox : ComboBox

{

Popup _popup;

FrameworkElement _elementPopupChild;

public override void OnApplyTemplate()

{

_popup = GetTemplateChild("Popup") as Popup;

_elementPopupChild = _popup.Child as FrameworkElement;

base.OnApplyTemplate();

}

protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)

{

base.OnItemsChanged(e);

if (_elementPopupChild != null)

{

_elementPopupChild.ClearValue(FrameworkElement.MinWidthProperty);

_elementPopupChild.ClearValue(FrameworkElement.MinHeightProperty);

_elementPopupChild.ClearValue(FrameworkElement.MaxWidthProperty);

_elementPopupChild.ClearValue(FrameworkElement.MaxHeightProperty);

_elementPopupChild.ClearValue(FrameworkElement.WidthProperty);

_elementPopupChild.ClearValue(FrameworkElement.HeightProperty);

 

this.InvalidateArrange();

}

}

protected override void OnDropDownOpened(EventArgs e)

{

base.OnDropDownOpened(e);

if (_elementPopupChild == null)

return;

FrameworkElement page = Application.Current.RootVisual as FrameworkElement;

var gt = _popup.TransformToVisual(page) as MatrixTransform;

double yOffset = 0.0;

double yScale = 1.0;

if (gt != null)

{

yOffset = gt.Matrix.OffsetY;

yScale = gt.Matrix.M22;

}

var pageHeight = page.ActualHeight;

var availableHeight = pageHeight - yOffset;

var scaledMaxHeight = availableHeight / yScale - this.ActualHeight;

if(_elementPopupChild.MaxHeight != double.NaN && _elementPopupChild.MaxHeight != double.PositiveInfinity)

scaledMaxHeight = Math.Min(scaledMaxHeight, _elementPopupChild.MaxHeight);

_elementPopupChild.MaxHeight = scaledMaxHeight > 0 ? scaledMaxHeight : 0;

}

 

 

}

 

Simon Ferquel
MVP Client App Dev
http://www.simonferquel.net/blog

mikealex
mikealex

Member

Member

3 points

10 Posts

Re: [RTW] ComboBox ScaleTransform, Sizing bugs

There is another Scale-related bug in that the mouse position on the dropdown's scrollbar is scaled incorrectly, meaning that the relative position of the mouse pointer and scrollbar moves as you drag it.

Try the following:

<UserControl x:Class="TestBed.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="300" Height="400">

    <UserControl.RenderTransform>
        <ScaleTransform ScaleX="2" ScaleY="2"></ScaleTransform>
    </UserControl.RenderTransform>
   
    <Canvas Width="300" Height="400" x:Name="LayoutRoot">
       
        <ComboBox Height="20" Width="100" MaxDropDownHeight="200">
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
            <ComboBoxItem Content="Text" />
        </ComboBox>
    </Canvas>
</UserControl>

Click the down-arrow to cause the dropdown to display. Note that if you grab the scrollbar "thumb" near the top of the thumb, and drag it down, the thumb moves at twice the speed of the mouse, meaning that the mouse cursor eventually moves off the thumb. At higher scale factors this can be a real problem, as the thumb flies along at "Scale x" the actual mouse speed.

The suggested fixed Combox Box doesn't make a difference to this behaviour.

 

Best Regards,

Mike

 

  • Unanswered Question
  • Answered Question
  • Announcement
Microsoft Communities