The first step, an example of UI

Design pattern are very important, we will discuss MVC and MVVM a lot, but, as far as the UI is concerned, we can focus on the code behind only.

Our first step is a demo with a ComboBox.
In xaml

<ComboBox
        RequestBringIntoView="ComboBox_RequestBringIntoView"

In code behind


    private bool IsUserVisible(FrameworkElement element, FrameworkElement container)
    {
        if (!element.IsVisible)
            return false;

        Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
        Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
        Point TopLeft = bounds.TopLeft;
        TopLeft.Offset(0, 1E-3);
        bool ret = rect.Contains(TopLeft); 
        return ret;
    }
    private static Point? initial_check = null;
    private Rect bounds;
    private void ComboBox_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
    {
        if (Mouse.LeftButton == MouseButtonState.Pressed)
        {
            return;
        }
        if (Keyboard.IsKeyDown(Key.Down) || Keyboard.IsKeyDown(Key.Up))
        {
            return;
        }
        ComboBox combo = (ComboBox)sender;
        if (combo.IsDropDownOpen)
        {
            var MousePoint = Mouse.GetPosition((ScrollViewer)e.TargetObject);
            if (initial_check != null)
            {
                if (initial_check.Value.Equals(MousePoint))
                {
                    return;
                }
            }
            initial_check = MousePoint;
            int first_el = 0;
            int last_el = 0;
            bool first_found = false;
            for (int i = 0; i < combo.Items.Count; i++)
            {
                if (IsUserVisible((FrameworkElement)(ComboBoxItem)
                                  combo.ItemContainerGenerator.ContainerFromIndex(i),
                                  (FrameworkElement)e.TargetObject))
                {
                    if (!first_found)
                    {
                        first_found = true;
                        first_el = i;
                    }
                    last_el = i;
                }
            }
            ComboBoxItem it = (ComboBoxItem)combo.ItemContainerGenerator.ContainerFromIndex(first_el); ;
            ComboBoxItem last_it = (ComboBoxItem)combo.ItemContainerGenerator.ContainerFromIndex(last_el); ;
            bounds = last_it.TransformToVisual((ScrollViewer)e.TargetObject).TransformBounds(new Rect(0.0, 0.0, last_it.ActualWidth, last_it.ActualHeight));
            if (bounds.Contains(MousePoint))
            {
                it.BringIntoView();
                Debug.WriteLine("ToView " + it.Content
                    + ";" + e.OriginalSource.GetType());
            }

        }
    }

Why is it interesting? Good question, it shows an advanced trick to teach you a couple of things:

  1. This solution doesn’t need to manage IsKeyDown also for any other starting chars of checkbox items
  2. The check on the initial mouse position avoids a continuous loop of requests
  3. This is an original and unedited document that explains how to get access to the ScrollViewer inside the ComboBox in WPF with a very elegant code
  4. There is a clever idea to deal with the rounding in the top left point of the first item and to prevent the final ComboBox from scrolling upwards when starting with the first 3-4 items

See you soon

Please note

Let’s compare to two ways of implementing the Combo in a DataGrid.

First, a sophisticated trick to make the cell part of the visual tree.

<!—now itemssource will find the correct DataContext-->
<dg:DataGridComboBoxColumn Header="Current Product" SelectedValueBinding="{Binding Path=CurrentProduct}" SelectedValuePath="ProductID" DisplayMemberPath="ProductName">              
  <dg:DataGridComboBoxColumn.ElementStyle>


<Style TargetType="ComboBox">
      <Setter Property="ItemsSource" Value="{Binding Path=ProductsInCategory}" />
    </Style>


  </dg:DataGridComboBoxColumn.ElementStyle>
  <dg:DataGridComboBoxColumn.EditingElementStyle>


<Style TargetType="ComboBox">
      <Setter Property="ItemsSource" Value="{Binding Path=ProductsInCategory}" />
    </Style>


  </dg:DataGridComboBoxColumn.EditingElementStyle>
</dg:DataGridComboBoxColumn>

On the opposite hand, an easier (more standard, but static!) approach.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s