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 参数来控制对齐精度。
我目前正在使用 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 参数来控制对齐精度。