Clipping Using Ellipse and binding it inside my us

2019-06-08 20:49发布

问题:

I have an image and I am clipping the mouse area using an Ellipse in which I am quite successful.

But I want that ellipse to be a part of my user control and the user control should move along with my finger and the clipping Ellipse should be inside the user control . The complete project can be downloaded from here

My UserControl'sXAML is

<UserControl
    x:Class="App78.Magnifier"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App78"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Height="230"
    Width="170">


    <Grid Height="230" Width="170">
        <Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
              Fill="#FFF4F4F5"
              Stretch="Fill"
              Stroke="Black"
              UseLayoutRounding="False"
              Height="227"
              Width="171" ></Path>
            <Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public"  Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
                <Ellipse.RenderTransform>
                    <TranslateTransform x:Name="MagnifierTransform" x:FieldModifier="public"/>
                </Ellipse.RenderTransform>

                <Ellipse.Fill>
                    <ImageBrush
                    ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
                    Stretch="None"
                    AlignmentX="Left"
                    AlignmentY="Top">
                        <ImageBrush.Transform>
                            <TransformGroup>
                                <TranslateTransform x:FieldModifier="public"
                                x:Name="PositionTransform"/>
                                <ScaleTransform x:FieldModifier="public"
                                x:Name="ZoomTransform"/>
                                <TranslateTransform x:FieldModifier="public"
                                x:Name="CenterTransform" />
                            </TransformGroup>
                        </ImageBrush.Transform>
                    </ImageBrush>
                </Ellipse.Fill>
            </Ellipse>

    </Grid>
</UserControl>

My MainPage.XAML is

<Page
    x:Class="App78.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App78"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d">

    <Grid
        x:Name="LayoutGrid"
        Margin="0,0"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        Holding="LayoutGrid_Holding"
        PointerMoved="LayoutGrid_OnPointerMoved"
        PointerWheelChanged="LayoutGrid_OnPointerWheelChanged">
        <Image
            x:Name="BigImage"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Stretch="Uniform"
            Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />


                <!--<Ellipse 
            x:Name="MagnifierEllipse"
            Opacity="1"
            Visibility="Collapsed"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            IsHitTestVisible="False"
            Width="150"
            Height="150"
            Stroke="White"
            StrokeThickness="3"
            Margin="-100">
                    <Ellipse.RenderTransform>
                        <TranslateTransform
                    x:Name="MagnifierTransform"/>
                    </Ellipse.RenderTransform>
                    <Ellipse.Fill>
                        <ImageBrush
                    ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
                    Stretch="None"
                    AlignmentX="Left"
                    AlignmentY="Top">
                            <ImageBrush.Transform>
                                <TransformGroup>
                                    <TranslateTransform
                                x:Name="PositionTransform"/>
                                    <ScaleTransform
                                x:Name="ZoomTransform"/>
                                    <TranslateTransform
                                x:Name="CenterTransform" />
                                </TransformGroup>
                            </ImageBrush.Transform>
                        </ImageBrush>
                    </Ellipse.Fill>
                </Ellipse>-->
        <local:Magnifier x:Name="MagnifierTip" Visibility="Visible" />
    </Grid>
</Page>

Please see the commented section of Ellipse , If I uncomment , I clearly get a Ellipse that is doing the clip.

My main requirenment is to get the Ellipse inside the user control , please refer the video for more clarification

My MainPage.XAML.CS has the following code

using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;

namespace App78
{
    public sealed partial class MainPage : Page
    {
        private double zoomScale = 2;
        private double pointerX = 0;
        private double pointerY = 0;
        private const double MinZoomScale = .25;
        private const double MaxZoomScale = 32;


        public MainPage()
        {
            this.InitializeComponent();

            var bi = (BitmapImage)BigImage.Source;
            bi.ImageOpened += bi_ImageOpened;
            this.SizeChanged += MainPage_SizeChanged;
        }

        void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
        {
            this.UpdateImageLayout();
        }

        void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            this.UpdateImageLayout();
        }

        private void UpdateImageLayout()
        {
            var bi = (BitmapImage)BigImage.Source;
            if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
                bi.PixelHeight < this.LayoutGrid.ActualHeight)
            {
                this.BigImage.Stretch = Stretch.None;
            }
            else
            {
                this.BigImage.Stretch = Stretch.Uniform;
            }

            this.UpdateMagnifier();
        }

        private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
        {
           // MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
            var point = e.GetCurrentPoint(this.LayoutGrid);
            this.pointerX = point.Position.X;
            this.pointerY = point.Position.Y;
            this.UpdateMagnifier();
        }

        private void UpdateMagnifier()
        {
            var bi = (BitmapImage)BigImage.Source;

            double offsetX = 0;
            double offsetY = 0;
            double imageScale = 1;

            var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
            var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;

            if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
                bi.PixelHeight < this.LayoutGrid.ActualHeight)
            {
                offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
                offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
                //imageScale = 1; - remains
            }
            else if (imageRatio < gridRatio)
            {
                offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
                offsetY = 0;
                imageScale = BigImage.ActualHeight / bi.PixelHeight;
            }
            else
            {
                offsetX = 0;
                offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
                imageScale = BigImage.ActualWidth / bi.PixelWidth;
            }

            MagnifierTip.MagnifierTransform.X = this.pointerX;
           MagnifierTip.MagnifierTransform.Y = this.pointerY;
           MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
           MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
           MagnifierTip. ZoomTransform.ScaleX = imageScale * zoomScale;
           MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
           MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
           MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
        }

        private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
            {
                zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
            }
            else
            {
                zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
            }

            this.UpdateMagnifier();
        }

        private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
        {
          //  MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;


        }
    }
}

回答1:

I think you need to get your MagnifierTransform to the "root" Grid of your Magnifier UserControl.
Also the CenterTransform doesn't seem to be needed in my opinion.
The other last change is in the MainPage, making the User Control aligned to Top/Left so that the TranslateTransforms make sense.

Just as a bonus, I got the updated project here , also with pointer pressed/released events to show/hide the magnifier. Be warned that "Holding" doesn't work with most mouse devices. Source: link
I also added comments on my changes (comments start with DV:). It's not perfect and there's obviously still a lot for you to implement but hopefully this is what you needed.

For whoever doesn't want to download the project I'll leave the updated code here:
MainPage.xaml

<Page
    x:Class="App78.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App78"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid
        x:Name="LayoutGrid"
        Margin="0,0"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        Holding="LayoutGrid_Holding"
        PointerMoved="LayoutGrid_OnPointerMoved"
        PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
        PointerPressed="LayoutGrid_OnPointerPressed"
        PointerReleased="LayoutGrid_OnPointerReleased">
        <Image
            x:Name="BigImage"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Stretch="Uniform"
            Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />

        <local:Magnifier VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="MagnifierTip" Visibility="Collapsed" />
    </Grid>
</Page>

MainPage.xaml.cs

using System;
using System.Diagnostics;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;

namespace App78
{
    public sealed partial class MainPage : Page
    {
        private double zoomScale = 2;
        private double pointerX = 0;
        private double pointerY = 0;
        private const double MinZoomScale = .25;
        private const double MaxZoomScale = 32;


        public MainPage()
        {
            this.InitializeComponent();

            var bi = (BitmapImage)BigImage.Source;
            bi.ImageOpened += bi_ImageOpened;
            this.SizeChanged += MainPage_SizeChanged;
        }

        void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
        {
            this.UpdateImageLayout();
        }

        void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            this.UpdateImageLayout();
        }

        private void UpdateImageLayout()
        {
            var bi = (BitmapImage)BigImage.Source;
            if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
                bi.PixelHeight < this.LayoutGrid.ActualHeight)
            {
                this.BigImage.Stretch = Stretch.None;
            }
            else
            {
                this.BigImage.Stretch = Stretch.Uniform;
            }

            this.UpdateMagnifier();
        }

        private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
        {
            //DV: If pointer is not in contact we can ignore it
            if (!e.Pointer.IsInContact) { return; }

            var point = e.GetCurrentPoint(this.LayoutGrid);
            this.pointerX = point.Position.X;
            this.pointerY = point.Position.Y;
            this.UpdateMagnifier();
        }

        private void UpdateMagnifier()
        {
            var bi = (BitmapImage)BigImage.Source;

            double offsetX = 0;
            double offsetY = 0;
            double imageScale = 1;

            var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
            var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;

            if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
                bi.PixelHeight < this.LayoutGrid.ActualHeight)
            {
                offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
                offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
                //imageScale = 1; - remains
            }
            else if (imageRatio < gridRatio)
            {
                offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
                offsetY = 0;
                imageScale = BigImage.ActualHeight / bi.PixelHeight;
            }
            else
            {
                offsetX = 0;
                offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
                imageScale = BigImage.ActualWidth / bi.PixelWidth;
            }

            //DV: This is probably not need anymore
           //MagnifierTip.MagnifierTransform.X = this.pointerX;
           //MagnifierTip.MagnifierTransform.Y = this.pointerY;
            MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
            MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;

            //DV: I haven't tested the Scaling/Zoom
            MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
            MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;

            MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
            MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;

            //DV: I added a GlobalGrid Transform which translates every children
            MagnifierTip.MagnifierTransformGrid.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
            MagnifierTip.MagnifierTransformGrid.Y = this.pointerY - (MagnifierTip.ActualHeight); ;

        }

        private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
            {
                zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
            }
            else
            {
                zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
            }

            this.UpdateMagnifier();
        }


        //DV: Holding usually only works with touch https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.holding.aspx?f=255&MSPPError=-2147217396
        private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
        {
            //
        }

        //DV: pointer pressed supports both mouse and touch but fires immeadiatley. You'll have to figure out a delay strategy or using holding for touch and right click for mouse
        private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
        }

        //DV: pointer released supports both mouse and touch.
        private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
        {
            MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        }
    }
}

Magnifier.xaml

<UserControl
    x:Class="App78.Magnifier"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App78"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Height="230"
    Width="170">


    <Grid Height="230" Width="170">

        <!-- DV: This is the global transform I added -->
        <Grid.RenderTransform>
            <TransformGroup>
                <TranslateTransform x:Name="MagnifierTransformGrid" x:FieldModifier="public"/>
            </TransformGroup>
        </Grid.RenderTransform>
        <Ellipse Opacity="1" Visibility="Visible" Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}"  HorizontalAlignment="Center" VerticalAlignment="Top" IsHitTestVisible="False" Width="135" Height="128"  StrokeThickness="3" Margin="0,17,0,0" />
        <Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public" Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
            <Ellipse.Fill>
                <ImageBrush
                    ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
                    Stretch="None"
                    AlignmentX="Left"
                    AlignmentY="Top">
                    <ImageBrush.Transform>
                        <TransformGroup>
                            <TranslateTransform x:FieldModifier="public"
                                x:Name="CenterTransform"/>
                            <TranslateTransform x:FieldModifier="public"
                                x:Name="PositionTransform"/>
                            <ScaleTransform x:FieldModifier="public"
                                x:Name="ZoomTransform"/>
                        </TransformGroup>
                    </ImageBrush.Transform>
                </ImageBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
              Fill="#FFF4F4F5"
              Stretch="Fill"
              Stroke="Black"
              UseLayoutRounding="False"
              Height="227"
              Width="171" ></Path>
    </Grid>
</UserControl>