wpf 带有 ItemTemplate 的 ComboBox,其中包含一个按钮

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/883626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 20:26:51  来源:igfitidea点击:

ComboBox with ItemTemplate that includes a button

wpfsilverlightcomboboxitemtemplate

提问by Brian Genisio

So, lets say I have a ComboBox with a custom data template. One of the items in the data template is a button:

所以,假设我有一个带有自定义数据模板的 ComboBox。数据模板中的一项是按钮:

<ComboBox Width="150" ItemsSource="{Binding MyItems}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <Button Content="ClickMe" /> 
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

The problem with this is that the button eats the click, and the item does not get selected if the button is selected. This means that the pull-down does not go away, and no item is selected.

这样做的问题是按钮会吃掉点击,如果选择了按钮,则项目不会被选中。这意味着下拉不会消失,也不会选择任何项目。

I get WHY this is happening.

我明白为什么会这样。

Is there a way to work around it? Possibly a way to process the button click (I am binding to a command) and tell it to continue up the chain so the combo box can also process the click?

有没有办法解决它?可能是一种处理按钮点击的方法(我绑定到一个命令)并告诉它继续向上链,以便组合框也可以处理点击?

Note: I am seeing my problem in Silverlight, but I am guessing that the exact same behavior can be seen with WPF.

注意:我在 Silverlight 中看到了我的问题,但我猜测在 WPF 中可以看到完全相同的行为。

采纳答案by Bryan Anderson

Your best bet would probably be to set the SelectedItem in the button's command.

您最好的选择可能是在按钮的命令中设置 SelectedItem。

回答by Brian Genisio

OK, I got it figured out. It is a total hack, but it still lets me bind my command to the button and continue to have Combo-box behavior for selecting the item:

好的,我明白了。这是一个彻底的黑客,但它仍然让我将我的命令绑定到按钮并继续使用组合框行为来选择项目:

<ComboBox x:Name="MyCombo" Width="150" ItemsSource="{Binding MyItems}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <Button Content="ClickMe" Click="Button_Click" /> 
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

And in the code behind:

在后面的代码中:

private void Button_Click(object sender, RoutedEventArgs e)
{
    MyCombo.SelectedItem = (sender as Button).DataContext;
    MyCombo.IsDropDownOpen = false;
}

If I really wanted to, I could bind the SelectedItem and IsDropDownOpen to properties in my ViewModel but I decided against it to keep this behavior as a hack extension of the XAML, in an effort to keep my ViewModel clean.

如果我真的想要,我可以将 SelectedItem 和 IsDropDownOpen 绑定到我的 ViewModel 中的属性,但我决定反对它,将这种行为作为 XAML 的黑客扩展,以保持我的 ViewModel 干净。

回答by MatthiasG

I found another possibility for the MVVM context. I used an derived class for ComboBoxand if an item is adden which derives from ButtonBaseI attach to the Clickevent to close the ComboBox.

我发现了 MVVM 上下文的另一种可能性。我使用了派生类ComboBox,如果添加了一个派生自的项目,ButtonBase我将附加到Click事件以关闭ComboBox.

This works for my project - but just, because the items itself are buttons, it would not work if they just contain buttons as a child element.

这适用于我的项目 - 但只是因为项目本身是按钮,如果它们只包含按钮作为子元素,它将不起作用。

public class MyComboBox : ComboBox
{
    public MyComboBox()
    {
        // use Loaded event to modify inital items.
        Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        if (Items != null)
        {
            foreach (var item in Items)
            {
                var button = item as ButtonBase;
                if (button != null)
                {
                    ModifyButtonItem(button);
                }
            }
        }
    }

    protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);
        // Check added items. If an item is a button, modify the button.
        if (e.NewItems != null)
        {
            foreach (var item in e.NewItems)
            {
                var button = item as ButtonBase;
                if (button != null)
                {
                    ModifyButtonItem(button);
                }
            }
        }
    }

    private void ModifyButtonItem(ButtonBase button)
    {
        button.Click += (sender, args) => { IsDropDownOpen = false; };
    }
}

回答by Andy

I don't know if there is a way to do what you want. If you were to put a Buttonin a ListBox, for example, the same behavior occurs - clicking the Buttondoes not cause its item in the ListBoxto be selected. In fact, this is the case for any control in an ItemsControlthat supports selection.

我不知道是否有办法做你想做的事。例如,如果您将 aButton放入 a ListBox,则会发生相同的行为 - 单击Button不会导致其在 中的项目ListBox被选中。事实上,任何ItemsControl支持选择的控件都是这种情况。

You might be able to do something with the Clickevent and mark it as not handled so that it continues up the visual tree, but even then I'm not sure if that would work or not.

您也许可以对Click事件做一些事情并将其标记为未处理,以便它在可视化树中继续向上,但即使如此,我也不确定这是否可行。