首页 > 解决方案 > 如何将放置在拆分按钮内容内的标签文本与选定的列表框项目绑定?

问题描述

我正在尝试使用 WPF 拆分按钮创建一个类似控件的下拉菜单。我可以让下拉菜单工作,但我接下来要实现的是,如果用户从下拉菜单中选择一个项目,该文本将更新为拆分按钮内容。此外,默认情况下,加载此控件时,第一个项目名称设置为拆分按钮内容。

我的偏好是在 xaml 本身中进行。

这是现有代码:

<userControls:BaseUserControl x:Class="UserControls.TestControl"
         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:userControls="clr-namespace:MainAssembly.UserControls"
         xmlns:const="clr-namespace:MainAssembly.const"
         xmlns:toolkit="abc/Wpf.Toolkit"
         xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">

<userControls:BaseUserControl.Resources>
    <ResourceDictionary>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
        <toolkit:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
        <toolkit:MultiValueToObjectArrayConverter x:Key="MultiValueToObjectArrayConverter" />
        <toolkit:BoolToInvertedBoolToVisibilityConverter x:Key="BoolToInvertedBoolToVisibilityConverter" />
    </ResourceDictionary>
</userControls:BaseUserControl.Resources>

<xctk:SplitButton Name="TestSplitBtn" ToolBar.OverflowMode="AsNeeded"
                                          Margin="5,1,5,1"
                                          >
    <xctk:SplitButton.DropDownContent>
        <ListBox Name="DropDownMenu" Margin="5,1,5,5"
                                  ItemsSource="{Binding DropDownContentCollection}"
                                  ToolBar.OverflowMode="AsNeeded"                                                                        
                                  Visibility="{Binding DropDownContentCollection, Converter={StaticResource NullToVisibilityConverter}}"
                                  ToolTip="{x:Static const:const.toolTip}"
                                      MaxWidth="150">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Button Style="{DynamicResource TransparentButton}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" Command="{Binding DataContext.TestCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBox}}}" CommandParameter="{Binding}">
                        <Button.Content>
                            <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
                                <Rectangle Fill="{DynamicResource Icon_Test}" Margin="4,0,5,0" Height="16" Width="16" />
                                <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center">
                                    <TextBlock.Text>
                                        <MultiBinding StringFormat="{} {0}({1})">
                                            <Binding Path="Name" />
                                            <Binding Path="ImageCount" />
                                        </MultiBinding>
                                    </TextBlock.Text>
                                </TextBlock>
                            </StackPanel>
                        </Button.Content>
                    </Button>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.Style>
                <Style TargetType="{x:Type ListBox}" BasedOn="{StaticResource ListBoxStyle}">
                    <Setter Property="ItemContainerStyle">
                        <Setter.Value>
                            <Style TargetType="ListBoxItem">
                                <Setter Property="AutomationProperties.AutomationId" Value="{Binding Name}"/>
                            </Style>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.Style>
        </ListBox>
    </xctk:SplitButton.DropDownContent>

    <xctk:SplitButton.Content>
        <toolkit:LabelledButton x:Name="TestSeries"
                                                        AutomationProperties.AutomationId="TestBtn"
                                                        CommandParameter="{Binding ViewerState}" Margin="0,1,0,0" Height="56">
            <toolkit:LabelledButton.Content>
                <Rectangle Fill="{DynamicResource Icon_Test}" Margin="0,2,0,-5"/>

            </toolkit:LabelledButton.Content>
            <toolkit:LabelledButton.Style>
                <Style TargetType="{x:Type toolkit:LabelledButton}" BasedOn="{StaticResource {x:Type toolkit:LabelledButton}}">
                    <Setter Property="LabelText" Value="{Binding SelectedImageSeries, Mode=TwoWay}" />
                    <Setter Property="ToolTip" Value="{Binding SelectedImageSeries, Mode=TwoWay}" />
                </Style>
            </toolkit:LabelledButton.Style>
        </toolkit:LabelledButton>
    </xctk:SplitButton.Content>

    <xctk:SplitButton.Style>
        <Style TargetType="{x:Type xctk:SplitButton}" BasedOn="{StaticResource TestSplitButtonStyle}">
            <Setter Property="Visibility" Value="Visible"/>
        </Style>
    </xctk:SplitButton.Style>
</xctk:SplitButton>

此外,当我单击按钮时,它会执行相关的命令,但下拉菜单不会关闭。

标签: c#wpfxamldata-binding

解决方案


您还必须将ListBox.SelectedItem属性绑定到SelectedImageSeries.

看起来你使用的SplitButton太复杂了。我发布了一个简化的解决方案。由于我们现在正确绑定了 中的SelectedImageSeries项目ListBox,因此无需在 中Button添加DataTemplatea ListBoxItem。您可以直接在属性设置器中处理所选项目,
而不是执行。为此,TestCommand我在视图模型中添加了一个方法。OnSelectedImageSeriesChanged

也无需添加另一个按钮 ( LabelButton) 嵌套到SplitButton.Content. DataTemplate您可以通过将 a 分配给属性并将 aSplitButton.ContentTemplate绑定ICommand到属性来修改内容布局,SplitButton.Command并且可以选择使用SplitButton.CommandParameter.

BindingMode.TwoWay仅当您将数据发送回数据源时才需要。在您使用它的每个地方都是多余的:LabelText永远ToolTip不要发送数据(他们不能)。大多数可以在反映用户输入时发送数据的属性在TwoWay默认情况下是绑定的,例如TextBox.Text,ListBox.SelectedItemToggleButton.IsChecked

查看模型

class MainViewModel
{
  public ObservableCollection<ImageSeries> DropDownContentCollection { get; }

  private ImageSeries selectedImageSeries;   
  public ImageSeries SelectedImageSeries
  {
    get => this.selectedImageSeries;
    set 
    { 
      this.selectedImageSeries = value; 
      OnPropertyChanged();

      // Handle ListBox item selected. Replaces command.
      OnSelectedImageSeriesChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

拆分按钮

<xctk:SplitButton Width="100" 
                  Content="{Binding SelectedImageSeries}"
                  Command="{Binding HandleButtonPressedCommand}"
                  CommandParameter="{Binding ViewerState}">
  <xctk:SplitButton.ContentTemplate>
    <DataTemplate>
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding}" />
        <Rectangle Fill="{DynamicResource Icon_Test}" Width="20" Height="20" />
      </StackPanel>
    </DataTemplate>
  </xctk:SplitButton.ContentTemplate>
  <xctk:SplitButton.DropDownContent>
    <ListBox ItemsSource="{Binding DropDownContentCollection}" SelectedItem="{Binding SelectedImageSeries}" />
  </xctk:SplitButton.DropDownContent>
</xctk:SplitButton>

推荐阅读