2017-06-13 3 views
2

J'ai un problème avec la propriété IsSelected. Il n'envoie pas de valeurs de view à view-model. J'ai posté mon code ci-dessous ViewModel:TreeViewItem MVVM IsSelected dans Xaml n'envoie pas de valeur à View-Model

public class Viewmodel : INotifyPropertyChanged 
{ 
    private ObservableCollection<int> seznam; 
    public ObservableCollection<int> Seznam 
    { 
     get { return seznam; } 
     set 
     { 
      seznam = value; 
     } 
    } 

    public Viewmodel() 
    { 
     Seznam = new ObservableCollection<int>(); 
     for (int i = 0; i < 3; i++) 
     { 
      Seznam.Add(i); 
     } 
    } 

    bool isSelected; 
    public bool IsSelected 
    { 
     get { return isSelected; } 
     set 
     { 
      isSelected = value; 
     } 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     if (this.PropertyChanged != null) 
      this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

Vue:

 <TreeView ItemsSource="{Binding Seznam}"> 
     <TreeView.ItemContainerStyle> 
      <Style TargetType="{x:Type TreeViewItem}"> 
       <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/> 
      </Style> 
     </TreeView.ItemContainerStyle> 
    </TreeView> 

Il n'arrête toujours pas au point d'arrêt que je mets sur get {return isSelected; }

+1

Toute erreur de liaison pendant que vous Debug? – Chrille

+1

Votre modèle de vue implémente-t-il INotifyPropertyChnaged? S'il vous plaît montrer la signature de votre viewmodel –

+0

Il n'y a pas d'erreur de liaison. –

Répondre

0

Avec votre message mis à jour, il est clair que vous n'avez pas implémenté correctement vos modèles de vue. En particulier, votre TreeView.ItemsSource est lié à la propriété Seznam de votre seul modèle de vue. Ceci est une collection de valeurs int.

Cela signifie que le contexte de données pour chaque conteneur d'éléments dans le TreeView, où vous tentez de vous lier à la propriété IsSelected, est une valeur int. Et bien sûr, les valeurs int n'ont même pas de propriété IsSelected.

(Soit dit en passant, je suis sceptique sur your claim that "There are no binding errors". Si vous avez regardé la sortie de débogage, vous devriez certainement avoir vu une erreur de liaison, où la tentative de lier à l'inexistante IsSelected propriété.)

Et Pensez-y un instant: en supposant que le conteneur d'éléments ait réussi à se lier à la propriété Viewmodel.IsSelected. Combien de conteneurs d'articles pensez-vous qu'il y a? Et combien d'instances de Viewmodel penses-tu qu'il y a? Vous devez croire qu'il existe de nombreux conteneurs d'éléments, à savoir un pour chaque élément de votre collection. Et qu'il n'y a qu'une instance de Viewmodel. Alors, comment l'état de sélection de tous ces éléments pourrait-il correspondre à la propriété unique Viewmodel.IsSelected?

La bonne façon de le faire serait de créer un objet modèle de vue distinct pour la collecte, avec une propriété pour votre valeur int, ainsi que des propriétés pour les IsSelected et IsExpanded états (puisque vous aviez initialement mentionné vouloir les deux) .

Voici l'exemple que j'ai écrit plus tôt juste pour me prouver que l'approche habituelle fonctionnerait comme prévu.Vous ne devriez pas avoir de difficulté à l'adapter à vos besoins & hellip;

modèle de vue par élément:

class TreeItemViewModel : NotifyPropertyChangedBase 
{ 
    public ObservableCollection<TreeItemViewModel> Items { get; } 
     = new ObservableCollection<TreeItemViewModel>(); 

    private bool _isSelected; 
    public bool IsSelected 
    { 
     get { return _isSelected; } 
     set { _UpdateField(ref _isSelected, value, _OnBoolPropertyChanged); } 
    } 

    private bool _isExpanded; 
    public bool IsExpanded 
    { 
     get { return _isExpanded; } 
     set { _UpdateField(ref _isExpanded, value, _OnBoolPropertyChanged); } 
    } 

    private void _OnBoolPropertyChanged(bool obj) 
    { 
     _RaisePropertyChanged(nameof(FullText)); 
    } 

    private string _text; 
    public string Text 
    { 
     get { return _text; } 
     set { _UpdateField(ref _text, value, _OnTextChanged); } 
    } 

    private void _OnTextChanged(string obj) 
    { 
     _RaisePropertyChanged(nameof(FullText)); 
    } 

    public string FullText 
    { 
     get { return $"{Text} (IsSelected: {IsSelected}, IsExpanded: {IsExpanded})"; } 
    } 
} 

modèle Vue principale pour la fenêtre:

class MainViewModel : NotifyPropertyChangedBase 
{ 
    public ObservableCollection<TreeItemViewModel> Items { get; } 
     = new ObservableCollection<TreeItemViewModel>(); 

    public ICommand ClearSelection { get; } 

    public MainViewModel() 
    { 
     ClearSelection = new ClearSelectionCommand(this); 
    } 

    class ClearSelectionCommand : ICommand 
    { 
     private readonly MainViewModel _parent; 

     public ClearSelectionCommand(MainViewModel parent) 
     { 
      _parent = parent; 
     } 

#pragma warning disable 67 
     public event EventHandler CanExecuteChanged; 
#pragma warning restore 67 

     public bool CanExecute(object parameter) 
     { 
      return true; 
     } 

     public void Execute(object parameter) 
     { 
      _parent._ClearSelection(); 
     } 
    } 

    private void _ClearSelection() 
    { 
     _ClearSelection(Items); 
    } 

    private static void _ClearSelection(IEnumerable<TreeItemViewModel> collection) 
    { 
     foreach (TreeItemViewModel item in collection) 
     { 
      _ClearSelection(item.Items); 
      item.IsSelected = false; 
      item.IsExpanded = false; 
     } 
    } 
} 

XAML pour la fenêtre:

<Window x:Class="TestSO44513864TreeViewIsSelected.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:l="clr-namespace:TestSO44513864TreeViewIsSelected" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.DataContext> 
    <l:MainViewModel> 
     <l:MainViewModel.Items> 
     <l:TreeItemViewModel Text="One"> 
      <l:TreeItemViewModel.Items> 
      <l:TreeItemViewModel Text="One A"/> 
      <l:TreeItemViewModel Text="One B"/> 
      </l:TreeItemViewModel.Items> 
     </l:TreeItemViewModel> 
     <l:TreeItemViewModel Text="Two"/> 
     <l:TreeItemViewModel Text="Three"/> 
     </l:MainViewModel.Items> 
    </l:MainViewModel> 
    </Window.DataContext> 

    <Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 
    <Button Content="Clear Selection" Command="{Binding ClearSelection}" 
      HorizontalAlignment="Left"/> 
    <TreeView ItemsSource="{Binding Items}" Grid.Row="1"> 
     <TreeView.ItemContainerStyle> 
     <p:Style TargetType="TreeViewItem"> 
      <Setter Property="IsSelected" Value="{Binding IsSelected}"/> 
      <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/> 
     </p:Style> 
     </TreeView.ItemContainerStyle> 
     <TreeView.ItemTemplate> 
     <HierarchicalDataTemplate DataType="l:TreeItemViewModel" 
            ItemsSource="{Binding Items}"> 
      <TextBlock Text="{Binding FullText}"/> 
     </HierarchicalDataTemplate> 
     </TreeView.ItemTemplate> 
    </TreeView> 
    </Grid> 
</Window> 

Et pour l'exhaustivité & hellip;

La classe de base pour boilerplate INotifyPropertyChanged mise en œuvre:

class NotifyPropertyChangedBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected void _UpdateField<T>(ref T field, T newValue, 
     Action<T> onChangedCallback = null, 
     [CallerMemberName] string propertyName = null) 
    { 
     if (EqualityComparer<T>.Default.Equals(field, newValue)) 
     { 
      return; 
     } 

     T oldValue = field; 

     field = newValue; 
     onChangedCallback?.Invoke(oldValue); 
     _RaisePropertyChanged(propertyName); 
    } 

    protected void _RaisePropertyChanged(string propertyName) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 
+0

Le premier et le deuxième paragraphe de votre réponse ont résolu mon problème. –

-1

Au lieu d'utiliser IsSelected sur chaque nœud de l'arborescence, utilisez TreeView.SelectedItem sur le TreeView lui-même. De là, vous pouvez lier, mais la propriété est en lecture seule.