Обновление SelectedItem в ItemsControl (ComboBox) в UserControl

#c# #wpf #user-controls #combobox #dependency-properties

#c# #wpf #пользовательские элементы управления #выпадающий список #свойства зависимостей

Вопрос:

Перед ComboBox тем, как будет выбран какой-либо элемент в a, его SelectedItem значение равно null, а ComboBox сам элемент визуально пуст. Как только что-то выбрано, у пользователя, похоже, нет никакого способа выбрать «отсутствие выделения» (хотя это можно сделать, установив значение SelectedItem null в коде).

Мои выпадающие списки привязаны к наблюдаемым коллекциям моих объектов. Я не хочу добавлять «специальный» первый нулевой объект в начало каждой ObservableCollection. Итак, я пользуюсь этой возможностью, чтобы немного узнать о написании UserControl.

Проблема SelectedItem в том, что он работает не так, как обычно. То есть объект ComboBox хорошо привязан к резервной ObservableCollection копии, но выбор чего-либо из ComboBox не обновляет SelectedItem то, к чему он должен быть привязан.

Я чувствую, что мне нужно передавать некоторую информацию из ComboBox в UserControl … куда-нибудь. Я на правильном пути? Что я должен искать в Google?

C#:

 public partial class ClearableComboBox : UserControl
{
    public ClearableComboBox()
    {
        InitializeComponent();
    }

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)base.GetValue(ItemsSourceProperty); }
        set { base.SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource",
            typeof(IEnumerable),
            typeof(ClearableComboBox));

    public object SelectedItem
    {
        get { return (object)base.GetValue(SelectedItemProperty); }
        set { base.SetValue(SelectedItemProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem",
            typeof(object),
            typeof(ClearableComboBox));

    public string DisplayMemberPath
    {
        get { return (string)base.GetValue(DisplayMemberPathProperty); }
        set { base.SetValue(DisplayMemberPathProperty, value); }
    }

    public static readonly DependencyProperty DisplayMemberPathProperty =
        DependencyProperty.Register("DisplayMemberPath",
            typeof(string),
            typeof(ClearableComboBox));

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        comboBox.SelectedItem = null;
    }
}
  

XAML:

 <UserControl x:Class="MyProj.ClearableComboBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             x:Name="root">
    <DockPanel>
        <Button DockPanel.Dock="Left" Click="Button_Click" ToolTip="Clear">
            <Image Source="pack://application:,,,/img/icons/silk/cross.png" Stretch="None" />
        </Button>
        <ComboBox
            Name="comboBox"
            ItemsSource="{Binding ElementName=root, Path=ItemsSource}"
            SelectedItem="{Binding ElementName=root, Path=SelectedItem}"
            DisplayMemberPath="{Binding ElementName=root, Path=DisplayMemberPath}" />
    </DockPanel>
</UserControl>
  

Использование:

 <wpfControl:ClearableComboBox ItemsSource="{Binding Path=Things}"
                              DisplayMemberPath="SomeProperty"
                              SelectedItem="{Binding Path=SelectedThing}" />

// Picking a Thing doesn't update SelectedThing :(
  

Ответ №1:

Поскольку combobox является производным от Selector class, который, в свою очередь, является производным от ItemsControl . Итак, извлекая из UserControl, вы лишаете свой combobox свойств класса Selector, которые могут внутренне обрабатывать объект выбора для вас. итак, я бы предложил вместо того, чтобы выводить его из UserControl, вы должны вывести его из Combobox следующим образом —

 public partial class ClearableComboBox : ComboBox
  

Таким образом, вам не придется переопределять ItemsSource , DisplayMemberPath и т. Д. в вашем классе, поскольку он уже присутствует в ComboBox классе. Вы всегда можете расширить свой класс дальше, чтобы предоставить дополнительные функции, которые в вашем случае устанавливают значение SelectedItem null при нажатии какой-либо кнопки. Надеюсь, это то, что вы хотите..

РЕДАКТИРОВАТЬ (пользовательский элемент управления)

Создание пользовательского элемента управления — это ваш ответ здесь, чтобы начать, если вы не знаете об этом, посмотрите на это для начала — http://www.wpftutorial.net/HowToCreateACustomControl.html

Когда вы создаете пользовательский элемент управления, скажем, CustomControl1, замените шаблон для CustomControl1 в вашем Generic.xaml файле на этот —

 <ControlTemplate TargetType="{x:Type local:CustomControl1}">
    <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
              <DockPanel>
                 <Button Name="btn" DockPanel.Dock="Left" ToolTip="Clear" Width="20">
                    <Image Source="pack://application:,,,/img/icons/silk/cross.png" Stretch="None" />
                </Button>
                <ComboBox Name="comboBox"
                          ItemsSource="{TemplateBinding ItemsSource}"
                          SelectedItem="{TemplateBinding SelectedItem}"
                          DisplayMemberPath="{TemplateBinding DisplayMemberPath}" />
              </DockPanel>
     </Border>
</ControlTemplate>
  

По умолчанию ваш CustomControl1 класс будет производным от Control . Замените его на производный от class ComboBox , чтобы вам не приходилось снова объявлять DP, как это, и копировать вставить этот код туда —

 public class CustomControl1 : ComboBox
{
        private Button clearButton;
        private ComboBox comboBox;

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

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            clearButton = GetTemplateChild("btn") as Button;
            comboBox = GetTemplateChild("comboBox") as ComboBox;
            clearButton.Click  = new RoutedEventHandler(clearButton_Click);
        }

        private void clearButton_Click(object sender, RoutedEventArgs e)
        {
            comboBox.SelectedItem = null;
        }
}
  

Теперь ваш класс CustomControl1 готов к использованию в других ваших файлах xaml, подобных этому —

 <local:CustomControl1 ItemsSource="{Binding YourSource}"
                      SelectedItem="{Binding YourSelectedItem}"
                      Height="50" Width="200"/>
  

Комментарии:

1. Как бы вы посоветовали мне очистить мой ClearableComboBox с помощью щелчка мыши?

2. Я обновил свой ответ с использованием пользовательского элемента управления. Посмотрите и дайте мне знать в случае каких-либо проблем.

3. Когда я использую <local:CustomControl1 ItemsSource="{Binding Foo}" SelectedItem="{Binding SelectedFoo, Mode=TwoWay}" /> , он заполняется Foos, но SelectedFoo не обновляется, когда я выбираю Foo из выпадающего списка.

4. Пришлось использовать следующее: SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedItem}"

Ответ №2:

Я решил обработать событие нажатия клавиши в поле со списком и обработать нажатие клавиши escape, чтобы очистить поле со списком SelectedItem .

Комментарии:

1. Поле со списком находится в окне, которое было показано с помощью ShowDialog() , поэтому клавиша escape закроет окно вместо очистки поля со списком.

Ответ №3:

Я думаю, что есть лучший способ, разработать оболочку / украшение для ComboBox , которая добавляет кнопку рядом с ComboBox и стирает выделение по щелчку.

Комментарии:

1. Да, это не помогло. Я считаю, что IsSynchronizedWithCurrentItem имеет больше общего с базовым CollectionView (или CollectionViewSource?).