2015-10-15 23 views
9

Mi sono imbattuto in qualcosa che potrebbe essere un bug nella casella di riepilogo wpf. Si prega di vedere il codice e poi spiego cosa succedeListbox IsSynchronizedWithCurrentItem causa la selezione del primo elemento anche se nulla gli dice di farlo

Finestra

<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> 

controllo utente

<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> 

codice dietro il controllo dell'utente

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); 
    } 
} 

vista del modello

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() }; 
    } 
} 

Ora il problema è che se all'interno del controllo utente (dove è la casella di riepilogo) se non si imposta la sincronizzazione su true, allora non vedrà il primo elemento all'avvio, se lo imposto, quindi qualcosa costringe un add alla raccolta. E poi, anche se all'interno dei bambini selezionati non imposto un valore, imposta comunque il valore del primo figlio.

Questo è in realtà una casella combinata multiselect, è arrivato così lontano e questa cosa semplice mi ha fermato per metà giornata. E non riesco a trovare cosa lo sta causando.

Qualsiasi aiuto sarebbe apprezzato

risposta

1

Questo ci aiuterà su come capire come si comporta quando listbox IsSynchronizedWithCurrentItem è impostata su true. Link

Dato il tuo requisito, questo dovrebbe essere falso.

Il gestore di eventi LisBox.SelectionChanged verrà richiamato ogni volta che si impostano i valori su ASLSelectedItems.

penso che funzionerà da:

  1. Rimuovere vostro gestore dal ListBox (lstBox).
  2. Poi modificare il 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 }

+0

Ciao, Grazie per la risposta, ma questo non risolve nulla, se rimuovere completamente il gestore, e poi in MainWindowViewModel commento questa linea SelectedModels = new ObservableCollection () {Models.FirstOrDefault()}; il primo oggetto viene comunque selezionato. Nulla lo dice per selezionare il primo elemento all'avvio, ma va ancora avanti e lo fa. – adminSoftDK

+0

@adminSoftDK Ok, stai usando INPC sul tuo codice attuale? – tgpdyk

+0

No, perché queste sono raccolte osservabili, quindi sono pronte per questo. Il codice che ho nella mia domanda è l'intero campione, dovresti essere in grado di copiarlo e incollarlo in una nuova soluzione e vedere il comportamento scorretto – adminSoftDK