Enlace de datos a SelectedItem en una vista de árbol de WPF

Resuelto Natrium asked hace 15 años • 21 respuestas

¿Cómo puedo recuperar el elemento seleccionado en una vista de árbol de WPF? Quiero hacer esto en XAML porque quiero vincularlo.

Se podría pensar que lo es SelectedItempero aparentemente eso no existe es de sólo lectura y por tanto inutilizable.

Esto es lo que quiero hacer:

<TreeView ItemsSource="{Binding Path=Model.Clusters}" 
            ItemTemplate="{StaticResource ClusterTemplate}"
            SelectedItem="{Binding Path=Model.SelectedCluster}" />

Quiero vincularlo SelectedItema una propiedad en mi modelo.

Pero esto me da el error:

La propiedad 'SelectedItem' es de solo lectura y no se puede configurar desde el marcado.

Editar: Ok, esta es la forma en que resolví esto:

<TreeView
          ItemsSource="{Binding Path=Model.Clusters}" 
          ItemTemplate="{StaticResource HoofdCLusterTemplate}"
          SelectedItemChanged="TreeView_OnSelectedItemChanged" />

y en el archivo de código subyacente de mi xaml:

private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    Model.SelectedCluster = (Cluster)e.NewValue;
}
Natrium avatar Jun 16 '09 14:06 Natrium
Aceptado

Me doy cuenta de que ya se aceptó una respuesta, pero la preparé para resolver el problema. Utiliza una idea similar a la solución de Delta, pero sin la necesidad de subclasificar TreeView:

public class BindableSelectedItemBehavior : Behavior<TreeView>
{
    #region SelectedItem Property

    public object SelectedItem
    {
        get { return (object)GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged));

    private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var item = e.NewValue as TreeViewItem;
        if (item != null)
        {
            item.SetValue(TreeViewItem.IsSelectedProperty, true);
        }
    }

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();

        this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
        }
    }

    private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        this.SelectedItem = e.NewValue;
    }
}

Luego puedes usar esto en tu XAML como:

<TreeView>
    <e:Interaction.Behaviors>
        <behaviours:BindableSelectedItemBehavior SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
    </e:Interaction.Behaviors>
</TreeView>

¡Ojalá ayude a alguien!

Steve Greatrex avatar Feb 25 '2011 14:02 Steve Greatrex

Esta propiedad existe: TreeView.SelectedItem

Pero es de sólo lectura, por lo que no puedes asignarlo mediante un enlace, sólo recuperarlo.

Thomas Levesque avatar Jun 16 '2009 08:06 Thomas Levesque

Bueno, encontré una solución. Mueve el desorden para que MVVM funcione.

Primero agregue esta clase:

public class ExtendedTreeView : TreeView
{
    public ExtendedTreeView()
        : base()
    {
        this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(___ICH);
    }

    void ___ICH(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (SelectedItem != null)
        {
            SetValue(SelectedItem_Property, SelectedItem);
        }
    }

    public object SelectedItem_
    {
        get { return (object)GetValue(SelectedItem_Property); }
        set { SetValue(SelectedItem_Property, value); }
    }
    public static readonly DependencyProperty SelectedItem_Property = DependencyProperty.Register("SelectedItem_", typeof(object), typeof(ExtendedTreeView), new UIPropertyMetadata(null));
}

y agrega esto a tu xaml:

 <local:ExtendedTreeView ItemsSource="{Binding Items}" SelectedItem_="{Binding Item, Mode=TwoWay}">
 .....
 </local:ExtendedTreeView>
Delta avatar Aug 20 '2010 21:08 Delta

Responde un poco más de lo que espera el OP... Pero espero que al menos pueda ayudar a alguien.

Si desea ejecutar a ICommandcada vez que SelectedItemcambie, puede vincular un comando en un evento y el uso de una propiedad SelectedItemen ya ViewModelno será necesario.

Para hacerlo:

1- Agregar referencia aSystem.Windows.Interactivity

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

2- Vincula el comando al eventoSelectedItemChanged

<TreeView x:Name="myTreeView" Margin="1"
            ItemsSource="{Binding Directories}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectedItemChanged">
            <i:InvokeCommandAction Command="{Binding SomeCommand}"
                                   CommandParameter="
                                            {Binding ElementName=myTreeView
                                             ,Path=SelectedItem}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <TreeView.ItemTemplate>
           <!-- ... -->
    </TreeView.ItemTemplate>
</TreeView>
JiBéDoublevé avatar Mar 09 '2016 09:03 JiBéDoublevé

Esto se puede lograr de una manera "mejor" usando solo el enlace y EventToCommand de la biblioteca GalaSoft MVVM Light. En su VM agregue un comando que se llamará cuando se cambie el elemento seleccionado e inicialice el comando para realizar cualquier acción que sea necesaria. En este ejemplo utilicé un RelayCommand y simplemente estableceré la propiedad SelectedCluster.

public class ViewModel
{
    public ViewModel()
    {
        SelectedClusterChanged = new RelayCommand<Cluster>( c => SelectedCluster = c );
    }

    public RelayCommand<Cluster> SelectedClusterChanged { get; private set; } 

    public Cluster SelectedCluster { get; private set; }
}

Luego agregue el comportamiento EventToCommand en su xaml. Esto es realmente fácil usando blend.

<TreeView
      x:Name="lstClusters"
      ItemsSource="{Binding Path=Model.Clusters}" 
      ItemTemplate="{StaticResource HoofdCLusterTemplate}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectedItemChanged">
            <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding SelectedClusterChanged}" CommandParameter="{Binding ElementName=lstClusters,Path=SelectedValue}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TreeView>
bstoney avatar Feb 04 '2011 14:02 bstoney