2011-08-10 8 views
0

this articleに基づくDataGridを持つComboBoxがあります。 これで値をフィルタリングすることはできません。そこでthis oneを実装しました。フィルタリングされたコレクションでは、DataGridでマウスを選択することはできません

フィルタリングがうまく機能していて、キーボードで上または下の候補を選択することができます。しかし、マウスで選択することはできません。

なぜこれはできないのですか?どうすれば修正できますか?

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Diagnostics; 
using System.Reflection; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Controls.Primitives; 
using System.Windows.Data; 
using System.Windows.Input; 
using System.Windows.Markup; 
using System.Windows.Media; 

namespace CustomControls { 
[DefaultProperty("Columns")] 
[ContentProperty("Columns")] 
[TemplatePart(Name = s_partPopupDataGrid, Type = typeof(DataGrid))] 
public class GridCombo : ComboBox { 
    #region Static 

    internal static readonly DependencyProperty ReplaceColumnsProperty = 
     DependencyProperty.Register(
      "ReplaceColumns", 
      typeof(IEnumerable<DataGridBoundColumn>), 
      typeof(GridCombo), 
      new FrameworkPropertyMetadata()); 

    public static readonly DependencyProperty CellStyleProperty = 
     DependencyProperty.Register(
      "CellStyle", 
      typeof(Style), 
      typeof(GridCombo), 
      new FrameworkPropertyMetadata()); 

    public static readonly DependencyProperty MinimumSearchLengthProperty = 
     DependencyProperty.Register(
      "MinimumSearchLength", 
      typeof(int), 
      typeof(GridCombo), 
      new UIPropertyMetadata(1)); 

    static GridCombo() { 
     DefaultStyleKeyProperty.OverrideMetadata(
      typeof(GridCombo), new FrameworkPropertyMetadata(typeof(GridCombo))); 
    } 

    #endregion 

    // ====================================================================== 
    #region Fields & Constructors 

    private const string s_partPopupDataGrid = "PART_PopupDataGrid"; 

    // Columns of DataGrid 
    private ObservableCollection<DataGridBoundColumn> _columns; 

    private readonly Dictionary<Type, List<PropertyInfo>> _properties = new Dictionary<Type, List<PropertyInfo>>(); 

    // Attached DataGrid control 
    private DataGrid _popupDataGrid; 
    private Popup _popup; 

    private string _oldFilter = string.Empty; 
    private string _currentFilter = string.Empty; 

    #endregion 

    // ====================================================================== 
    #region Public 

    public Style CellStyle { 
     get { return (Style)GetValue(CellStyleProperty); } 
     set { SetValue(CellStyleProperty, value); } 
    } 

    /// <summary> 
    /// If set, the "Columns" property is ignored. Useful if you need 
    /// a dependency property. 
    /// </summary> 
    internal IEnumerable<DataGridBoundColumn> ReplaceColumns { 
     get { return (ObservableCollection<DataGridBoundColumn>)GetValue(ReplaceColumnsProperty); } 
     set { SetValue(ReplaceColumnsProperty, value); } 
    } 

    // The property is default and Content property for CustComboBox 
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
    public ObservableCollection<DataGridBoundColumn> Columns { 
     get { 
      if (_columns == null) { 
       _columns = new ObservableCollection<DataGridBoundColumn>(); 
      } 
      return _columns; 
     } 
    } 

    // Apply theme and attach columns to DataGrid popup control 
    public override void OnApplyTemplate() { 
     if (_popupDataGrid == null) { 
      _popupDataGrid = Template.FindName(s_partPopupDataGrid, this) as DataGrid; 
      if (_popupDataGrid != null && (_columns != null || ReplaceColumns != null)) { 
       if (ReplaceColumns != null) { 
        foreach (var column in ReplaceColumns) { 
         var copy = DataGridFix.CopyDataGridColumn(column); 
         _popupDataGrid.Columns.Add(copy); 
        } 
       } else { 
        // Add columns to DataGrid columns 
        for (int i = 0; i < _columns.Count; i++) 
         _popupDataGrid.Columns.Add(_columns[i]); 
       } 

       // Add event handler for DataGrid popup 
       _popupDataGrid.MouseDown += PopupDataGridMouseDown; 
       _popupDataGrid.SelectionChanged += PopupDataGridSelectionChanged; 
      } 
     } 
     if (_popup == null) { 
      _popup = Template.FindName("PART_Popup", this) as Popup; 
      if (_popup != null && _popupDataGrid != null) { 
       _popup.Opened += PopupOpened; 
       _popup.Focusable = true; 
      } 
     } 

     // Call base class method 
     base.OnApplyTemplate(); 
    } 

    [Description("Length of the search string that triggers filtering.")] 
    [Category("Filtered ComboBox")] 
    [DefaultValue(1)] 
    public int MinimumSearchLength { 
     [DebuggerStepThrough] 
     get { return (int)GetValue(MinimumSearchLengthProperty); } 
     [DebuggerStepThrough] 
     set { SetValue(MinimumSearchLengthProperty, value); } 
    } 

    #endregion 

    // ====================================================================== 
    #region Protected 

    // When selection changed in combobox (pressing arrow key down or up) must be synchronized with opened DataGrid popup 
    protected override void OnSelectionChanged(SelectionChangedEventArgs e) { 
     base.OnSelectionChanged(e); 
     if (_popupDataGrid == null) 
      return; 

     if (!DesignerProperties.GetIsInDesignMode(this)) { 
      if (IsDropDownOpen) { 
       _popupDataGrid.SelectedItem = SelectedItem; 
       ScrollIntoView(SelectedItem); 
      } 
     } 
    } 

    protected override void OnDropDownOpened(EventArgs e) { 
     if (_popupDataGrid == null) 
      return; 

     _popupDataGrid.SelectedItem = SelectedItem; 

     base.OnDropDownOpened(e); 
    } 

    protected TextBox EditableTextBox { 
     get { return Template.FindName("PART_EditableTextBox", this) as TextBox; } 
    } 

    protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { 
     if (!IsEditable) { 
      base.OnPreviewLostKeyboardFocus(e); 
      return; 
     } 

     ClearFilter(); 
     int temp = SelectedIndex; 
     SelectedIndex = -1; 
     Text = string.Empty; 
     SelectedIndex = temp; 
     base.OnPreviewLostKeyboardFocus(e); 
    } 

    protected override void OnKeyUp(KeyEventArgs e) { 
     if (!IsEditable) { 
      base.OnKeyUp(e); 
      return; 
     } 

     if (e.Key == Key.Up || e.Key == Key.Down) { 
      // Navigation keys are ignored 
     } else if (e.Key == Key.Tab || e.Key == Key.Enter) { 
      // Explicit Select -> Clear Filter 
      ClearFilter(); 
     } else { 
      // The text was changed 
      if (Text != _oldFilter) { 
       // Clear the filter if the text is empty, 
       // apply the filter if the text is long enough 
       if (Text.Length == 0 || Text.Length >= MinimumSearchLength) { 
        RefreshFilter(); 
        IsDropDownOpen = true; 

        // Unselect 
        EditableTextBox.SelectionStart = int.MaxValue; 
       } 
      } 

      base.OnKeyUp(e); 

      _currentFilter = Text; 
     } 
    } 

    protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { 
     if (!IsEditable) { 
      base.OnItemsSourceChanged(oldValue, newValue); 
      return; 
     } 

     if (newValue != null) { 
      var view = CollectionViewSource.GetDefaultView(newValue); 
      view.Filter += FilterPredicate; 
     } 
     if (oldValue != null) { 
      var view = CollectionViewSource.GetDefaultView(oldValue); 
      view.Filter -= FilterPredicate; 
     } 
     base.OnItemsSourceChanged(oldValue, newValue); 
    } 

    protected override void OnPreviewKeyDown(KeyEventArgs e) { 
     if (!IsEditable) { 
      base.OnPreviewKeyDown(e); 
      return; 
     } 

     if (e.Key == Key.Tab || e.Key == Key.Enter) { 
      // Explicit Selection -> Close ItemsPanel 
      IsDropDownOpen = false; 
     } else if (e.Key == Key.Escape) { 
      // Escape -> Close DropDown and redisplay Filter 
      IsDropDownOpen = false; 
      SelectedIndex = -1; 
      Text = _currentFilter; 
     } else { 
      if (e.Key == Key.Down) { 
       // Arrow Down -> Open DropDown 
       IsDropDownOpen = true; 
      } 
      base.OnPreviewKeyDown(e); 
     } 

     _oldFilter = Text; 
    } 

    #endregion 

    // ====================================================================== 
    #region Private 

    private void RefreshFilter() { 
     if (ItemsSource != null) { 
      var view = CollectionViewSource.GetDefaultView(ItemsSource); 
      view.Refresh(); 
     } 
    } 

    private void ClearFilter() { 
     _currentFilter = string.Empty; 
     RefreshFilter(); 
    } 

    private bool FilterPredicate(object value) { 
     if (value == null) { 
      return false; 
     } 

     if (Text.Length == 0) { 
      return true; 
     } 

     var properties = GetProperties(value.GetType()); 
     foreach (var property in properties) { 
      var propertyValue = (property.GetValue(value, null) ?? string.Empty).ToString(); 
      if (propertyValue.ToLowerInvariant().Contains(Text.ToLowerInvariant())) { 
       return true; 
      } 
     } 

     return false; 
    } 

    private IEnumerable<PropertyInfo> GetProperties(Type type) { 
     if (!_properties.ContainsKey(type)) { 
      _properties.Add(type, new List<PropertyInfo>()); 

      foreach (var column in _columns) { 
       if (column.Binding != null && column.Binding is Binding) { 
        var path = ((Binding)column.Binding).Path.Path; 
        var property = type.GetProperty(path); 
        if (property != null) { 
         _properties[type].Add(property); 
        } 
       } 
      } 
     } 

     return _properties[type]; 
    } 

    private void PopupOpened(object sender, EventArgs e) { 
     ScrollIntoView(SelectedItem); 
    } 

    private void ScrollIntoView(object item) { 
     if (item != null && _popupDataGrid.Items.Contains(item)) 
      _popupDataGrid.ScrollIntoView(item); 
    } 

    // Synchronize selection between Combo and DataGrid popup 
    private void PopupDataGridSelectionChanged(object sender, SelectionChangedEventArgs e) { 
     // When open in Blend prevent raising exception 
     if (!DesignerProperties.GetIsInDesignMode(this)) { 
      var grid = sender as DataGrid; 
      if (grid != null && grid.IsVisible) { 
       SelectedItem = grid.SelectedItem; 
      } 
     } 
    } 

    // Event for DataGrid popup MouseDown 
    private void PopupDataGridMouseDown(object sender, MouseButtonEventArgs e) { 
     DataGrid dg = sender as DataGrid; 
     if (dg != null) { 
      var dep = (DependencyObject)e.OriginalSource; 

      // iteratively traverse the visual tree and stop when dep is one of .. 
      while ((dep != null) && 
        !(dep is DataGridCell) && 
        !(dep is DataGridColumnHeader)) { 
       dep = VisualTreeHelper.GetParent(dep); 
      } 

      if (dep == null) 
       return; 

      if (dep is DataGridColumnHeader) { 
       // do something 
      } 

      // When user clicks to DataGrid cell, popup have to be closed 
      if (dep is DataGridCell) { 
       IsDropDownOpen = false; 
      } 
     } 
    } 

    #endregion 
} 
} 

次のXAMLはそれをテストするために使用することができます:

<Window x:Class="GridComboTestView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:combo="clr-namespace:CustomControls;assembly=CustomControls" 
    Height="300" Width="300"> 
<StackPanel> 
    <Button Content="ChangeElements" Command="{Binding ChangeElements}" /> 
    <combo:GridCombo 
     x:Name="GridCombo" 
     ItemsSource="{Binding Elements}" 
     DisplayMemberPath="Number" 
     IsEditable="True" 
     SelectedItem="{Binding SelectedElement}" 
     MaxDropDownHeight="100"> 
     <DataGridTextColumn Binding="{Binding Number, Mode=OneWay}" /> 
     <DataGridTextColumn Binding="{Binding Name, Mode=OneWay}" /> 
    </combo:GridCombo> 
</StackPanel> 
</Window> 

答えて

0

あなたはPopupDataGridMouseDownのベースを()を呼び出していません。ここ

は、私たちのコンボボックスのコードです。これはそれを修正するが、見て何かを確認していない。

+0

PopupDataGridMouseDownは、イベントハンドラであり、オーバーライドされたメソッドではありません。申し訳ありません。 – MichaelS

+0

@MichaelSはい私はそれがイベントハンドラであることを認識しています。 base.OnMouseDown(e)を追加しようとしましたか?そのイベントハンドラの最後の行は何ですか? – Paparazzi

+0

はい、試しましたが、動作しません。 – MichaelS

関連する問題