0

我有一个画布(拼图)和一个透明背景的子 png 图像列表(拼图部件)。仅当鼠标悬停在图像的非透明部分上时,我才需要拖动这些图像。因此,我的实现如下所示:

    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));
            }
        }
    }
4

0 回答 0