Перед вызовом метода экземпляра или переменной экземпляра ему нужен объект (экземпляр). Когда экземпляр переменной вызывается из статического метода, компилятор не знает, к какому объекту принадлежит эта переменная. Поскольку статические методы не имеют объекта (только одна копия всегда). Когда вы вызываете переменную экземпляра или методы экземпляра из метода экземпляра, она ссылается на объект this
. Это означает, что переменная принадлежит к любому объекту, созданному, и каждый объект имеет свою собственную копию методов и переменных экземпляра.
Статические переменные отмечены как static
, а переменные экземпляра не имеют определенного ключевого слова.
Другой вариант - использовать привязку. Если у вас есть объект, с которым вы используете привязку, чтобы получить текст каждого 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
или рухнул.
Очень поздно для вечеринки с моим ответом, но для тех, кто хочет получить чистое решение 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();
}
}
В моем случае (у меня была такая же проблема), но было неприемлемо использовать привязку к свойству 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;
}
Да, метод ContainerFromItem ничего не возвращает, даже если вы вызываете его из прямого родительского TreeViewItem.
Возможно, вам придется немного переделать. Если вы создадите все как явный TreeViewItem, вы сможете сохранить ссылку на него и установить IsSelected на нем.
Попробовав разные решения, я пришел на этот сайт. Чжоу Юн показывает, как программно расширить все узлы TreeView. В его методе есть две основные идеи:
Вот код, в который я попал
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);
}
}
}
ContainerFromItem
без фактического экземпляра элемента.
– O. R. Mapper
6 December 2012 в 23:27
Cannot set read-only property ''.
. – Navid Farhadi 21 July 2011 в 08:56