c#/WPF 产品配置器/可拖动项

c#/WPF product configurator/ draggable items

我目前正在使用 C#/WPF 构建产品配置器。该产品类似于定制的插座条。它由长度可变的铝型材制成。您可以选择插座并沿铝型材放置。

我们已经有了数据模型。我们有 class "profile" 具有 "length" 等属性,class "items" 具有 "position"、"type" 等属性

我做了一个可视化数据的函数。我有一个 MainCanvas,我在其中将轮廓绘制为矩形,并且我正在使用 属性 "width"。属于该产品的项目在列表中。我正在使用 for 循环绘制项目,为列表中的每个插件插入一个 wpf "items"。

当我更改数据时,我必须清除 canvas 并重新绘制。我认为下一步会将 class 的变量绑定到 WPF 属性,例如插头的位置或配置文件的长度。

稍后,插头应该 select/dragable 来改变它们的位置(例如,在 5mm 的网格中)。那是我被困的地方。我知道我可以 select 并通过 "OnLeftMouseButton" 等操作拖动插头。现在的问题是:我应该把插头放在什么类型的容器里?

我想我可以将插件的 WPF 代码包装在 UserControl 中。这是正确的方法吗?据我所知,我可以使用 Hittest 创建一个 UserControl selectable。 Hittestresult 将被放入列表 selected。为了显示 selection,我可以使用 UserControl 的 Borderthickness/Brush。对于拖动,我可以使用 ManupilationDelta 更改 Position(并更改绑定的 Position-Variable)。因为插头的数量是可变的,所以我必须从 C# 代码生成用户控件。 我知道使用 UserControls 实现 Hittest 并不容易,因为它们不是真正的 "visible".

我是 C# 的新手,我很难在 Internet 上找到有类似问题或项目的人,这可能是因为我在搜索错误的词。我的假设是否正确?您会为此使用什么 WPF 控件?

我使用了 this answer 并添加了一些修改。

我使用 ItemControl 创建了包含不同对象的视图。这样,您只需将对象添加到列表即可添加图像。容器是 Canvas,但它可以是任何东西,因为位置由 RenderTransform 控制:

<ItemsControl Name="MainView" ItemsSource="{Binding ListObjects}">
    <ItemsControl.ItemsPanel >
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:MVDragableObject}">
            <local:DragableObject/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

这是后面的代码。一些基本的模型视图:

public class MVObjectManager
{
    public ObservableCollection<MVDragableObject> ListObjects { get; set; }
    public MVObjectManager()
    {
        ListObjects = new ObservableCollection<MVDragableObject>();
    }
}
public class MVDragableObject
{
}

以及填充和绑定容器的代码。你可以注意到我在集合中添加了 3 个项目:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        MVObjectManager Manager=new MVObjectManager();
        Manager.ListObjects.Add(new MVDragableObject());
        Manager.ListObjects.Add(new MVDragableObject());
        Manager.ListObjects.Add(new MVDragableObject());
        MainView.DataContext  = Manager;
    }
}

我定义的很简单UserControl。由您来定制它:

<UserControl x:Class="StackFill.DragableObject"
         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" 
         mc:Ignorable="d">
<Grid>
    <Rectangle Fill="Red" Height="30" Width="30"/>
</Grid>

</UserControl>

下面是管理拖放行为的代码:

public partial class DragableObject : UserControl
{
    public DragableObject()
    {
        InitializeComponent();
        this.MouseLeftButtonDown += new MouseButtonEventHandler(DragableObject_MouseLeftButtonDown);
        this.MouseLeftButtonUp += new MouseButtonEventHandler(DragableObject_MouseLeftButtonUp);
        this.MouseMove += new MouseEventHandler(DragableObject_MouseMove);
    }

    protected bool isDragging;
    private Point clickPosition;

    private void DragableObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        isDragging = true;
        var draggableControl = sender as UserControl;
        clickPosition = e.GetPosition(this.Parent as UIElement);
        draggableControl.CaptureMouse();
    }

    private void DragableObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        isDragging = false;
        var draggable = sender as UserControl;
        draggable.ReleaseMouseCapture();
    }

    private void DragableObject_MouseMove(object sender, MouseEventArgs e)
    {
        var draggableControl = sender as UserControl;

        if (isDragging && draggableControl != null)
        {
            Point currentPosition = e.GetPosition(this.Parent as UIElement);

            var transform = draggableControl.RenderTransform as TranslateTransform;
            if (transform == null)
            {
                transform = new TranslateTransform();
                draggableControl.RenderTransform = transform;
            }

            transform.X = snapPosition(currentPosition.X - clickPosition.X, 10);
            transform.Y = snapPosition(currentPosition.Y - clickPosition.Y, 10);
        }
    }

    private double snapPosition(double position, double gridSize)
    {
        return (Math.Truncate(position / gridSize) * gridSize);
    }
}

您可以通过更改 gridSize 参数来控制对齐精度。