所以我正在开展一个项目,要求我做一些硬件模拟。假设我的皮带上装满了传感器。就像这个。当obj到达传感器时,它应该触发……
我不确定你是否打算关注MVVM,但无论哪种情况你都可以尝试这样做:
对于视觉部分,您可以使用 Canvas ,但任何其他小组可能会工作。 Canvas允许你使用 Canvas.Left 这样的东西,你所有的定位可能都是基于 System.Windows.Point 无论如何,价值,而不是相对定位。
Canvas
Canvas.Left
System.Windows.Point
剩下的事情可以用其中一个来绘制 System.Window.Shapes.Shape 子类。这两个酒吧可能 Rectangle ,对于梁将 Line ,对于传感器可能是一个 Ellipse 或者a Rectangle 。
System.Window.Shapes.Shape
Rectangle
Line
Ellipse
梁对象应该有 Visibility 绑定到ViewModel中的某个属性(如果不使用MVVM,则只是后面的代码)。该 Stroke 梁和梁的性质 Fill 传感器的属性也应该受到约束。
Visibility
Stroke
Fill
我不确定传感器尺寸会如何影响任何东西,可能意味着光束可能很宽?如果确实有影响,请绑定 Width 和 Height 同样。还记得绑定梁的 StrokeThickness 如果传感器尺寸很重要
Width
Height
StrokeThickness
对于该对象,您将使用 Rectangle 再次。再次,做必要的绑定。新的绑定将是 Canvas.Left 和 Canvas.Top ,以便你知道这个位置。
Canvas.Top
现在我们需要为对象设置动画。由于您通过绑定获得了位置,因此您可以更改source属性中的位置 - 绑定引擎将为您执行渲染更改。您可以使用计时器来移动对象。
现在我们需要找到碰撞。一种方法是计算它后面的代码,这将在每次对象位置改变时完成。还有另一种方法,因为您使用的是Canvas。读这个 所以发帖 。您需要做的就是转换底层证券 Geometry 对象和梁的实例(通过 myobject.RenderedGeometry.FillContainsWithDetail(beam) )。
Geometry
myobject.RenderedGeometry.FillContainsWithDetail(beam)
当然还有很多其他方法可以做到这一点。我只是希望我设法提供启动项目的方向。
你需要 HitTesting 。
然后你需要设置一个 DependencyProperty 就像是 CollisionDetected 至 True 。
DependencyProperty
CollisionDetected
True
然后申请一个 DataTrigger 到你的 Beam xaml改变它 Style/BackgroundColor 等等
DataTrigger
Beam
Style/BackgroundColor
<uc:BeamControl> <uc:BeamControl.Style> <Style TargetType="BeamControl"> <Style.Triggers> <DataTrigger Binding="{Binding CollisionDetected}" Value="True"> <Setter Property="Background" Value="Red" /> </DataTrigger> </Style.Triggers> </Style> </uc:BeamControl.Style> </uc:BeamControl>
***********************新增代码*********************** *
在这个工作解决方案中,我使用了附加属性。我一直都很简单,专注于核心问题。
的 Window2.xaml 强>
<Window x:Class="WpfAnimation.Window2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfAnimation" Title="Window2" Height="300" Width="900"> <Canvas> <Path local:Window2.MovingObjectPos="{Binding (Canvas.Left), ElementName=ElpObj}" Fill="Red" Stretch="Fill" Stroke="Black" HorizontalAlignment="Left" Canvas.Left="100" Canvas.Top="50" Width="39" Data="M19.5,0.5 L19.5,147.5 M0.5,148.5 L38.5,148.5 L38.5,169.5 L0.5,169.5 z"> </Path> <Path local:Window2.MovingObjectPos="{Binding (Canvas.Left), ElementName=ElpObj}" Fill="Red" Stretch="Fill" Stroke="Black" HorizontalAlignment="Left" Canvas.Left="200" Canvas.Top="50" Width="39" Data="M19.5,0.5 L19.5,147.5 M0.5,148.5 L38.5,148.5 L38.5,169.5 L0.5,169.5 z"> </Path> <Path local:Window2.MovingObjectPos="{Binding (Canvas.Left), ElementName=ElpObj}" Fill="Red" Stretch="Fill" Stroke="Black" HorizontalAlignment="Left" Canvas.Left="300" Canvas.Top="50" Width="39" Data="M19.5,0.5 L19.5,147.5 M0.5,148.5 L38.5,148.5 L38.5,169.5 L0.5,169.5 z"> </Path> <Path local:Window2.MovingObjectPos="{Binding (Canvas.Left), ElementName=ElpObj}" Fill="Red" Stretch="Fill" Stroke="Black" HorizontalAlignment="Left" Canvas.Left="400" Canvas.Top="50" Width="39" Data="M19.5,0.5 L19.5,147.5 M0.5,148.5 L38.5,148.5 L38.5,169.5 L0.5,169.5 z"> </Path> <Ellipse x:Name="ElpObj" Fill="Pink" Stroke="Black" HorizontalAlignment="Right" VerticalAlignment="Top" Width="57" Height="51" Canvas.Left="701" Canvas.Top="115"> <Ellipse.Triggers> <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" To="50" By="-2.0"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Ellipse.Triggers> </Ellipse> </Canvas> </Window>
的 Window2.xaml.cs 强>
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; namespace WpfAnimation { /// <summary> /// Interaction logic for Window2.xaml /// </summary> public partial class Window2 : Window { public Window2() { InitializeComponent(); } public static double GetMovingObjectPos(DependencyObject obj) { return (double)obj.GetValue(MovingObjectPosProperty); } public static void SetMovingObjectPos(DependencyObject obj, double value) { obj.SetValue(MovingObjectPosProperty, value); } // Using a DependencyProperty as the backing store for MovingObject. This enables animation, styling, binding, etc... public static readonly DependencyProperty MovingObjectPosProperty = DependencyProperty.RegisterAttached("MovingObjectPos", typeof(double), typeof(Window2), new PropertyMetadata(0.0, new PropertyChangedCallback(MovingObjectPosChanged))); private static void MovingObjectPosChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { double leftOfMovingObject = (double) e.NewValue ; Path beam = (Path) d; System.Diagnostics.Debug.WriteLine("Left = " + e.NewValue.ToString()); double leftOfBeam = Canvas.GetLeft(beam); double widthOfBeam = 20.0; if (leftOfMovingObject > leftOfBeam && leftOfMovingObject < leftOfBeam + widthOfBeam) { System.Diagnostics.Debug.WriteLine("Hit >>>>> = " + e.NewValue.ToString()); beam.Fill = Brushes.Gray; } } } }
我使用了我的旧答案 Task ,并考虑无限移动物体。
Task
它使用实际 HitTesting 。
UserControl
Loading
Panel
<UserControl x:Class="WpfAnimation.BeamControl" ... Height="300" Width="39"> <Path Fill="Red" Stretch="Fill" Stroke="Black" HorizontalAlignment="Left" Width="39" Data="M19.5,0.5 L19.5,147.5 M0.5,148.5 L38.5,148.5 L38.5,169.5 L0.5,169.5 z"/> </UserControl>
using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; namespace WpfAnimation { /// <summary> /// Interaction logic for BeamControl.xaml /// </summary> public partial class BeamControl : UserControl { Path _beamPath; public Panel Sensor { get { return (Panel)GetValue(SensorProperty); } set { SetValue(SensorProperty, value); } } // Using a DependencyProperty as the backing store for Sensor. This enables animation, styling, binding, etc... public static readonly DependencyProperty SensorProperty = DependencyProperty.Register("Sensor", typeof(Panel), typeof(BeamControl), new PropertyMetadata(null)); private double left; private double top; public BeamControl() { InitializeComponent(); this.Loaded += BeamControl_Loaded; } void BeamControl_Loaded(object sender, RoutedEventArgs e) { _beamPath = this.Content as Path; left = Canvas.GetLeft(this); top = Canvas.GetTop(this); Task.Factory.StartNew(() => { while (true) { _doDetection(); } }); } void _doDetection() { // 200 is the height of the beam, you can change it for (double i = top; i < 200; i = i + 1) { System.Diagnostics.Debug.WriteLine(i.ToString()); Point pt = new Point(left + 20.0, i); this.Dispatcher.Invoke(() => { VisualTreeHelper.HitTest(Sensor, new HitTestFilterCallback((o) => { if (o is Ellipse) { _beamPath.Fill = Brushes.DarkKhaki; System.Diagnostics.Debug.WriteLine("Detected ! " + o.GetType().ToString()); System.Diagnostics.Debug.WriteLine(pt.ToString()); return HitTestFilterBehavior.Stop; } else System.Diagnostics.Debug.WriteLine(o.GetType().ToString()); return HitTestFilterBehavior.Continue; }), new HitTestResultCallback((result) => { return HitTestResultBehavior.Continue; }), new PointHitTestParameters(pt)); }); } } } }
<Window x:Class="WpfAnimation.MainWindow" ... Title="Window5" Height="303.383" Width="622.556"> <Canvas> <local:BeamControl Canvas.Left="200" Canvas.Top="50" Sensor="{Binding RelativeSource={RelativeSource AncestorType=Canvas, Mode=FindAncestor}}"/> <local:BeamControl Canvas.Left="300" Canvas.Top="50" Sensor="{Binding RelativeSource={RelativeSource AncestorType=Canvas, Mode=FindAncestor}}"/> <Ellipse x:Name="ElpObj" Fill="Pink" Stroke="Black" HorizontalAlignment="Right" VerticalAlignment="Top" Width="57" Height="51" Canvas.Left="548" Canvas.Top="114"> <Ellipse.Triggers> <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" To="50" By="-0.5" Duration="0:0:20"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Ellipse.Triggers> </Ellipse> </Canvas> </Window>
运行该应用程序 没有调试 。