Wpf treeview выбранная привязка элемента [дубликат]

Перед вызовом метода экземпляра или переменной экземпляра ему нужен объект (экземпляр). Когда экземпляр переменной вызывается из статического метода, компилятор не знает, к какому объекту принадлежит эта переменная. Поскольку статические методы не имеют объекта (только одна копия всегда). Когда вы вызываете переменную экземпляра или методы экземпляра из метода экземпляра, она ссылается на объект this. Это означает, что переменная принадлежит к любому объекту, созданному, и каждый объект имеет свою собственную копию методов и переменных экземпляра.

Статические переменные отмечены как static, а переменные экземпляра не имеют определенного ключевого слова.

22
задан Sergej Andrejev 2 June 2009 в 16:37
поделиться

6 ответов

Другой вариант - использовать привязку. Если у вас есть объект, с которым вы используете привязку, чтобы получить текст каждого TreeViewItem (например), вы можете создать стиль, который также связывает свойство IsSelected:

<TreeView>
    <TreeView.Resources>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsSelected"
                    Value="{Binding Path=IsSelected, Mode=TwoWay}" />
        </Style>
    </TreeView.Resources>
</TreeView>

Это предполагает, что связанный объект имеет свойство IsSelected типа bool. Затем вы можете выбрать TreeViewItem, установив IsSelected на true для своего соответствующего объекта.

Тот же подход может использоваться с свойством IsExpanded для управления при расширении TreeViewItem или рухнул.

31
ответ дан Andy 28 August 2018 в 06:37
поделиться
  • 1
    Да, я знал об этом. Но похоже, что он вводит кодовое соединение. В любом случае, хорошо, что у вас есть этот ответ. Люди, которые придут на эту страницу, могут предпочесть свой путь по моему – Sergej Andrejev 3 June 2009 в 19:40
  • 2
    @Andy: Как я могу сделать это в Silverlight? Я пробую этот код получить ошибку Cannot set read-only property ''.. – Navid Farhadi 21 July 2011 в 08:56
  • 3
    @Navid. Я не уверен, что сможешь. Я не много сделал с Silverlight, но я не думаю, что TreeViewItem.IsSelected является DependencyProperty в Silverlight. Вы не можете использовать привязку для свойств, которые не являются DependencyProperty. – Andy 21 July 2011 в 18:33
  • 4
    – O. R. Mapper 9 December 2012 в 16:05
  • 5
    @ Энди-Спасибо. вы спасли меня. – New Developer 30 November 2014 в 15:35

Очень поздно для вечеринки с моим ответом, но для тех, кто хочет получить чистое решение MVVM, это можно сделать с помощью Trigger Event (для обновления привязки при выборе пользователем нового элемента) и триггера данных (для обновления выбранного элемента когда значение привязки изменяется).

Для этого для работы в главном ViewModel нужны элементы, свойство для выбранного в данный момент элемента и свойство команды, которое будет вызываться, когда изменяется текущий выбранный элемент:

public class MainViewModel : ViewModelBase
{
    // the currently selected node, can be changed programmatically
    private Node _CurrentNode;
    public Node CurrentNode
    {
        get { return this._CurrentNode; }
        set { this._CurrentNode = value; RaisePropertyChanged(() => this.CurrentNode); }
    }

    // called when the user selects a new node in the tree view
    public ICommand SelectedNodeChangedCommand { get { return new RelayCommand<Node>(OnSelectedNodeChanged); } }
    private void OnSelectedNodeChanged(Node node)
    {
        this.CurrentNode = node;
    }

    // list of items to display in the tree view
    private ObservableCollection<Node> _Items;
    public ObservableCollection<Node> Items
    {
        get { return this._Items; }
        set { this._Items = value; RaisePropertyChanged(() => this.Items); }
    }
}

TreeView требует триггера события для вызова SelectedNodeChangedCommand при изменении выбора и DataTrigger в стиле TreeViewItem, чтобы элементы управления выбирались, когда значение CurrentNode было изменено программно в коде:

<TreeView x:Name="treeView" ItemsSource="{Binding Items}"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            xmlns:cmd ="http://www.galasoft.ch/mvvmlight">
        <TreeView.Resources>

            <conv:EqualityConverter x:Key="EqualityConverter" />

            <Style TargetType="TreeViewItem">
                <Setter Property="IsExpanded" Value="True" />
                <Setter Property="IsSelected" Value="False" />
                <Style.Triggers>
                    <!-- DataTrigger updates TreeViewItem selection when vm code changes CurrentNode -->
                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <MultiBinding Converter="{StaticResource EqualityConverter}">
                                <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type TreeView}}" Path="DataContext.CurrentNode" />
                                <Binding />
                            </MultiBinding>
                        </DataTrigger.Binding>
                        <Setter Property="IsSelected" Value="True" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>


            <!-- *** HierarchicalDataTemplates go here ***  -->

        </TreeView.Resources>

        <!-- EventTrigger invokes SelectedNodeChangedCommand when selection is changed by user interaction -->
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <cmd:EventToCommand Command="{Binding SelectedNodeChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=SelectedItem}"  />
            </i:EventTrigger>
        </i:Interaction.Triggers>

    </TreeView>

DataTrigger работает, обнаруживая, когда значение CurrentNode соответствует узлу для текущего элемента списка. К сожалению, DataTriggers не могут привязать свое значение, поэтому вместо этого нужно протестировать с помощью EqualityConverter, который просто выполняет простое сравнение:

    public class EqualityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values[0] == values[1];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
0
ответ дан Mark Feldman 28 August 2018 в 06:37
поделиться
4
ответ дан Matt 28 August 2018 в 06:37
поделиться

В моем случае (у меня была такая же проблема), но было неприемлемо использовать привязку к свойству IsSelected объекта Data, а также я не мог легко получить путь к элементу дерева, поэтому следующий код отлично справился с работой:

  private void SelectTreeViewItem(object item)
    {
        try
        {
            var tvi = GetContainerFromItem(this.MainRegion, item);

            tvi.Focus();
            tvi.IsSelected = true;

            var selectMethod =
                typeof(TreeViewItem).GetMethod("Select",
                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            selectMethod.Invoke(tvi, new object[] { true });
        }
        catch { }
    }

  private TreeViewItem GetContainerFromItem(ItemsControl parent, object item)
    {
        var found = parent.ItemContainerGenerator.ContainerFromItem(item);
        if (found == null)
        {
            for (int i = 0; i < parent.Items.Count; i++)
            {
                var childContainer = parent.ItemContainerGenerator.ContainerFromIndex(i) as ItemsControl;
                TreeViewItem childFound = null;
                if (childContainer != null && childContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                {
                    childContainer.ItemContainerGenerator.StatusChanged += (o, e) =>
                        {
                             childFound = GetContainerFromItem(childContainer, item);
                        };
                }
                else
                {
                     childFound = GetContainerFromItem(childContainer, item);                            
                }
                if (childFound != null)
                    return childFound;                 
            }
        }
        return found as TreeViewItem;
    }
0
ответ дан Mladen Nikolov 28 August 2018 в 06:37
поделиться

Да, метод ContainerFromItem ничего не возвращает, даже если вы вызываете его из прямого родительского TreeViewItem.

Возможно, вам придется немного переделать. Если вы создадите все как явный TreeViewItem, вы сможете сохранить ссылку на него и установить IsSelected на нем.

0
ответ дан RandomEngy 28 August 2018 в 06:37
поделиться

Попробовав разные решения, я пришел на этот сайт. Чжоу Юн показывает, как программно расширить все узлы TreeView. В его методе есть две основные идеи:

  • ContainerFromItem вернет контейнер только в том случае, если элемент является прямым дочерним элементом этого элемента. В TreeView это означает, что будет возвращен только дочерний контейнер первого уровня, и вы должны вызвать ContainerFromItem для дочернего TreeViewItem, чтобы получить контейнер с следующего уровня
  • . Для ContainerFromItem для работы с TreeViewItem должны создаваться визуальные дети, и это происходит только тогда, когда TreeViewItem расширен. Это означает, что для выбора TreeViewItem все элементы, предшествующие требуемому элементу, должны быть расширены. На практике это означает, что нам нужно будет указать путь к элементу, который мы хотим выбрать, а не только к элементу.

Вот код, в который я попал

public static void SelectItem(this ItemsControl parentContainer, List<object> path)
{
    var head = path.First();
    var tail = path.GetRange(1, path.Count - 1);
    var itemContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(head) as TreeViewItem;

    if (itemContainer != null && itemContainer.Items.Count == 0)
    {
        itemContainer.IsSelected = true;

        var selectMethod = typeof(TreeViewItem).GetMethod("Select", BindingFlags.NonPublic | BindingFlags.Instance);
        selectMethod.Invoke(itemContainer, new object[] { true });
    }
    else if (itemContainer != null)
    {
        itemContainer.IsExpanded = true;

        if (itemContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
        {
            itemContainer.ItemContainerGenerator.StatusChanged += delegate
            {
                SelectItem(itemContainer, tail);
            };
        }
        else
        {
            SelectItem(itemContainer, tail);
        }
    }
}
4
ответ дан Sergej Andrejev 28 August 2018 в 06:37
поделиться
  • 1
    Кажется, что я должен иметь элемент, который я хочу выбрать. Однако обычно этот объект недоступен; у вас просто есть идентификатор этого объекта, верно? Я не уверен, как вызвать ContainerFromItem без фактического экземпляра элемента. – O. R. Mapper 6 December 2012 в 23:27
Другие вопросы по тегам:

Похожие вопросы: