使用MVVM向comboBox添加按钮

时间:2016-10-11 12:43:41

标签: wpf xaml mvvm combobox

我正在使用MVVM模式,并且ComboBox绑定到viewmodel中的属性,如下所示:

<ComboBox ItemsSource="{Binding Path=ItemCollection}"
          SelectedItem="{Binding Path=SelectedItem}">    
    <ComboBox.ItemTemplate>
        <DataTemplate>
             <!-- Custom combobox item template -->
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

这很好用,DataTemplate我可以指定每个项目的显示方式。

我想在combobox dropdown itempresenter的末尾添加一个按钮。 类似于它在MS Word中的下图中所做的。

ComboBox with button

当按下所选按钮“更多列...”时,会显示一个对话框,用户可以输入详细信息。我正在尝试归档相同的工作流程。

2 个答案:

答案 0 :(得分:1)

我不认为ComboBox是正确的选择,因为ComboBox的自然行为是选择所点击的项目打开一个对话框

以下是如何实现以下内容的完整代码示例: 1.创建DropDownButton行为,如下所示

    public class DropDownButtonBehavior : Behavior<Button>
    {
    private long attachedCount;
    private bool isContextMenuOpen;

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.AddHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click), true);
    }

    void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        Button source = sender as Button;
        if (source != null && source.ContextMenu != null)
        {
            // Only open the ContextMenu when it is not already open. If it is already open,
            // when the button is pressed the ContextMenu will lose focus and automatically close.
            if (!isContextMenuOpen)
            {
                source.ContextMenu.AddHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed), true);
                Interlocked.Increment(ref attachedCount);
                // If there is a drop-down assigned to this button, then position and display it 
                source.ContextMenu.PlacementTarget = source;
                source.ContextMenu.Placement = PlacementMode.Bottom;
                source.ContextMenu.IsOpen = true;
                isContextMenuOpen = true;
            }
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.RemoveHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click));
    }

    void ContextMenu_Closed(object sender, RoutedEventArgs e)
    {
        isContextMenuOpen = false;
        var contextMenu = sender as ContextMenu;
        if (contextMenu != null)
        {
            contextMenu.RemoveHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed));
            Interlocked.Decrement(ref attachedCount);
        }
    }
}
  1. 为按钮

    创建模板
    <Button>
        <i:Interaction.Behaviors>
            <local:DropDownButtonBehavior/>
        </i:Interaction.Behaviors>
        <Button.Content>
            <StackPanel Orientation="Horizontal">
                <ContentControl Content="{Binding SelectedItem}"/>
                <Separator Margin="2,0">
                    <Separator.LayoutTransform>
                        <TransformGroup>
                            <TransformGroup.Children>
                                <TransformCollection>
                                    <RotateTransform Angle="90"/>
                                </TransformCollection>
                            </TransformGroup.Children>
                        </TransformGroup>
                    </Separator.LayoutTransform>
                </Separator>
                <Path Margin="2" VerticalAlignment="Center" Width="6" Fill="#FF527DB5" Stretch="Uniform" HorizontalAlignment="Right" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "/>
            </StackPanel>
        </Button.Content>
        <Button.ContextMenu>
            <ContextMenu>
                <ContextMenu.ItemContainerStyle>
                    <Style TargetType="MenuItem">
                        <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}, Mode=FindAncestor}, Path=DataContext.SelectionChangedCommand}" />
                        <Setter Property="CommandParameter" Value="{Binding}"></Setter>
                    </Style>
                </ContextMenu.ItemContainerStyle>
                <ContextMenu.ItemsSource>
                    <CompositeCollection>
                        <CollectionContainer Collection="{Binding Source={StaticResource MyData}}" />
                        <MenuItem Header="More"
                                  Command="{Binding MoreButtonCommand}"/>
                    </CompositeCollection>
                </ContextMenu.ItemsSource>
            </ContextMenu>
        </Button.ContextMenu>
    </Button>
    
  2. 视图模型

    public class MainWindowViewModel : BindableBase
    {
    private MyData _selectedItem;
    
    public MainWindowViewModel()
    {
        Collection = new ObservableCollection<MyData>
        {
            new MyData {Data = "aaa"},
            new MyData {Data = "bbb"},
        };
        SelectedItem = Collection.First();
        // This is important. It changes the selected items upon menu item click
        SelectionChangedCommand = new DelegateCommand<MyData>(data => SelectedItem = data);
        MoreButtonCommand = new DelegateCommand(() => {} /* Launch dialog ... */);
    }
    
    public ObservableCollection<MyData> Collection { get; set; }
    
    public MyData SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            OnPropertyChanged(() => SelectedItem);
        }
    }
    public DelegateCommand<MyData> SelectionChangedCommand { get; set; }
    public DelegateCommand MoreButtonCommand { get; set; }
    

    }

  3. 让我解释一下这里发生了什么。 每次单击该按钮都会打开一个类似于ComboBox行为的下拉菜单。此列表中显示的项目是从绑定项目和静态项目生成的MenuItem。从ItemSource创建的命令会向ViewModel启动一个selectionChanged命令,导致所选项目发生更改,而静态命令则可以启动您指定给它的任何命令。

    希望这有帮助

答案 1 :(得分:0)

我需要使用嵌入式搜索/过滤功能创建一次自定义下拉菜单,并通过创建一个按钮来完成它,该按钮会在按下时显示弹出窗口。弹出窗口有一个项目列表框,并通过使用网格布局在顶部和下方显示一些按钮。我在这里为你剥了一点:

<UserControl x:Class="CustomDropDown"
             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" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
             mc:Ignorable="d"  x:Name="Root" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Button x:Name="DropDownButton" Click="DropDownButton_Click" HorizontalContentAlignment="Stretch">
            <DockPanel HorizontalAlignment="Stretch" LastChildFill="False" Margin="2,0">
                <TextBlock DockPanel.Dock="Left" Text="{Binding ElementName=Root, Path=Header}"/>
                <TextBlock DockPanel.Dock="Right" Text="▼"/>
            </DockPanel>
        </Button>
        <Popup x:Name="DropDownPopup" Placement="Bottom" Focusable="False" StaysOpen="False"
                Width="{Binding ElementName=DropDownButton, Path=ActualWidth}" MinWidth="250"
                Height="Auto" AllowsTransparency="True">
            <Border Padding="5" Background="White"  Margin="0,0,8,8"
                    BorderBrush="Silver" CornerRadius="0,0,5,5" BorderThickness="1">
                <Border.Effect>
                    <DropShadowEffect BlurRadius="5" Opacity="0.5"/>
                </Border.Effect>
                <Grid HorizontalAlignment="Stretch" Background="White">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <ListBox Grid.Row="0" MaxHeight="150" x:Name="DropDownList" SelectionMode="Extended"
                            ItemsSource="{Binding ElementName=Root,  Path=ItemsSource}"
                            DisplayMemberPath="{Binding ElementName=Root, Path=DisplayMemberPath}">
                        <ListBox.ItemContainerStyle>
                            <Style TargetType="{x:Type ContentControl}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                            <!-- Item Style -->
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ListBox.ItemContainerStyle>
                    </ListBox>
                    <Grid>
                        <!-- More Columns Button Here -->
                    </Grid>
                </Grid>
            </Border>
        </Popup>
    </Grid>
</UserControl>

因为我把它作为UserControl,所以我有一些依赖属性,比如Header,DisplayMemberPath等,我希望能够在XAML中使用控件时定义绑定。 SelectedItems有自己的依赖属性,并在后面的代码中注册了一个事件,以使所有内容保持同步。

public IRangeCollection SelectedItems
{
    get { return (IRangeCollection)GetValue(SelectedItemsProperty); }
    set { SetValue(SelectedItemsProperty, value); }
}

// Using a DependencyProperty as the backing store for SelectedItems.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemsProperty =
    DependencyProperty.Register("SelectedItems",
        typeof(IRangeCollection), typeof(CustomDropDown),
        new PropertyMetadata(new PropertyChangedCallback(SelectedItemsPropertyChanged)));

private static void SelectedItemsPropertyChanged(DependencyObject sender,
    DependencyPropertyChangedEventArgs e)
{
    CustomDropDown dropDown = (CustomDropDown)sender;
    dropDown.UpdateDropDownFromSelectedItems();
    dropDown.UpdateSelectedItemsCollectionChangedHandler(e.OldValue, e.NewValue);
}

private void UpdateSelectedItemsCollectionChangedHandler(object oldValue, object newValue)
{
    if (oldValue != null && oldValue is INotifyCollectionChanged)
    {
        ((INotifyCollectionChanged)oldValue).CollectionChanged -= SelectedItems_CollectionChanged;
    }
    if (newValue != null && newValue is INotifyCollectionChanged)
    {
        ((INotifyCollectionChanged)newValue).CollectionChanged += SelectedItems_CollectionChanged;
    }
}