吾门 2024-10-18 14:14 采纳率: 0%
浏览 47
已结题

Prism框架ItemControl元素绑定ViewModel中的命令失败

需求:拖拽到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
    }
}
  • 写回答

13条回答 默认 最新

  • 阿里嘎多学长 2024-10-18 14:15
    关注

    AIGC生成:


    Prism框架ItemControl元素绑定ViewModel中的命令失败

    你遇到了 Prism 框架中的 ItemControl 元素绑定 ViewModel 中的命令失败的问题。下面是解决方案:

    首先,需要在 ViewModel 中定义一个 ICommand 类型的属性,用于处理删除 Border 的命令:

    public class ViewModel : INotifyPropertyChanged
    {
        private ICommand _deleteCommand;
    
        public ICommand DeleteCommand
        {
            get { return _deleteCommand ?? (_deleteCommand = new RelayCommand(DeleteBorder)); }
        }
    
        private void DeleteBorder()
        {
            // 删除 Border 的逻辑
        }
    }
    

    然后,在 XAML 文件中,使用 DataTemplate 将 ItemControl 元素绑定到 ViewModel 中的 DeleteCommand 属性:

    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    
    <Canvas>
        <ItemsControl ItemsSource="{Binding Borders}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border Width="100" Height="100" Canvas.Left="0" Canvas.Top="0">
                        <Border.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="删除" Command="{Binding DeleteCommand}" />
                            </ContextMenu>
                        </Border.ContextMenu>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Canvas>
    

    在上面的代码中,我们使用 DataTemplate 将 Border 元素绑定到 ViewModel 中的 DeleteCommand 属性。这样,当用户右键点击 Border 时,会触发 DeleteCommand 命令。

    最后,在 ViewModel 中实现 DeleteBorder 方法,用于删除 Border:

    private void DeleteBorder()
    {
        // 删除 Border 的逻辑
        // 例如:
        Borders.Remove(Borders.FirstOrDefault(b => b == (Border)sender));
    }
    

    在上面的代码中,我们使用 LINQ 查询找到当前 Border 对象,然后从 Borders 集合中删除它。

    总的来说,这个解决方案使用 Prism 框架中的 ICommand 接口和 DataTemplate 将 ItemControl 元素绑定到 ViewModel 中的命令属性。


    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 10月18日
  • 创建了问题 10月18日