我正在使用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中的下图中所做的。
当按下所选按钮“更多列...”时,会显示一个对话框,用户可以输入详细信息。我正在尝试归档相同的工作流程。
答案 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);
}
}
}
为按钮
创建模板<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>
视图模型
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; }
}
让我解释一下这里发生了什么。
每次单击该按钮都会打开一个类似于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;
}
}