需求:拖拽到Canvas下生成一个新的Border,这个Border可以在Canvas内拖拽,该Border通过右键菜单将其删除。
思路:Canvas动态加载ItemsControl,根据Drop捕获到的信息添加到ItemsControl绑定的ProcessButtonItems中,根据ButtonItems内的信息生成Border,Border绑定拖拽Behavior,嵌套MenuItem的Click事件动态删除ProcessButtonItems
问题:Border的Command事件无法绑定
报错提示:找不到源: RelativeSource FindAncestor, AncestorType='MyVisionWpfDemo.Views.CanvasFlowProcessView', AncestorLevel='1'。 D:\MyCode\MyVisionWpf\MyVisionWpfDemo\Views\CanvasFlowProcessView.xaml 35 MyVisionWpfDemo
xaml代码段(CanvasFlowProcessView.xaml)
<UserControl x:Class="MyVisionWpfDemo.Views.CanvasFlowProcessView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:MyVisionWpfDemo.Views"
xmlns:UserResources="clr-namespace:MyVisionWpfDemo.Resources"
xmlns:UserBehaviors="clr-namespace:MyVisionWpfDemo.Behaviors"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<prism:InvokeCommandAction Command="{Binding Canvas_Drop}">
<!--Prism框架传递的参数必须是从Prism实例化的-->
</prism:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<ItemsControl ItemsSource="{Binding ProcessButtonItems}" Background="Green" AllowDrop="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<UserResources:UserFlowProcessBorder Background="Aquamarine" Canvas.Left="{Binding PositionX}" Canvas.Top="{Binding PositionY}" Content="{Binding Name}">
<i:Interaction.Behaviors>
<UserBehaviors:UserBorderBehavior/>
</i:Interaction.Behaviors>
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<!--无法绑定成功-->
<MenuItem Header="Delete 1" Command="{Binding DataContext.Border_Delete, RelativeSource={RelativeSource AncestorType={x:Type local:CanvasFlowProcessView}}}"/>
</ContextMenu>
</Grid.ContextMenu>
<Label Content="{Binding Name}"/>
</Grid>
</UserResources:UserFlowProcessBorder>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
View代码段(CanvasFlowProcessView.xaml.cs)
namespace MyVisionWpfDemo.Views
{
/// <summary>
/// CanvasFlowProcessView.xaml 的交互逻辑
/// </summary>
public partial class CanvasFlowProcessView : UserControl
{
public CanvasFlowProcessView(IEventAggregator eventAggregator, IDialogService dialogService, IRegionManager regionManager)
{
InitializeComponent();
this.DataContext = new CanvasFlowProcessViewModel(eventAggregator, dialogService, regionManager);
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
//未遵守MVVM原则
}
}
}
ViewModel代码段(CanvasFlowProcessViewModel.cs)
namespace MyVisionWpfDemo.ViewModels
{
public class InstanceItem : BindableBase
{
public int ID { get; set; } = 1;
#region 属性
private string _typeName;
public string TypeName
{
get { return _typeName; }
set { SetProperty(ref _typeName, value); }
}
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
private bool _isLeftDown;
public bool IsLeftDown
{
get { return _isLeftDown; }
set { SetProperty(ref _isLeftDown, value); }
}
private double _positionX;
public double PositionX
{
get { return _positionX; }
set { SetProperty(ref _positionX, value); }
}
private double _positionY;
public double PositionY
{
get { return _positionY; }
set { SetProperty(ref _positionY, value); }
}
#endregion
public InstanceItem(string typeName, string name)
{ TypeName = typeName; Name = name; }
public InstanceItem(InstanceItem itemValue)
{ TypeName = itemValue.TypeName; Name = itemValue.Name; }
}
public class CanvasFlowProcessViewModel : BindableBase
{
// 在 ViewModel 中定义一个按键集合【string为View名称】——不支持多线程
public ObservableCollection<InstanceItem> ProcessButtonItems { get; set; } = new ObservableCollection<InstanceItem> { };
#region 命令
private DelegateCommand<object> border_Delete;
public DelegateCommand<object> Border_Delete =>
border_Delete ?? (border_Delete = new DelegateCommand<object>(ExecuteBorder_Delete, CanExecuteBorder_Delete));
void ExecuteBorder_Delete(object parameter)
{
}
bool CanExecuteBorder_Delete(object parameter)
{
return true;
}
private DelegateCommand<object> canvas_Drop;
public DelegateCommand<object> Canvas_Drop =>
canvas_Drop ?? (canvas_Drop = new DelegateCommand<object>(ExecuteCanvas_Drop, CanExecuteCanvas_Drop));
void ExecuteCanvas_Drop(object parameter)
{
Debug.WriteLine("Canvas_Drop triggered");
string[] strParasTypeNames = { "DragEventArgs" };
string type = parameter.GetType().ToString();
//判断传递过来的paras是否为指定类型
if (!(strParasTypeNames.All(type => parameter.GetType().ToString().Contains(type))))
return;
UserProcessThumb subject = ((DragEventArgs)parameter).Data.GetData(typeof(UserProcessThumb)) as UserProcessThumb;
string TypeName = subject?.TypeName;
if (TypeName != null)
{
InstanceItem instance = new InstanceItem(TypeName, TypeName);
var sourceElement = ((DragEventArgs)parameter).OriginalSource as UIElement;
// 获取鼠标相对于源元素的位置
Point position = ((DragEventArgs)parameter).GetPosition(sourceElement);
// View中的控件坐标绑定instance中的X与Y
instance.PositionX = Math.Max(0, position.X);
instance.PositionY = Math.Max(0, position.Y);
if (!ProcessButtonItems.Contains(instance))
ProcessButtonItems.Add(instance);
else
{
instance.Name = type;
ProcessButtonItems.Add(instance);
}
}
}
bool CanExecuteCanvas_Drop(object parameter)
{
return true;
}
#endregion
#region 初始化
/// <summary>
/// Prism事件聚合器
/// </summary>
public IEventAggregator _ea;
private IDialogService _ds;
private IRegionManager _rm;
public CanvasFlowProcessViewModel(IEventAggregator eventAggregator, IDialogService dialogService, IRegionManager regionManager)
{
_ea = eventAggregator;
_ds = dialogService;
_rm = regionManager;
}
#endregion
}
}