WPF комбинированный список с добавленными элементами MRU - PullRequest
0 голосов
/ 01 ноября 2018

Моя цель - создать комбинированный список, который отображает любой список строк при открытии (так стандартное поведение), но когда пользователь выбирает одну из строк, она добавляется в верхней части списка под «Недавно использованными» сепаратор.

По сути, я хочу элемент управления, который ведет себя точно так же, как элемент управления для выбора шрифтов в 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 голосов
/ 01 ноября 2018

Вы пробовали что-то подобное? Он использует группировку, но делает это особым образом, чтобы элементы mru не удалялись из общего списка / группы:

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; }
  }
...