从 WPF 行为向 ViewModel 发送消息

Send message from WPF behavior to the ViewModel

之后,我现在可以通过行为拖动 canvas 中的项目。

问题是项目新坐标计算是在行为内部完成的,我不确定如何在不在视图中写入任何内容的情况下将它们发送到项目的 ViewModel。

我的 MainViewModel 有一个 Items 集合:

public BindableCollection<ItemViewModel> Items { get; set; }

ItemViewModel 如下所示:

[ImplementPropertyChanged]
public class ItemViewModel
{
    private readonly IEventAggregator events;
    private Random r = new Random();

    public ItemViewModel(IEventAggregator events)
    {
        this.events = events;
        this.events.Subscribe(this);

        // default values
        this.BackgroundColor = this.RandomColor();
        this.Width = 100;
        this.Height = 100;
        this.X = 10;
        this.Y = 10;
    }

    public Brush BackgroundColor { get; set; }
    public string Content { get; set; }
    public int Height { get; set; }
    public int Width { get; set; }
    public double X { get; set; }
    public double Y { get; set; }

    /// <summary>
    /// 
    /// </summary>
    private SolidColorBrush RandomColor()
    {
        var properties = typeof(Brushes).GetProperties();
        var count = properties.Count();
        var color = properties.Select(x => new { Property = x, Index = this.r.Next(count) })
                              .OrderBy(x => x.Index)
                              .First();

        return (SolidColorBrush)color.Property.GetValue(color, null);
    }
}

我的 DragBehavior 目前只是 this code snippet 的 copy/paste。

最后,我的 MainView 显示这样的项目(我使用 Caliburn.Micro 通过 x:Name 绑定项目):

<ItemsControl x:Name="Items">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Path=X}" />
            <Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
            <Setter Property="Width" Value="{Binding Path=Width}" />
            <Setter Property="Height" Value="{Binding Path=Height}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Background="{Binding Path=BackgroundColor}"
                behaviors:DragBehavior.Drag="True">
                <!-- contents -->
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

我正在考虑按照以下方式添加一些内容:

<Border ...
    behaviors:DragBehavior.OnDropped="{Binding Dropped}">

并且在 ItemViewModel 中:

public void Dropped(Point newCoordinates) {
  // change the X and Y of the viewmodel
}

但我不知道从哪里开始,甚至不知道如何称呼它。行动 ?扳机 ?它似乎与我要查找的内容无关。

Gong DragDropBehavior 库有一个系统,项目实现了一个接口并且行为运行正确的方法,但我不太明白它是如何工作的(这是我第一次使用行为)。

到目前为止,这是我使用的方法(灵感来自 Gong 的工作原理)。

基本上,视图绑定到视图模型本身,它应该实现一个接口。行为检索此绑定,并通过绑定元素调用接口方法。

我有一个 GitHub 帐户,其中包含此代码:https://github.com/cosmo0/DragSnap(在撰写本文时尚未更新)。

该行为有一个新的 DropHandler 属性 :

    public static readonly DependencyProperty DropHandlerProperty =
        DependencyProperty.RegisterAttached(
            "DropHandler",
            typeof(IDropHandler),
            typeof(DragOnCanvasBehavior),
            new PropertyMetadata(OnDropHandlerChanged));

    private IDropHandler DropHandler { get; set; }

    public static IDropHandler GetDropHandler(UIElement target)
    {
        return (IDropHandler)target.GetValue(DropHandlerProperty);
    }

    public static void SetDropHandler(UIElement target, IDropHandler value)
    {
        target.SetValue(DropHandlerProperty, value);
    }

    private static void OnDropHandlerChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        // Initialize variables and assign handlers
        // see original code in Github linked in original question

        // Additionnaly, initialize the DropHandler :
        var handler = (IDropHandler)(e.NewValue);
        Instance.DropHandler = handler;
    }

    private void ElementOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        var element = (UIElement)sender;
        element.ReleaseMouseCapture();

        // this is the main change
        if (this.DropHandler != null)
        {
            this.DropHandler.Dropped();
        }
    }

    private void ElementOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
    {
        // TODO : do some calculations

        this.DropHandler.Moved(x, y);
    }

我的 ItemViewModel 实现 IDropHandler,以及 DroppedMoved 方法:

    public void Dropped()
    {
        this.events.PublishOnUIThread(new ItemDroppedEvent(this.X, this.Y, this.Width, this.Height, this.ID));
    }

    public void Moved(double x, double y)
    {
        this.X = x;
        this.Y = y;
    }

视图绑定到视图模型本身:

<Border
    behaviors:DragOnCanvasBehavior.DropHandler="{Binding}">

我在处理拖动的元素位置时仍然遇到问题,但实际问题(从行为向视图模型发送消息)已得到解答。