Canvas (Puzzle) と、背景が透明な子 PNG 画像 (PuzzleParts) のリストがあります。これらの画像をドラッグする必要があるのは、画像の透明でない部分にマウスを置いた場合のみです。したがって、私の実装は次のようになります。
public class Puzzle : Canvas
{
public Puzzle()
{
Background = new SolidColorBrush(Colors.Transparent);
MouseLeftButtonDown += Puzzle_MouseLeftButtonDown;
}
public void Load(PuzzleSettings settings)
{
foreach (var child in Children)
{
//child.MouseLeftButtonDown -= OnMouseLeftButtonDown;
child.MouseLeftButtonUp -= OnMouseLeftButtonUp;
child.MouseMove -= OnMouseMove;
}
Children.Clear();
foreach (var part in settings.Parts)
{
var puzzlePart = new PuzzlePart(part);
//puzzlePart.MouseLeftButtonDown += OnMouseLeftButtonDown;
puzzlePart.MouseLeftButtonUp += OnMouseLeftButtonUp;
puzzlePart.MouseMove += OnMouseMove;
Children.Add(puzzlePart);
}
}
/// Finds all PuzzleParts under mouse pointer and first of them with non-
/// transparent pixel (dragging PuzzlePart)
private void Puzzle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var elements = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), this);
foreach (var element in elements)
{
if (!(element is PuzzlePart))
continue;
var part = element as PuzzlePart;
if (!IsTransparentPixel(part, e.GetPosition(part)))
{
OnMouseLeftButtonDown(part, e);
break;
}
}
}
/// Checks if specific pixel of PuzzlePart is transparent
private bool IsTransparentPixel(PuzzlePart part, Point point)
{
var uri = new Uri(part.Settings.ImagePath, UriKind.Relative);
var bitmapImage = new BitmapImage {CreateOptions = BitmapCreateOptions.None, UriSource = uri};
var writableBitmap = new WriteableBitmap(bitmapImage);
// check bounds
if (point.X < 0.0 || point.X > writableBitmap.PixelWidth - 1 ||
point.Y < 0.0 || point.Y > writableBitmap.PixelHeight - 1)
return false;
var row = (int)Math.Floor(point.Y);
var col = (int)Math.Floor(point.X);
int pixel = writableBitmap.Pixels[row * writableBitmap.PixelWidth + col];
byte[] pixelBytes = BitConverter.GetBytes(pixel);
if (pixelBytes[0] == 0x00)
return true;
return false;
}
private bool _isDragging;
private Point _lastPosition;
private double _offsetX;
private double _offsetY;
private int _lastZIndex;
#region Puzzle part dragging
/// Performs PuzzlePart dragging
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (_isDragging)
{
var puzzlePart = (PuzzlePart) sender;
Point p = e.GetPosition(this);
double x = p.X - _offsetX;
double y = p.Y - _offsetY;
var displacementX = puzzlePart.Settings.ControlPoint.Position.X - x;
var displacementY = puzzlePart.Settings.ControlPoint.Position.Y - y;
if (Math.Sqrt(displacementX * displacementX + displacementY * displacementY) < _sensivity)
{
x = puzzlePart.Settings.ControlPoint.Position.X;
y = puzzlePart.Settings.ControlPoint.Position.Y;
}
// Set the new position for the puzzle part control.
puzzlePart.SetValue(LeftProperty, x);
puzzlePart.SetValue(TopProperty, y);
}
}
/// Ends drag operation
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var element = (UIElement)sender;
_isDragging = false;
element.ReleaseMouseCapture();
_lastPuzzlePart = (PuzzlePart) element;
}
/// Begins drag operation
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var puzzlePart = (PuzzlePart) sender;
puzzlePart.CaptureMouse();
_isDragging = true;
SetZIndex(puzzlePart, _lastZIndex++);
_lastPosition = e.GetPosition(puzzlePart);
_offsetX = _lastPosition.X;
_offsetY = _lastPosition.Y;
}
#endregion
}
PuzzlePart が Canvas の上にある場合はうまく機能しますが、PuzzlePart が上にない場合にドラッグするとパフォーマンスの問題が発生し、マウス オーバー ピクセルを検査してドラッグ イメージを見つける必要があります。何が問題なのかわかりません。私のXAMLコード:
<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:eim="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions"
xmlns:Controls="clr-namespace:KidsABCPuzzle.Controls;assembly=KidsABCPuzzle.Controls"
x:Class="KidsABCPuzzle.Views.PuzzleView"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Landscape" Orientation="Landscape"
mc:Ignorable="d" d:DesignHeight="480" d:DesignWidth="800"
shell:SystemTray.IsVisible="False">
<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="LoadedStoryboard">
<DoubleAnimation Duration="0:0:1" To="0.6" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="border" d:IsOptimized="True">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</phone:PhoneApplicationPage.Resources>
<Grid x:Name="LayoutRoot" Background="Transparent">
<Image Source="../Images/Level1.png"/>
<Border x:Name="border" Background="White" Opacity="0.0">
<i:Interaction.Triggers>
<i:EventTrigger>
<eim:ControlStoryboardAction Storyboard="{StaticResource LoadedStoryboard}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Border>
<Controls:Puzzle x:Name="puzzle"/>
</Grid>
</phone:PhoneApplicationPage>
とパズルパート:
<Style TargetType="Controls:PuzzlePart">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Controls:PuzzlePart">
<Grid>
<Image x:Name="Image"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
/// <summary>
/// Represents puzzle part
/// </summary>
[TemplatePart(Name = "Image", Type = typeof(Image))]
public class PuzzlePart : ContentControl
{
/// <summary>
/// Image of puzzle part
/// </summary>
public Image Image { get; private set; }
public PuzzlePartSettings Settings { get; set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="point"> Intialization placement. </param>
public PuzzlePart(ControlPoint point)
{
DefaultStyleKey = typeof(PuzzlePart);
Settings.ControlPoint = point;
}
public PuzzlePart(PuzzlePartSettings settings)
{
DefaultStyleKey = typeof (PuzzlePart);
Settings = settings;
}
/// <summary>
/// Applies default template
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Image = (Image)GetTemplateChild("Image");
if (Settings != null
&& !string.IsNullOrEmpty(Settings.ImagePath))
{
Image.Source = new BitmapImage(new Uri(Settings.ImagePath, UriKind.Relative));
}
}
}