在文章开始之前先看一看效果图
我们可以拖拽一个"游戏"给ListBox,并且ListBox也能接受拖拽过来的数据, 但是我们不能拖拽一个"游戏类型"给它。
所以当拖拽开始发生的时候我们必须添加一些限制条件,以防止接受不正确的数据。
Item实体
CS
public class ItemModel : ViewModelBase{public string ItemName { get; set; }}
组实体
CS
public class GroupModel : ViewModelBase{/// <summary>/// 组名/// </summary>public string GroupName { get; set; }private int groupCount;/// <summary>/// 组数量/// </summary>public int GroupCount{get { return groupCount; }set { groupCount = value; base.RaisePropertyChanged("GroupCount"); }}/// <summary>/// 子类集合/// </summary>public ObservableCollection<ItemModel> ItemModelList { get; set; }}
给"游戏"实体创建一个模板
XAML
<HierarchicalDataTemplate x:Key="template_Item"><TextBlock Text="{Binding ItemName}"/> </HierarchicalDataTemplate>
给"游戏组"实体创建一个模板
XAML
<HierarchicalDataTemplate x:Key="template_Group" ItemsSource="{Binding ItemModelList}" ItemTemplate="{StaticResource template_Item}"><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding GroupName}"/><TextBlock Text="{Binding GroupCount}" Margin="5,0,0,0"/></StackPanel> </HierarchicalDataTemplate>
但是当我准备给TreeView赋值的时候 , 我想起来TreeView的SelectedItem属性不是依赖属性 , 它不支持Binding操作
所以只有自己写一个控件继承TreeView了。为它扩展一个MySelectedItem属性出来。并且重写SelectedItemChange事件
把TreeView的SelectedItem交给扩展的依赖属性MySelectedItem .这样在界面上就可以Binding选中项了
不过由于TreeView各个节点的数据实体可能类型不相同,所以扩展的属性只能定义为object类型
创建自定义树
CS
public class MyTreeView : TreeView{public MyTreeView(){}/// <summary>/// 自定义TreeView选中项,支持数据Binding/// </summary>public object MySelectItem{get { return GetValue(MySelectItemProperty); }set { SetValue(MySelectItemProperty, value); }}public static DependencyProperty MySelectItemProperty = DependencyProperty.Register("MySelectItem", typeof(object), typeof(MyTreeView));/// <summary>/// 当改变发生时,为自定义的SelectItem属性赋值/// </summary>/// <param name="e"></param>protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e){if (this.SelectedItem != null)this.MySelectItem = this.SelectedItem;base.OnSelectedItemChanged(e);}}
XAML
<mc:MyTreeView x:Name="myTree" MouseMove="TreeView_MouseMove" TextBlock.FontSize="14" MySelectItem="{Binding SelectGame,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding GroupSourceList}" ItemTemplate="{StaticResource template_Group}"> </mc:MyTreeView>
CS
private TreeViewItem ti = new TreeViewItem();private void TreeView_MouseMove(object sender, MouseEventArgs e){if (e.LeftButton == MouseButtonState.Pressed){if (myTree.SelectedItem == null)return;DragDrop.DoDragDrop(ti, sender, DragDropEffects.Move);}}
DragDrop.DoDragDrop方法需要传入一个DependencyObject对象以设置其拖拽时的效果。
但由于TreeView做了数据绑定, 所以它的SelectItem取出来是一个数据实体。而不是一个DependencyObject对象了。
所以我用了一个比较SB的办法就是new一个TreeViewItem。然后设置拖拽移动的效果。
创建ListBox
<ListBox ItemsSource="{Binding GameSourceList}" AllowDrop="true"><ListBox.ItemTemplate><DataTemplate><TextBlock Text="{Binding ItemName}"/></DataTemplate></ListBox.ItemTemplate><i:Interaction.Triggers><i:EventTrigger EventName="DragEnter"><Command:EventToCommand Command="{Binding DragEnterCommand}" PassEventArgsToCommand="True"/></i:EventTrigger><i:EventTrigger EventName="DragOver"><Command:EventToCommand Command="{Binding DragEnterCommand}" PassEventArgsToCommand="True"/></i:EventTrigger><i:EventTrigger EventName="Drop"><Command:EventToCommand Command="{Binding DropCommand}" PassEventArgsToCommand="True"/></i:EventTrigger></i:Interaction.Triggers></ListBox>
ViewModel
public class MainViewModel : ViewModelBase{public MainViewModel(){Init();}#region Properties/// <summary>/// 数据源/// </summary>public ObservableCollection<GroupModel> GroupSourceList { get; set; }/// <summary>/// 数据源/// </summary>public ObservableCollection<ItemModel> GameSourceList { get; set; }private object selectGame;/// <summary>/// 当前选中项/// </summary>public object SelectGame{get { return selectGame; }set{selectGame = value;base.RaisePropertyChanged("SelectGame");}}#endregion#region Methodsprivate void Init(){GameSourceList = new ObservableCollection<ItemModel>();GroupSourceList = new ObservableCollection<GroupModel>();GroupModel gp1 = new GroupModel();#region 模拟数据gp1.GroupName = "竞技游戏";gp1.ItemModelList = new ObservableCollection<ItemModel>();gp1.ItemModelList.Add(new ItemModel() { ItemName = "CS GO" });gp1.ItemModelList.Add(new ItemModel() { ItemName = "星际争霸2" });gp1.ItemModelList.Add(new ItemModel() { ItemName = "FIFA 14" });gp1.GroupCount = gp1.ItemModelList.Count;GroupModel gp2 = new GroupModel();gp2.GroupName = "网络游戏";gp2.ItemModelList = new ObservableCollection<ItemModel>();gp2.ItemModelList.Add(new ItemModel() { ItemName = "CS OnLine" });gp2.ItemModelList.Add(new ItemModel() { ItemName = "街头篮球" });gp2.ItemModelList.Add(new ItemModel() { ItemName = "完美世界" });gp2.GroupCount = gp2.ItemModelList.Count;GroupModel gp3 = new GroupModel();gp3.GroupName = "休闲游戏";gp3.ItemModelList = new ObservableCollection<ItemModel>();gp3.ItemModelList.Add(new ItemModel() { ItemName = "德州扑克" });gp3.ItemModelList.Add(new ItemModel() { ItemName = "街头篮球" });gp3.ItemModelList.Add(new ItemModel() { ItemName = "完美世界" });GroupSourceList.Add(gp1);GroupSourceList.Add(gp2);GroupSourceList.Add(gp3);gp3.GroupCount = gp3.ItemModelList.Count;#endregionDragEnterCommand = new RelayCommand<DragEventArgs>(DragEnter);DropCommand = new RelayCommand<DragEventArgs>(Drop);}private void DragEnter(DragEventArgs args){if (SelectGame.GetType() == typeof(ItemModel)) //如果拖拽的对象是"游戏"则接受之 {args.Effects = DragDropEffects.Move;System.Console.WriteLine("accept");}else{args.Effects = DragDropEffects.None; //否则拒绝接受拖拽System.Console.WriteLine("no accept");}args.Handled = true;}private void Drop(DragEventArgs args){GameSourceList.Add(SelectGame as ItemModel); //将接受到的"游戏"写入ListBox }#endregion#region Commandspublic ICommand DragEnterCommand { get; set; }public ICommand DropCommand { get; set; }#endregion}
到这里一个简单的拖拽就完成了。
QQ 3045568793 欢迎交流