DataGrid CellEditingTemplate with Popup losing focus

Nov 23, 2009 at 9:23 PM

Hello,

I'm trying to make a UserControl to be used in the CellEditingTemplate. Its kinda like the DatePicker.

I created the UserControl and added a popup with a textbox into it. The problem I run into is when I click on the textbox in the popup the cell loses focus and it thinks that I'm done editing the cell. If I add the popup into the CellEditingTemplate directly the textbox can receive focus with out a problem and I can type like normal.

Does the UserControl do something funky with the focus that causes the DataGridCell to think it lost focus ?

Heres my code

<UserControl
   x:Class="HR.Controls.UserPicker"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid>
        <TextBlock x:Name="PART_TextBox" Text="Hello WOrld" />
        <Popup Width="234" Height="175" IsOpen="True" StaysOpen="True"
         
             Placement="Bottom"
             PlacementTarget="{Binding ElementName=PART_TextBox}"
         >
             <TextBox
                  x:Name="searchTextBox"
                  Text="&gt;Enter Name&lt;"/>
        </Popup>
    </Grid>
</UserControl>

Any tips would be greatly appreciated.

Thanks,
Raul

Dec 1, 2009 at 6:10 PM

Bump

Feb 4, 2010 at 11:50 PM

I'm also experiencing this issue. Anyone have any update?

Interestingly, the DatePicker included in the WPF Toolkit does not have this problem. It's a Control which has a ToggleButton which opens a Popup and appears to work perfectly fine inside the DataGrid. I've looked through the code to see if there's something obvious that it's doing but haven't found anything yet.

Feb 5, 2010 at 5:13 PM

I was able to make a custom Control show a popup correctly from within the DataGrid. For example, the following UserControl does not allow clicking inside the Popup when displayed from a DataGrid Cell

<UserControl x:Class="DataGridTest.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Focusable="False"
    KeyboardNavigation.TabNavigation="Once"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid>
    <Button x:Name="_TheButton" Content="{Binding Name}" Focusable="False" Click="_TheButton_Click"/>
    <Popup x:Name="_Popup">
      <ListBox x:Name="_ListBox">
        <CheckBox Content="Test 1"/>
        <CheckBox Content="Test 2"/>
        <CheckBox Content="Test 3"/>
      </ListBox>
    </Popup>
  </Grid>
</UserControl>

using System;
using System.Windows;
using System.Windows.Controls;

namespace DataGridTest
{
  public partial class UserControl1 : UserControl
  {
    public UserControl1()
    {
      InitializeComponent();
    }
    private void _TheButton_Click(object sender, RoutedEventArgs e)
    {
      _Popup.IsOpen = true;
    }
  }
}

If I implement similar functionality in a custom Control, the Popup is displayed and I can click on the items inside of it, just as I would like. So the question is, what's the difference between a UserControl and a custom Control when used in this manner? Is there any way to make a UserControl act like the custom Control - it's a lot less overhead to create a UserControl for these purposes.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace DataGridTest
{
  [TemplatePart(Name = CustomControl1.ElementButton, Type = typeof(Button))]
  [TemplatePart(Name = CustomControl1.ElementPopup, Type = typeof(Popup))]
  public class CustomControl1 : Control
  {
    private const string ElementButton = "PART_Button";
    private const string ElementPopup = "PART_Popup";

    private Popup _popUp;
    private ButtonBase _dropDownButton;
    private bool _disablePopupReopen;
    static CustomControl1()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
    }

    public CustomControl1()
    {
    }
    public static readonly DependencyProperty IsDropDownOpenProperty =
        DependencyProperty.Register(
        "IsDropDownOpen",
        typeof(bool),
        typeof(CustomControl1),
        new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsDropDownOpenChanged));

    private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      CustomControl1 dp = d as CustomControl1;

      bool newValue = (bool)e.NewValue;
      if (dp._popUp != null && dp._popUp.IsOpen != newValue)
      {
        dp._popUp.IsOpen = newValue;
      }
    }

    public bool IsDropDownOpen
    {
      get { return (bool)GetValue(IsDropDownOpenProperty); }
      set { SetValue(IsDropDownOpenProperty, value); }
    }


    private void PopUp_Opened(object sender, EventArgs e)
    {
      if (!this.IsDropDownOpen) this.IsDropDownOpen = true;
    }

    private void PopUp_Closed(object sender, EventArgs e)
    {
      if (this.IsDropDownOpen)
      {
        this.IsDropDownOpen = false;
      }
      this.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
    }

    private void PopUp_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      Popup popup = sender as Popup;
      if (popup != null && !popup.StaysOpen)
      {
        if (this._dropDownButton != null)
        {
          if (this._dropDownButton.InputHitTest(e.GetPosition(this._dropDownButton)) != null)
          {
            this._disablePopupReopen = true;
          }
        }
      }
    }


    private void DropDownButton_Click(object sender, RoutedEventArgs e)
    {
      TogglePopUp();
    }

    private void TogglePopUp()
    {
      if (this.IsDropDownOpen)
      {
        this.IsDropDownOpen = false;
      }
      else
      {
        if (this._disablePopupReopen)
        {
          this._disablePopupReopen = false;
        }
        else
        {
          this.IsDropDownOpen = true;
        }
      }
    }

    private void DropDownButton_MouseLeave(object sender, MouseEventArgs e)
    {
      this._disablePopupReopen = false;
    }


    public override void OnApplyTemplate()
    {
      if (_popUp != null)
      {
        _popUp.RemoveHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(PopUp_PreviewMouseLeftButtonDown));
        _popUp.Opened -= PopUp_Opened;
        _popUp.Closed -= PopUp_Closed;
        _popUp.Child = null;
      }

      if (_dropDownButton != null)
      {
        _dropDownButton.Click -= DropDownButton_Click;
        _dropDownButton.RemoveHandler(MouseLeaveEvent, new MouseEventHandler(DropDownButton_MouseLeave));
      }
      base.OnApplyTemplate();

      _popUp = GetTemplateChild(ElementPopup) as Popup;

      if (_popUp != null)
      {
        _popUp.AddHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(PopUp_PreviewMouseLeftButtonDown));
        _popUp.Opened += PopUp_Opened;
        _popUp.Closed += PopUp_Closed;
        ListBox list = new ListBox();
        for (int ix = 0; ix < 10; ix++)
        {
          list.Items.Add(new CheckBox() { Content = string.Format("test {0}", ix + 1) });
        }
        _popUp.Child = list;

        if (this.IsDropDownOpen)
        {
          this._popUp.IsOpen = true;
        }
      }

      _dropDownButton = GetTemplateChild(ElementButton) as Button;
      if (_dropDownButton != null)
      {
        _dropDownButton.Click += DropDownButton_Click;
        _dropDownButton.AddHandler(MouseLeaveEvent, new MouseEventHandler(DropDownButton_MouseLeave), true);
      }
    }
  }
}

  <Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:CustomControl1}">
          <Border 
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
            <Grid x:Name="PART_Root" HorizontalAlignment="Stretch">
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
              </Grid.ColumnDefinitions>
              <TextBlock>Click here</TextBlock>
              <Button 
                x:Name="PART_Button" 
                Grid.Column="1"
                Width="20"
                Margin="3,0,3,0" 
                Focusable="False" 
                VerticalAlignment="Stretch"
                HorizontalAlignment="Left" />
              <Popup 
                x:Name="PART_Popup" 
               PlacementTarget="{Binding ElementName=PART_Button}"
               Placement="Bottom" 
               StaysOpen="False"
               AllowsTransparency="True" />
            </Grid>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

Aug 8, 2013 at 5:33 AM
Edited Aug 8, 2013 at 6:02 AM
can someone out there, tell me a way to put a combobox inside a column of a listview or a datagrid, and not make the cell lose focus when clicking on the combobox? (apparently because of the combobox pop up it loses focus) and even without clicking on the combobox the cell loses focus if programmatically the selected value is being set.

(for my purpose actually I want the focus to remain on the ROW and not just on the cell... I have several columns with comboboxes so this is definitely a problem) .