更新 UserControl 中 ItemsControl (ComboBox) 中的 SelectedItem

发布于 2024-12-12 05:33:14 字数 3222 浏览 0 评论 0原文

在选择 ComboBox 中的任何项目之前,其 SelectedItem 为 null,并且 ComboBox 本身在视觉上是空白的。一旦选择了某些内容,用户似乎没有任何方法可以选择“缺少选择”(尽管可以通过在代码中将 SelectedItem 设置为 null 来完成)。

我的 ComboBox 绑定到我的对象的 ObservableCollections。我不想在每个 ObservableCollection 的前面添加一个“特殊”的第一个类似 null 的对象。因此,我借此机会学习一些有关编写 UserControl 的知识。

问题是 SelectedItem 无法按正常方式工作。也就是说,ComboBox 很好地绑定到了一个支持 ObservableCollection,但从 ComboBox 中选取某些内容不会更新 SelectedItem< /code> 它应该绑定到。

我觉得我需要将一些信息从用户控件中的组合框传递到...某个地方。我走在正确的轨道上吗?我应该用谷歌搜索什么?

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 :(

Before any item in a ComboBox is selected, its SelectedItemis null and the ComboBox itself is visually blank. Once something is selected, there doesn't seem to be any way for the user to select "the absence of a selection" (though it can be done by setting SelectedItem to null in code).

My ComboBoxes are bound to ObservableCollections of my objects. I don't want to add a "special" first null-like object to the front of every ObservableCollection. So I'm taking this opportunity to learn a bit about writing a UserControl.

The problem is SelectedItem doesn't work the way it normally does. That is, the ComboBox is nicely bound to a backing ObservableCollection, but picking something from the ComboBox doesn't update the SelectedItem it's supposed to be bound to.

I feel like I need to be passing along some information from the ComboBox in the UserControl to...somewhere. Am I on the right track? What should I be googling for?

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>

Usage:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

眉黛浅 2024-12-19 05:33:14

由于组合框派生自 Selector 类,而该类又派生自 ItemsControl。因此,通过从 UserControl 派生,您将避免使用 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 派生。将其替换为从类 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"/>

Since combobox derives from Selector class which in turn derives from ItemsControl. So, by deriving from UserControl you are devoiding your combobox with properties of Selector class which might internally handle the Selection thing for you. so, i would suggest instead of deriving it from UserControl, you should derive it from Combobox like this -

public partial class ClearableComboBox : ComboBox

So, that ways you won't have to override the ItemsSource, DisplayMemberPath etc. in your class since it s already present in the ComboBox class. You can always extend your class further to provide addidtional features which is in your case setting the SelectedItem to null on some button click. Hope this is what you want..

EDIT (Custom Control)

Creating a Custom Control is your answer here, to get started if you are not aware of it, look at this for start - http://www.wpftutorial.net/HowToCreateACustomControl.html

When you create a Custom Control say CustomControl1, replace the template for CustomControl1 in your Generic.xaml file with this one -

<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>

By default your CustomControl1 class will be derived from Control. Replace it to derive from class ComboBox so that you don't have declare DP's yet over again like this and copy paste this code there -

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

Now, your CustomControl1 class is ready for use in your other xaml files like this -

<local:CustomControl1 ItemsSource="{Binding YourSource}"
                      SelectedItem="{Binding YourSelectedItem}"
                      Height="50" Width="200"/>
月下客 2024-12-19 05:33:14

我选择处理组合框上的按键事件并处理转义按键以清除组合框的 SelectedItem

I chose to handle the key press event on the combo box and handle the escape key press to clear out the combo box's SelectedItem.

我不咬妳我踢妳 2024-12-19 05:33:14

我认为有更好的方法,为 ComboBox 开发一个包装器/装饰器,在 ComboBox 旁边添加一个按钮并在单击时擦除选择。

I think there's a better way, develop a wrapper/Adorner for ComboBox, that adds a button next to the ComboBox and wipe the selection on click.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文