【问题标题】:Easy Drag Drop implementation MVVMEasy Drag Drop 实现 MVVM
【发布时间】:2020-07-19 18:12:48
【问题描述】:

我是 MVVM 的新手,我目前正在尝试将拖放功能添加到我的应用程序中。问题是我已经在代码隐藏中开发了接口,但我现在正尝试将代码重新写入 MVVM,因为我只是在项目的开始阶段。

这里是上下文:用户将能够将框(ToggleButton,但它可能会更改)添加到网格,有点像棋盘。下面是我正在处理的视图模型:

<Page.Resources>
    <Style TargetType="{x:Type local:AirportEditionPage}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Page}">

                    <!-- The page content-->
                    <Grid>

                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="{Binding ToolKitWidth, FallbackValue=50}" />
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="{Binding RightPanelWidth, FallbackValue=400}"/>
                        </Grid.ColumnDefinitions>

                        <!-- The airport grid where Steps and Links are displayed -->
                        <ScrollViewer Grid.ColumnSpan="4" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
                            <Viewbox Height="{Binding AirportGridHeight}" Width="{Binding AirportGridWidth}" RenderOptions.BitmapScalingMode="HighQuality">
                                <ItemsControl x:Name="ChessBoard" ItemsSource="{Binding Items}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <Canvas Width="{Binding CardQuantityRow}" Height="{Binding CardQuantityColumn}" Background="{StaticResource AirportGridBackground}"/>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <Grid Width="1" Height="1">
                                                <ToggleButton Style="{StaticResource StepCardContentStyle}"/>
                                            </Grid>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                    <ItemsControl.ItemContainerStyle>
                                        <Style>
                                            <Setter Property="Canvas.Left" Value="{Binding Pos.X}"/>
                                            <Setter Property="Canvas.Top" Value="{Binding Pos.Y}" />
                                        </Style>
                                    </ItemsControl.ItemContainerStyle>
                                </ItemsControl>
                            </Viewbox>
                        </ScrollViewer>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

项目基本上来自具有名称、图标和位置(点)的类(INotifiedPropertyChanged 的​​子级)。

现在,我试图让用户能够将框 (ToggleButton) 拖放到他/她想要的任何地方。但是,我对命令、附加属性等完全迷失了。我整天都在学习教程并尝试拖放解决方案,但由于知识贫乏,我不知道如何将所有这些应用到我的代码中。

在我的代码隐藏版本中,这很容易。当左键单击按钮时,我对网格的一个变量说“嘿,我正在被拖放”。当用户移动时,我更改了 Item 坐标,当用户松开左键(左键向上)时,dragdrop_object 变量再次变为 null。

在 MVVM 的框架中,我完全迷失了。你能给我一些曲目来帮助我度过难关吗?我本来打算放弃 MVVM 很多时间,但我知道最好跟上,即使每个小功能都需要几个小时才能实现(它应该随着时间的推移而减少......)。

如果您需要更多详细信息来回答我的问题,请不要犹豫。

【问题讨论】:

  • MVVM 基本上意味着将视图与模型分离并通过视图模型连接它们。此拖放看起来像您提到的由 XAML 文件的代码隐藏管理的视图,这就是 MVVM。如果拖放应该影响模型,则视图(XAML+代码隐藏)可以访问视图模型。因此,在代码隐藏中获取视图模型并进行更改。
  • 感谢您的回复 Sorush !因此,如果我理解正确,即使它与我的模型(这里是我的项目)交互,在代码隐藏中制作它也不反对 MVVM。当我们开始在 MVVM 世界中区分什么可以在代码隐藏中实现和什么不能实现时,这并不容易。此外,我阅读了有关在 ViewModel 中进行拖放的文章,所以这可能会让我很困惑。
  • 是的,XAML 和代码隐藏都是视图。有些东西使用代码隐藏更容易,比如复杂的视图逻辑,而有些东西使用 XAML 更容易,比如将视图元素绑定到视图模型属性。我强调视图只知道视图模型(而不是模型)。 View-model 只知道模型。该模型对其他人一无所知。基本上,模型是可移植的代码,您可以在其他地方使用它,例如在 Web 应用程序中。为了区别,将您的应用程序编写在执行所有逻辑的控制台应用程序中,创建用户期望看到的视图,并将它们与视图模型连接。
  • 再次感谢您提供所有这些详细信息。它对我有很大帮助。问题是我已经在控制台应用程序中研究了一个巨大的算法。我确实知道在连接两个应用程序之前构建界面,我有点迷茫,但我开始看得更清楚了。最初,我还使用完整的代码隐藏(0 绑定)创建了接口(其中很大一部分),每个都运行良好,但我发现 MVVM 是一个更好的架构。我现在正试图将代码迁移到这种架构,但很痛苦。我会尽我所能,并牢记您的宝贵建议。
  • 一般来说MVVM不是代码隐藏,而是业务逻辑和视图的分离。在 C#(或代码隐藏)中实现某些逻辑并没有错。处理 UI 事件是一种需要一些 C# 的方案,但无法使用 EventTrigger 处理它们。 Patterns - WPF Apps With The Model-View-ViewModel Design Pattern。您不必将ItemsControl 包装成ViewBox

标签: c# wpf mvvm


【解决方案1】:

我在这里找到了解决方案:Move items in a canvas using MVVM 和这里:Combining ItemsControl with draggable items - Element.parent always null

确切地说,这是我添加的代码:

public class DragBehavior
{
    public readonly TranslateTransform Transform = new TranslateTransform();
    private static DragBehavior _instance = new DragBehavior();
    public static DragBehavior Instance
    {
        get { return _instance; }
        set { _instance = value; }
    }

    public static bool GetDrag(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsDragProperty);
    }

    public static void SetDrag(DependencyObject obj, bool value)
    {
        obj.SetValue(IsDragProperty, value);
    }

    public static readonly DependencyProperty IsDragProperty =
      DependencyProperty.RegisterAttached("Drag",
      typeof(bool), typeof(DragBehavior),
      new PropertyMetadata(false, OnDragChanged));

    private static void OnDragChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        // ignoring error checking
        var element = (UIElement)sender;
        var isDrag = (bool)(e.NewValue);

        Instance = new DragBehavior();
        ((UIElement)sender).RenderTransform = Instance.Transform;

        if (isDrag)
        {
            element.MouseLeftButtonDown += Instance.ElementOnMouseLeftButtonDown;
            element.MouseLeftButtonUp += Instance.ElementOnMouseLeftButtonUp;
            element.MouseMove += Instance.ElementOnMouseMove;
        }
        else
        {
            element.MouseLeftButtonDown -= Instance.ElementOnMouseLeftButtonDown;
            element.MouseLeftButtonUp -= Instance.ElementOnMouseLeftButtonUp;
            element.MouseMove -= Instance.ElementOnMouseMove;
        }
    }

    private void ElementOnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        ((UIElement)sender).CaptureMouse();
    }

    private void ElementOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        ((UIElement)sender).ReleaseMouseCapture();
    }

    private void ElementOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
    {
        FrameworkElement element = sender as FrameworkElement;
        Canvas parent = element.FindAncestor<Canvas>();
        var mousePos = mouseEventArgs.GetPosition(parent);
        if (!((UIElement)sender).IsMouseCaptured) return;
        if (mousePos.X < parent.Width && mousePos.Y < parent.Height && mousePos.X >= 0 && mousePos.Y >=0)
            ((sender as FrameworkElement).DataContext as Step).Pos = new System.Drawing.Point(Convert.ToInt32(Math.Floor(mousePos.X)), Convert.ToInt32((Math.Floor(mousePos.Y))));
    }


}

我的 DataTemplate 现在是:

<DataTemplate>
     <ContentControl Height="1" Width="1" local:DragBehavior.Drag="True" Style="{StaticResource StepCardContentControl}"/>
</DataTemplate>

我在一个专用文件中添加了 FindAncestor 静态类,如下所示:

public static class FindAncestorHelper
{
    public static T FindAncestor<T>(this DependencyObject obj)
    where T : DependencyObject
    {
        DependencyObject tmp = VisualTreeHelper.GetParent(obj);
        while (tmp != null && !(tmp is T))
        {
            tmp = VisualTreeHelper.GetParent(tmp);
        }
        return tmp as T;
    }
}

(我的项目现在是 ContentControls)。 由于项目在画布中的位置直接使用它们的 Pos 变量(Canvas.SetLeft 和 Canvas.SetTop 基于 Pos(Pos.X 和 Pos.Y)和绑定)进行管理,我只是根据画布中的 MousePosition 更新它.

另外,正如评论中所建议的,我会看看是否有比我正在使用的 ScrollViewer 和 Viewbox 更好的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 2010-09-15
    • 1970-01-01
    相关资源
    最近更新 更多