将消息从WPF行为发送到ViewModel

时间:2015-01-14 14:22:24

标签: c# wpf mvvm triggers action

my previous question之后,我现在可以通过行为拖动画布中的项目。

问题是项目新坐标计算是在行为内部完成的,我不知道如何在不在视图中写任何内容的情况下将它们发送到项目的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>
    /// https://stackoverflow.com/a/11282427/6776
    /// </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的复制/粘贴。

最后,我的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库有一个系统,项目实现一个界面,行为运行正确的方法,但我真的不明白它是如何工作的(这是我第一次工作有行为)。

1 个答案:

答案 0 :(得分:0)

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

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

我有一个使用此代码的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;
    }

视图绑定了viewmodel本身:

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

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