Listbox IsSynchronizedWithCurrentItem导致选择第一个项目,即使它没有告诉它这样做



我遇到了一个可能是wpf列表框的bug。请参阅代码,然后我解释发生了什么

窗口
<Window >
<Grid>
    <local:MultiSelectionComboBox Width="200"
                                  MinHeight="25"
                                  HorizontalAlignment="Center"
                                  VerticalAlignment="Center"
                                  ASLDisplayMemberPath="FirstName"
                                  ASLSelectedItems="{Binding SelectedModels}"
                                  ItemsSource="{Binding Models}" />
    <ListBox HorizontalAlignment="Right"
             VerticalAlignment="Top"
             DisplayMemberPath="FirstName"
             ItemsSource="{Binding SelectedModels}" />
</Grid>
</Window>
用户控制

<ComboBox>
<ComboBox.Style>
    <Style TargetType="{x:Type ComboBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ComboBox}">
                    <Grid x:Name="MainGrid"
                          Width="Auto"
                          Height="Auto"
                          SnapsToDevicePixels="true">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="0" />
                        </Grid.ColumnDefinitions>
                        <Popup x:Name="PART_Popup"
                               Grid.ColumnSpan="2"
                               Margin="1"
                               AllowsTransparency="true"
                               IsOpen="{Binding Path=IsDropDownOpen,
                                                RelativeSource={RelativeSource TemplatedParent}}"
                               Placement="Center"
                               PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
                            <Border x:Name="DropDownBorder"
                                    MinWidth="{Binding Path=ActualWidth,
                                                       ElementName=MainGrid}"
                                    MaxHeight="{TemplateBinding MaxDropDownHeight}">
                                <ScrollViewer CanContentScroll="true">
                                    <StackPanel>
                                        <CheckBox x:Name="checkBox" 
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ComboBox}, Path=CheckAllCommand}">select all</CheckBox>
                                        <ListBox x:Name="lstBox"
here lies the problem without the line below I dont get to see the result on start up,
with the it selects the first item even if nothing is triggering it
                                                 IsSynchronizedWithCurrentItem="True"
                                                 ItemsSource="{TemplateBinding ItemsSource}"
                                                 KeyboardNavigation.DirectionalNavigation="Contained"
                                                 SelectionChanged="ListBoxOnSelectionChanged"
                                                 SelectionMode="Multiple"
                                                 SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                    </StackPanel>
                                </ScrollViewer>
                            </Border>
                        </Popup>
                        <ToggleButton Grid.ColumnSpan="2"
                                      MinWidth="20"
                                      HorizontalAlignment="Right"
                                      Background="{TemplateBinding Background}"
                                      BorderBrush="{TemplateBinding BorderBrush}"
                                      IsChecked="{Binding Path=IsDropDownOpen,
                                                          Mode=TwoWay,
                                                          RelativeSource={RelativeSource TemplatedParent}}"
                                      Style="{DynamicResource ToggleButtonStyle1}" />
                        <ItemsControl x:Name="itemsControl"
                                      Margin="4,2,0,2"
                                      ItemsSource="{Binding Path=SelectedItems,
                                                            ElementName=lstBox}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <WrapPanel IsItemsHost="True" Orientation="Horizontal" />
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Border Margin="1"
                                            HorizontalAlignment="Left"
                                            VerticalAlignment="Top"
                                            Background="#383838"
                                            CornerRadius="2"
                                            Padding="5,2,3,2">
                                        <StackPanel Orientation="Horizontal">
                                            <TextBlock x:Name="Test"
                                                       Foreground="White"
                                                       Loaded="Test_OnLoaded"
                                                       Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                                                                     AncestorType={x:Type ComboBox}},
                                                                      Path=ASLDisplayMemberPathActualValue}" />
                                            <Button Width="12"
                                                    Height="12"
                                                    Margin="5,0,0,0"
                                                    Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                                                                     AncestorType=ComboBox},
                                                                      Path=UnselectCommand}"
                                                    CommandParameter="{Binding}">
                                                <Button.Template>
                                                    <ControlTemplate TargetType="{x:Type Button}">
                                                        <Grid>
                                                            <Ellipse x:Name="PART_ButtonBackground"
                                                                     Fill="White"
                                                                     Opacity="0" />
                                                            <TextBlock x:Name="PART_Text"
                                                                       Margin="0,0,0,1"
                                                                       HorizontalAlignment="Center"
                                                                       VerticalAlignment="Center"
                                                                       FontSize="10"
                                                                       Foreground="White"
                                                                       Text="X" />
                                                        </Grid>
                                                        <ControlTemplate.Triggers>
                                                            <Trigger Property="IsMouseOver" Value="True">
                                                                <Setter TargetName="PART_ButtonBackground" Property="Opacity" Value="0.8" />
                                                                <Setter TargetName="PART_Text" Property="Foreground" Value="Black" />
                                                            </Trigger>
                                                        </ControlTemplate.Triggers>
                                                    </ControlTemplate>
                                                </Button.Template>
                                            </Button>
                                        </StackPanel>
                                    </Border>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ComboBox.Style>
</ComboBox>

用户控件后面的代码

public partial class MultiSelectionComboBox : ComboBox
{
    #region fields, dependencies, command and constructor
    private ListBox listBox;
    private ItemsControl itemsControl;
    private CheckBox checkBox;
    public static readonly DependencyProperty ASLSelectedItemsProperty = DependencyProperty.Register("ASLSelectedItems", typeof(ObservableCollection<object>), typeof(MultiSelectionComboBox), new PropertyMetadata(null));
    public static readonly DependencyProperty ASLDisplayMemberPathProperty = DependencyProperty.Register("ASLDisplayMemberPath", typeof(string), typeof(MultiSelectionComboBox), new PropertyMetadata("Description"));
    public MultiSelectionComboBox()
    {
        InitializeComponent();
    }
    public DelegateCommand<object> UnselectCommand { get; private set; }
    public DelegateCommand CheckAllCommand { get; private set; }
    public ObservableCollection<object> ASLSelectedItems
    {
        get { return (ObservableCollection<object>)GetValue(ASLSelectedItemsProperty); }
        set { SetValue(ASLSelectedItemsProperty, value); }
    }
    public string ASLDisplayMemberPath
    {
        get { return (string)GetValue(ASLDisplayMemberPathProperty); }
        set { SetValue(ASLDisplayMemberPathProperty, value); }
    }
    #endregion
    private void CheckAll()
    {
        if (checkBox.IsChecked == true)
        {
            listBox.SelectAll();
        }
        else
        {
            listBox.UnselectAll();
        }
    }
    private void GetControls()
    {
        checkBox = Template.FindName("checkBox", this) as CheckBox;
        listBox = Template.FindName("lstBox", this) as ListBox;
        itemsControl = Template.FindName("itemsControl", this) as ItemsControl;
    }
    private bool bug = true;
    private void ListBoxOnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        e.AddedItems.Cast<object>().Where(item => !ASLSelectedItems.Contains(item)).ForEach(p => ASLSelectedItems.Add(p));
        e.RemovedItems.Cast<object>().Where(item => ASLSelectedItems.Contains(item)).ForEach(p => ASLSelectedItems.Remove(p));
        checkBox.IsChecked = (listBox.ItemsSource as IList).Count == listBox.SelectedItems.Count;
    }
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        GetControls();
        SetControls();
        UnselectCommand = new DelegateCommand<object>(p => listBox?.SelectedItems.Remove(p));
        CheckAllCommand = new DelegateCommand(CheckAll);
    }
    private void SetControls()
    {
        listBox.DisplayMemberPath = ASLDisplayMemberPath;
        itemsControl.DisplayMemberPath = ASLDisplayMemberPath;
    }
    private void Test_OnLoaded(object sender, RoutedEventArgs e)
    {
        (sender as TextBlock)?.SetBinding(TextBlock.TextProperty, ASLDisplayMemberPath);
    }
}

视图模型
public class MainWindowViewModel
{
    public ObservableCollection<Model> Models { get; set; }
    public ObservableCollection<object> SelectedModels { get; set; }
    public MainWindowViewModel()
    {
        Models = new ObservableCollection<Model>()
        {
            new Model() {FirstName = "Amelia", Age = 0},
            new Model() {FirstName = "Bruno", Age = 5},
            new Model() {FirstName = "Colin", Age = 47},
            new Model() {FirstName = "Daniel", Age = 32},
            new Model() {FirstName = "Iza", Age = 28},
            new Model() {FirstName = "James", Age = 23},
            new Model() {FirstName = "Simon", Age = 23}
        };
        SelectedModels = new ObservableCollection<object> {Models.FirstOrDefault() };
    }
}

现在的问题是,如果在用户控件中(列表框所在的地方),如果我不将synchronize设置为true,那么它不会在启动时看到第一个项目,如果我设置它,那么有些东西会强制添加到集合中。然后,即使在选定的子元素中我没有设置值,它仍然会设置第一个子元素的值。

这实际上是多选组合框,到目前为止,这个简单的东西阻止了我半天。我也不知道是什么原因。

如有任何帮助,不胜感激

这将帮助我们理解当IsSynchronizedWithCurrentItem被设置为true时listbox的行为。链接

根据您的要求,这应该是假的。

LisBox。每次将Values设置为ASLSelectedItems时,都会调用SelectionChanged事件处理程序。

我认为它会起作用:

  1. 从ListBox (lstBox)中删除处理程序
  2. 修改你的DP

    public static readonly DependencyProperty ASLSelectedItemsProperty = DependencyProperty.Register("ASLSelectedItems", typeof(ObservableCollection<object>), typeof(MultiSelectionComboBox), new PropertyMetadata(null, OnSelectedItemsChanged));

    private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Add your logic from your handler }

相关内容

最新更新