具有添加的MRU项的WPF组合框

时间:2018-11-01 08:45:43

标签: c# wpf custom-controls

我的目标是创建一个组合框,该组合框在打开时显示任何字符串列表(因此是标准行为),但是当用户选择其中一个字符串时,它将被添加到列表顶部的“最近使用过”下方分隔符。

本质上,我想要一个行为与在MS Word中选择字体的控件完全一样的控件: example from MS Word

我的第一步是创建一个具有附加Dependency属性的自定义控件,该属性包含最近选择的项。当用户从列表中选择一个项目时,此列表将更新。我不想修改项目的原始列表,因为我的目标是获得一个可重用的控件,使用户不必自己管理最新的项目。

    private static readonly DependencyPropertyKey LastSelectedItemsPropertyKey = 
        DependencyProperty.RegisterReadOnly(
            "LastSelectedItems", 
            typeof (Dictionary<string, int>), 
            typeof (MemoryCombobox),
            new FrameworkPropertyMetadata(default(ObservableCollection<string>), FrameworkPropertyMetadataOptions.None));

    public static readonly DependencyProperty LastSelectedItemsProperty = LastSelectedItemsPropertyKey.DependencyProperty;

我现在的问题是:如何在组合框的单个下拉菜单中显示所有项目(标签和两个列表),如下所示:

---------------------
Label: Recently Selected
---------------------
<All items from the 'LastSelectedItems' DependencyProperty>
---------------------
Label: All Items
---------------------
<All items from the 'ItemsSource' property of the combobox
---------------------

我不想为此使用分组,因为这些项将不会在最近使用过的项下面的“所有项”列表中重复出现,就像用户希望的那样。

1 个答案:

答案 0 :(得分:1)

您是否尝试过以下方法。它使用分组,但是以特殊方式进行分组,因此不会从总列表/组中删除mru-items:

XAML:

<ComboBox Name="MyCombo" SelectionChanged="MyCombo_SelectionChanged" VerticalAlignment="Top">
  <ComboBox.GroupStyle>
    <GroupStyle>
      <GroupStyle.HeaderTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding Name}" Background="DarkGray" Foreground="White" FontWeight="Bold" />
        </DataTemplate>
      </GroupStyle.HeaderTemplate>
    </GroupStyle>
  </ComboBox.GroupStyle>
  <ComboBox.ItemTemplate>
    <DataTemplate>
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Name}" Margin="0,0,5,0" />
        <TextBlock Text="{Binding Value}" />
      </StackPanel>
    </DataTemplate>
  </ComboBox.ItemTemplate>
</ComboBox>

后面的代码:

  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();

      m_preventFeedback = true;
      ItemsList = new ObservableCollection<VMItem>
      {
        new VMItem(new Item("John", 1234), 2),
        new VMItem(new Item("Peter", 2345), 2),
        new VMItem(new Item("Michael", 3456), 2),
      };

      ListCollectionView view = new ListCollectionView(ItemsList);
      view.GroupDescriptions.Add(new PropertyGroupDescription("CategoryId", new ItemGroupValueConverter()));
      MyCombo.ItemsSource = view;
      m_preventFeedback = false;
    }

    private ObservableCollection<VMItem> ItemsList = new ObservableCollection<VMItem>();

    bool m_preventFeedback = false;

    private void MyCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      if (m_preventFeedback) return;

      if (MyCombo.SelectedItem is VMItem item)
      {
        m_preventFeedback = true;

        VMItem mru = ItemsList.FirstOrDefault(i => i.Name == item.Name && i.CategoryId == 1) ?? new VMItem(item.Item, 1);

        ItemsList.Remove(mru);
        ItemsList.Insert(0, mru);
        MyCombo.SelectedItem = mru;

        m_preventFeedback = false;
      }
    }
  }

  public class ItemGroupValueConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
      switch ((int)value)
      {
        case 1: return "Last Used";
        case 2: return "Available Items";
      }

      return "N/A";

    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
      return value;
    }
  }

  public class VMItem : INotifyPropertyChanged
  {
    private Item m_item;

    public VMItem(Item item, int categoryId)
    {
      m_item = item;
      m_categoryId = categoryId;
    }

    public string Name
    {
      get { return m_item.Name; }
      set
      {
        m_item.Name = value;
        OnPropertyChanged("Name");
      }
    }

    public int Value
    {
      get { return m_item.Value; }
      set
      {
        m_item.Value = value;
        OnPropertyChanged("Value");
      }
    }

    private int m_categoryId;
    public int CategoryId
    {
      get { return m_categoryId; }
      set
      {
        m_categoryId = value;
        OnPropertyChanged("CategoryId");
      }
    }


    public Item Item => m_item;

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string property)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }

  }

  public class Item
  {
    public Item(string name, int value)
    {
      Name = name;
      Value = value;
    }

    public string Name { get; set; }
    public int Value { get; set; }
  }