TreeView与拖放和HierarchicalDataTemplate和其他控制元素作为TreeViewItem
本文关键字:元素 TreeViewItem 控制 其他 拖放 HierarchicalDataTemplate TreeView | 更新日期: 2023-09-27 18:18:17
我试图得到一个TreeView工作,它使用HierarchialDataTemplate绑定节点到ObservableCollection。TreeViewItems是带有复选框和组合框的网格。所有这些都有点工作,但我没有设法让拖放功能工作。
代码基于TreeView, HierarchicalDataTemplate和递归数据和http://www.codeproject.com/Articles/55168/Drag-and-Drop-Feature-in-WPF-TreeView-Control
<Window x:Class="TreeView_HierarchicalDataTemplate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:TreeView_HierarchicalDataTemplate="clr-namespace:TreeView_HierarchicalDataTemplate"
Title="MainWindow" x:Name="mainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>
<Grid>
<TreeView Name="TreeView_After" AllowDrop="True" DataContext="{Binding ElementName=mainWindow, Path=TreeModel}" ItemsSource="{Binding Items}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="TreeViewItem.DragOver" Handler="TreeView_After_DragOver"/>
<EventSetter Event="TreeViewItem.Drop" Handler="TreeView_After_Drop"/>
<EventSetter Event="TreeViewItem.MouseMove" Handler="TreeView_After_MouseMove"/>
<EventSetter Event="TreeViewItem.MouseDown" Handler="TreeView_After_MouseDown"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type TreeView_HierarchicalDataTemplate:NodeViewModel}" ItemsSource="{Binding Children}">
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}"></TextBlock>
<GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
<CheckBox Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="1" Margin="5,5,0,0">activated</CheckBox>
<GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
<StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
<TextBlock Margin="5">Action:</TextBlock>
<ComboBox>
<TextBlock>Move To</TextBlock>
</ComboBox>
<TextBlock Margin="5">C:''Videos'Folder'aVideo.mkv</TextBlock>
</StackPanel>
<GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="1" Width="5"/>
<StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="3" Orientation="Horizontal">
<TextBlock Margin="5">Duplicate of:</TextBlock>
<ComboBox>
<TextBlock>None</TextBlock>
</ComboBox>
</StackPanel>
</Grid>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
这是Observable Collection
public class TreeViewModel
{
public ObservableCollection<NodeViewModel> Items { get; set; }
}
public class NodeViewModel : UIElement
{
public NodeViewModel()
{
Show = true;
}
public string Id { get; set; }
public string Name { get; set; }
public bool Show { get; set; }
public ObservableCollection<NodeViewModel> Children { get; set; }
}
但是这个Mouse down事件不起作用
private void TreeView_After_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
_lastMouseDown = e.GetPosition(TreeView_After);
}
}
另外,如果我从UIElement派生NodeViewModel,我只会从元素(UIElement)到容器(NodeViewModel)进行类型转换。但是,如果我从UIElement中派生出NodeViewModel, TreeView就不再显示了:(
private NodeViewModel GetNearestContainer(UIElement element)
{
// Walk up the element tree to the nearest tree view item.
NodeViewModel container = element as NodeViewModel;
while ((container == null) && (element != null))
{
element = VisualTreeHelper.GetParent(element) as UIElement;
container = element as NodeViewModel;
}
return container;
}
我得到了Rohit Vats的一些帮助,像这样:
MainWindow.xaml:
<Window x:Class="TreeView_HierarchicalDataTemplate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:TreeView_HierarchicalDataTemplate="clr-namespace:TreeView_HierarchicalDataTemplate"
Title="MainWindow" x:Name="mainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>
<Grid>
<TreeView Name="TreeView_After" AllowDrop="True" DataContext="{Binding ElementName=mainWindow, Path=TreeModel}" ItemsSource="{Binding Items}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="TreeViewItem.DragOver" Handler="TreeView_After_DragOver"/>
<EventSetter Event="TreeViewItem.Drop" Handler="TreeView_After_Drop"/>
<EventSetter Event="TreeViewItem.MouseMove" Handler="TreeView_After_MouseMove"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type TreeView_HierarchicalDataTemplate:NodeViewModel}" ItemsSource="{Binding Children}">
<Grid Background="LightBlue" MouseDown="TreeView_After_MouseDown">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}"></TextBlock>
<GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
<CheckBox Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="1" Margin="5,5,0,0">activated</CheckBox>
<GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
<StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
<TextBlock Margin="5">Action:</TextBlock>
<ComboBox>
<TextBlock>Move To</TextBlock>
</ComboBox>
<TextBlock Margin="5">C:''Videos'Folder'aVideo.mkv</TextBlock>
</StackPanel>
<GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="1" Width="5"/>
<StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="3" Orientation="Horizontal">
<TextBlock Margin="5">Duplicate of:</TextBlock>
<ComboBox>
<TextBlock>None</TextBlock>
</ComboBox>
</StackPanel>
</Grid>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
TreeViewModel:
public class TreeViewModel
{
public ObservableCollection<NodeViewModel> Items { get; set; }
}
public class NodeViewModel
{
public NodeViewModel()
{
Show = true;
}
public string Id { get; set; }
public string Name { get; set; }
public bool Show { get; set; }
public ObservableCollection<NodeViewModel> Children { get; set; }
}
主窗口代码:
public partial class MainWindow : Window
{
TreeViewModel TreeViewModel_After;
public MainWindow()
{
TreeViewModel_After = new TreeViewModel();
TreeViewModel_After.Items = new ObservableCollection<NodeViewModel>{
new NodeViewModel { Name = "Root", Children = new ObservableCollection<NodeViewModel> {
new NodeViewModel { Name = "Level1" , Children = new ObservableCollection<NodeViewModel>{
new NodeViewModel{ Name = "Level2"}}} } }};
InitializeComponent();
}
public TreeViewModel TreeModel
{
get
{
return TreeViewModel_After;
}
}
Point _lastMouseDown;
NodeViewModel draggedItem, _target;
private void TreeView_After_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
_lastMouseDown = e.GetPosition(TreeView_After);
}
}
private bool CheckGridSplitter(UIElement element)
{
if (element is GridSplitter)
{
return true;
}
GridSplitter GridSplitter = FindParent<GridSplitter>(element);
if (GridSplitter != null)
{
return true;
}
return false;
}
private void TreeView_After_MouseMove(object sender, MouseEventArgs e)
{
try
{
if (e.LeftButton == MouseButtonState.Pressed)
{
UIElement element = e.OriginalSource as UIElement;
bool bGridSplitter = CheckGridSplitter(element);
Point currentPosition = e.GetPosition(TreeView_After);
if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
(Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
{
draggedItem = (NodeViewModel)TreeView_After.SelectedItem;
if ( (draggedItem != null) && !bGridSplitter)
{
DragDropEffects finalDropEffect = DragDrop.DoDragDrop(TreeView_After, TreeView_After.SelectedValue,
DragDropEffects.Move);
//Checking target is not null and item is dragging(moving)
if ((finalDropEffect == DragDropEffects.Move) && (_target != null))
{
// A Move drop was accepted
if (draggedItem.Name != _target.Name)
{
CopyItem(draggedItem, _target);
_target = null;
draggedItem = null;
}
}
}
}
}
}
catch (Exception)
{
}
}
private void TreeView_After_DragOver(object sender, DragEventArgs e)
{
try
{
Point currentPosition = e.GetPosition(TreeView_After);
if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
(Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
{
// Verify that this is a valid drop and then store the drop target
NodeViewModel item = GetNearestContainer(e.OriginalSource as UIElement);
if (CheckDropTarget(draggedItem, item))
{
e.Effects = DragDropEffects.Move;
}
else
{
e.Effects = DragDropEffects.None;
}
}
e.Handled = true;
}
catch (Exception)
{
}
}
private void TreeView_After_Drop(object sender, DragEventArgs e)
{
try
{
e.Effects = DragDropEffects.None;
e.Handled = true;
// Verify that this is a valid drop and then store the drop target
NodeViewModel TargetItem = GetNearestContainer(e.OriginalSource as UIElement);
if (TargetItem != null && draggedItem != null)
{
_target = TargetItem;
e.Effects = DragDropEffects.Move;
}
}
catch (Exception)
{
}
}
private bool CheckDropTarget(NodeViewModel _sourceItem, NodeViewModel _targetItem)
{
//Check whether the target item is meeting your condition
bool _isEqual = false;
if(_sourceItem.Name != _targetItem.Name )
{
_isEqual = true;
}
return _isEqual;
}
private void CopyItem(NodeViewModel _sourceItem, NodeViewModel _targetItem)
{
//Asking user wether he want to drop the dragged TreeViewItem here or not
if (MessageBox.Show("Would you like to drop " + _sourceItem.Name + " into " + _targetItem.Name + "", "", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
try
{
_targetItem.Children.Add(_sourceItem);
}
catch (Exception)
{
}
}
}
private NodeViewModel GetNearestContainer(UIElement element)
{
// Walk up the element tree to the nearest tree view item.
TreeViewItem UIContainer = FindParent<TreeViewItem>(element);
NodeViewModel NVContainer = null;
if (UIContainer != null)
{
NVContainer = UIContainer.DataContext as NodeViewModel;
}
return NVContainer;
}
private static Parent FindParent<Parent>(DependencyObject child)
where Parent : DependencyObject
{
DependencyObject parentObject = child;
parentObject = VisualTreeHelper.GetParent(parentObject);
//check if the parent matches the type we're looking for
if (parentObject is Parent || parentObject == null)
{
return parentObject as Parent;
}
else
{
//use recursion to proceed with next level
return FindParent<Parent>(parentObject);
}
}
}