如何将 WPF 树视图项的扩展事件绑定到视图模型
How to bind expanded event of WPF treeview item to the viewmodel
我正在尝试将展开的事件绑定到视图模型(不是 *.xaml.cs 文件)以仅在展开节点后扩展树视图。
我的做法是:
<TreeView x:Name="TreeViewTest" ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children, Mode=OneTime}">
<StackPanel>
<Label Content="{Binding Title, Mode=OneTime}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TreeViewItem.Expanded">
<i:InvokeCommandAction Command="{Binding DataContext.ExpandedCommand, Source={x:Reference TreeViewTest}}"> </i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
我收到以下错误消息:
Cannot call MarkupExtension.ProvideValue because of a cyclical dependency. Properties inside a MarkupExtension cannot reference objects that reference the result of the MarkupExtension. The affected MarkupExtensions are:
System.Windows.Data.Binding
谁能帮我解决这个错误,或者有其他方法可以将事件绑定到视图模型中的命令吗?
要创建到 TreeView 的绑定,您必须使用 RelativeSource Self,而不是使用 x:Reference 的 Source。
例如,我将 TreeView 的宽度绑定到高度:
<TreeView Name="tv" Width="{Binding Height, RelativeSource={RelativeSource Self}}">
</TreeView>
或者您也可以使用 ElementName,如下所示:
<TreeView Name="tv" Width="{Binding Height, ElementName=tv}">
</TreeView>
据我了解,您尝试将命令附加到 TreeViewItem.Expanded 事件。
我对这个问题做了一个小的研究,这是我最终得到的结果。
- 树视图项和此事件存在一个小问题。
- 有几种解决问题的方法:
首先是 TreeViewItem 样式中的 AttachedProperties,这里是 link:
Invoke Command when TreeViewItem is Expanded
其次是Behavior涉及的解决方案:
XAML:
<Grid>
<TreeView x:Name="TreeViewTest" ItemsSource="{Binding Items}" TreeViewItem.Expanded="TreeViewTest_OnExpanded">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type soTreeViewHelpAttempt:TreeObject}"
ItemsSource="{Binding Children, Mode=OneTime}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding }" />
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<StackPanel>
<Label Content="{Binding Title, Mode=OneTime}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<i:Interaction.Behaviors>
<soTreeViewHelpAttempt:TreeViewItemExpandBehavior OnExpandedAction="{Binding OnExpandedActionInViewModel}"/>
</i:Interaction.Behaviors>
</TreeView></Grid>
视图模型:
public MainDataContext()
{
OnExpandedActionInViewModel = new Action<object, RoutedEventArgs>(OnExpanded);
}
public Action<object, RoutedEventArgs> OnExpandedActionInViewModel
{
get { return _onExpandedActionInViewModel; }
private set
{
_onExpandedActionInViewModel = value;
OnPropertyChanged();
}
}
行为命令属性:
public static readonly DependencyProperty OnExpandedActionProperty = DependencyProperty.Register(
"OnExpandedAction", typeof (Action<object,RoutedEventArgs>), typeof (TreeViewItemExpandBehavior), new PropertyMetadata(default(Action<object,RoutedEventArgs>)));
public Action<object,RoutedEventArgs> OnExpandedAction
{
get { return (Action<object,RoutedEventArgs>) GetValue(OnExpandedActionProperty); }
set { SetValue(OnExpandedActionProperty, value); }
}
行为代码:
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObjectOnLoaded;
}
private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var treeView = sender as TreeView;
if(treeView == null) return;
treeView.ItemsSource.Cast<object>().ToList().ForEach(o =>
{
var treeViewItem = treeView.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem;
if(treeViewItem == null) return;
treeViewItem.Expanded += TreeViewItemOnExpanded;
_list.Add(treeViewItem);
});
}
private void TreeViewItemOnExpanded(object sender, RoutedEventArgs routedEventArgs)
{
if(OnExpandedAction == null)
return;
OnExpandedAction(sender, routedEventArgs);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObjectOnLoaded;
_list.ForEach(item => item.Expanded -= TreeViewItemOnExpanded);
_list.Clear();
}
希望对您有所帮助。
此致,
我知道这个问题很老了,但是有一个不需要任何自定义代码的明显解决方案。
当使用 i:EventTrigger
时,您可以指定对象来监听事件,如下所示,我使用 RelativeSource
绑定在可视化树中向上移动并找到 TreeViewItem
.
此外,您可以使用 ElementName
按名称绑定到元素,这不会导致循环依赖,因为当控件已经创建和初始化时绑定在调度程序上工作(不像 x:Reference
,必须在 XAML 反序列化期间解决)。
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" EventName="Expanded">
<i:InvokeCommandAction Command="{Binding ElementName=TreeView, Path=DataContext.ExpandedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
我正在尝试将展开的事件绑定到视图模型(不是 *.xaml.cs 文件)以仅在展开节点后扩展树视图。
我的做法是:
<TreeView x:Name="TreeViewTest" ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children, Mode=OneTime}">
<StackPanel>
<Label Content="{Binding Title, Mode=OneTime}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TreeViewItem.Expanded">
<i:InvokeCommandAction Command="{Binding DataContext.ExpandedCommand, Source={x:Reference TreeViewTest}}"> </i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
我收到以下错误消息:
Cannot call MarkupExtension.ProvideValue because of a cyclical dependency. Properties inside a MarkupExtension cannot reference objects that reference the result of the MarkupExtension. The affected MarkupExtensions are: System.Windows.Data.Binding
谁能帮我解决这个错误,或者有其他方法可以将事件绑定到视图模型中的命令吗?
要创建到 TreeView 的绑定,您必须使用 RelativeSource Self,而不是使用 x:Reference 的 Source。 例如,我将 TreeView 的宽度绑定到高度:
<TreeView Name="tv" Width="{Binding Height, RelativeSource={RelativeSource Self}}">
</TreeView>
或者您也可以使用 ElementName,如下所示:
<TreeView Name="tv" Width="{Binding Height, ElementName=tv}">
</TreeView>
据我了解,您尝试将命令附加到 TreeViewItem.Expanded 事件。 我对这个问题做了一个小的研究,这是我最终得到的结果。
- 树视图项和此事件存在一个小问题。
- 有几种解决问题的方法:
首先是 TreeViewItem 样式中的 AttachedProperties,这里是 link: Invoke Command when TreeViewItem is Expanded
其次是Behavior涉及的解决方案:
XAML:
<Grid> <TreeView x:Name="TreeViewTest" ItemsSource="{Binding Items}" TreeViewItem.Expanded="TreeViewTest_OnExpanded"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type soTreeViewHelpAttempt:TreeObject}" ItemsSource="{Binding Children, Mode=OneTime}"> <HierarchicalDataTemplate.ItemTemplate> <DataTemplate> <StackPanel> <Label Content="{Binding }" /> </StackPanel> </DataTemplate> </HierarchicalDataTemplate.ItemTemplate> <StackPanel> <Label Content="{Binding Title, Mode=OneTime}" /> </StackPanel> </HierarchicalDataTemplate> </TreeView.ItemTemplate> <i:Interaction.Behaviors> <soTreeViewHelpAttempt:TreeViewItemExpandBehavior OnExpandedAction="{Binding OnExpandedActionInViewModel}"/> </i:Interaction.Behaviors> </TreeView></Grid>
视图模型:
public MainDataContext() { OnExpandedActionInViewModel = new Action<object, RoutedEventArgs>(OnExpanded); } public Action<object, RoutedEventArgs> OnExpandedActionInViewModel { get { return _onExpandedActionInViewModel; } private set { _onExpandedActionInViewModel = value; OnPropertyChanged(); } }
行为命令属性:
public static readonly DependencyProperty OnExpandedActionProperty = DependencyProperty.Register( "OnExpandedAction", typeof (Action<object,RoutedEventArgs>), typeof (TreeViewItemExpandBehavior), new PropertyMetadata(default(Action<object,RoutedEventArgs>))); public Action<object,RoutedEventArgs> OnExpandedAction { get { return (Action<object,RoutedEventArgs>) GetValue(OnExpandedActionProperty); } set { SetValue(OnExpandedActionProperty, value); } }
行为代码:
protected override void OnAttached() { base.OnAttached(); AssociatedObject.Loaded += AssociatedObjectOnLoaded; } private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs) { var treeView = sender as TreeView; if(treeView == null) return; treeView.ItemsSource.Cast<object>().ToList().ForEach(o => { var treeViewItem = treeView.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem; if(treeViewItem == null) return; treeViewItem.Expanded += TreeViewItemOnExpanded; _list.Add(treeViewItem); }); } private void TreeViewItemOnExpanded(object sender, RoutedEventArgs routedEventArgs) { if(OnExpandedAction == null) return; OnExpandedAction(sender, routedEventArgs); } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.Loaded -= AssociatedObjectOnLoaded; _list.ForEach(item => item.Expanded -= TreeViewItemOnExpanded); _list.Clear(); }
希望对您有所帮助。 此致,
我知道这个问题很老了,但是有一个不需要任何自定义代码的明显解决方案。
当使用 i:EventTrigger
时,您可以指定对象来监听事件,如下所示,我使用 RelativeSource
绑定在可视化树中向上移动并找到 TreeViewItem
.
此外,您可以使用 ElementName
按名称绑定到元素,这不会导致循环依赖,因为当控件已经创建和初始化时绑定在调度程序上工作(不像 x:Reference
,必须在 XAML 反序列化期间解决)。
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" EventName="Expanded">
<i:InvokeCommandAction Command="{Binding ElementName=TreeView, Path=DataContext.ExpandedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>