1

我有一堆图标,应该在 Windows Phone 8 应用程序中以不同大小在许多不同的地方使用。使用普通图形,我必须为每种支持的分辨率创建每种尺寸的每个图标。这可能会变成很多文件...

因此,我想改用矢量图形。因为我的所有图标都是 PDF/Illustrator .ai,所以这不是问题。我使用 Blend 来导入这些文件,它会自动创建 XAML:

<Canvas x:Name="TestIcon" Height="27.985" Canvas.Left="5.506" Canvas.Top="2.007" Width="20.988">
    <Path Data="F1M26.992,... Fill="#FF376EB5" Height="27.977" Canvas.Left="0" Stretch="None" Canvas.Top="0" Width="27.993">
        <Path.Clip>
            <RectangleGeometry Rect="0,-0.001,27.992,27.977"/>
         </Path.Clip>
    </Path>
</Canvas>

问题:如何在整个应用程序中使用此画布?

我将 Canvas 添加到 Application.Resources 并尝试引用它:

<!-- Version 1 -->
<Button Content="{StaticResource TestIcon} .../>

<!-- Version 2 -->
<Button ...>
    <ContentPresenter Content="{StaticResource TestIcon}" .../>
</Button>

<!-- Version 3 -->
<Button ...>
    <Canvas ..>
        <Path Data="F1M26.992,... ...>
            <Path.Clip>
                ...
             </Path.Clip>
        </Path>
    </Canvas>
</Button>

使用版本 1 或 2 时,Designer 中一切正常。一旦我运行该应用程序,它就会因 System.Windows.Markup.XamlParseException 而崩溃:

未能分配给属性“System.Windows.Controls.ContentPresenter.Content”。

版本 3 在 Designer 和运行时都可以正常工作。但当然,此解决方案不使用任何对 VectorGraphic 的引用,而是直接包含 Canvas。

知道如何解决这个问题吗?

4

3 回答 3

3

您可能应该在 Resources 中为每个形状创建 ControlTemplate,然后在应用程序的任何位置重用此模板:

<ControlTemplate x:Key="MyIcon">
    <Canvas x:Name="TestIcon" Height="27.985" Canvas.Left="5.506" Canvas.Top="2.007" Width="20.988">
        <Path Data="F1M26.992,... Fill="#FF376EB5" Height="27.977" Canvas.Left="0" Stretch="None" Canvas.Top="0" Width="27.993">
            <Path.Clip>
                <RectangleGeometry Rect="0,-0.001,27.992,27.977"/>
             </Path.Clip>
        </Path>
    </Canvas>
</ControlTemplate>

on another place, where you want to show  it:
<ContentControl Template="{StaticResource MyIcon}"/>
于 2013-11-07T22:08:17.667 回答
2

看看这个:在 Windows 8 商店应用程序中重用矢量图形

共有 3 种方法可以重用 XAML 路径

  1. 使用路径本身

    <Border Height="200"
        Width="200"
        BorderBrush="Red"
        BorderThickness="1"
        Background="Yellow"
        Padding="20"
        Grid.Row="1"
        Grid.Column="1">
    <Viewbox Stretch="Uniform">
        <!-- Works at designtime but crashes at runtime -->
        <!--<ContentControl Content="{StaticResource PrancingHorse}" />-->    
        <!-- That would work if if were possible to define the PathGeomatry as a resource. -->
        <!--<Path Data="{StaticResource PrancingGeometry}" />-->
        <Path Data="M22.923445,61.752014L22.733789,61.7598....."
                Fill="White" />
    </Viewbox>
    

  2. 使用路径数据作为字符串资源

    <Page.Resources>
        <x:String x:Key="PrancingString">M22.923445,61.752014L22.733789,61.759857 .....</x:String>
    </Page.Resources>
    
    <Border Height="200"
        Width="200"
        BorderBrush="Red"
        BorderThickness="1"
        Padding="20"
        Grid.Row="3"
        Grid.Column="2">
    <Viewbox Stretch="Uniform">
        <Path Data="{StaticResource PrancingString}"
              Fill="White" />
    </Viewbox>
    

  3. 使用图标控制

图标控制.cs

public sealed class IconControl : Control
{
    public static readonly DependencyProperty DataGeometryProperty =
        DependencyProperty.Register("DataGeometry", typeof(PathGeometry), typeof(IconControl), new PropertyMetadata(null));

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(String), typeof(IconControl), new PropertyMetadata(null, new PropertyChangedCallback(OnDataChanged)));

    public IconControl()
    {
        this.DefaultStyleKey = typeof(IconControl);
    }

    // Write-only to be used in a binding.
    public String Data
    {
        private get { return (String)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Read-only to be used in the control's template.
    public PathGeometry DataGeometry
    {
        get { return (PathGeometry)GetValue(DataGeometryProperty); }
        private set { SetValue(DataGeometryProperty, value); }
    }

    private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        IconControl ic = d as IconControl;

        // In WPF: ic.DataGeometry = PathGeometry.CreateFromGeometry(PathGeometry.Parse(e.NewValue.ToString()));
        ic.DataGeometry = new PathGeometryParser().Parse(e.NewValue.ToString());
    }
}

PathGeometryParser.cs

public class PathGeometryParser
{
    #region Constants
    const bool AllowSign = true;
    const bool AllowComma = true;
    const bool IsFilled = true;
    const bool IsClosed = true; 
    #endregion

    #region Fields
    IFormatProvider _formatProvider;

    PathFigure _figure = null;     // Figure object, which will accept parsed segments
    string _pathString;        // Input string to be parsed
    int _pathLength;
    int _curIndex;          // Location to read next character from
    bool _figureStarted;     // StartFigure is effective 

    Point _lastStart;         // Last figure starting point
    Point _lastPoint;         // Last point 
    Point _secondLastPoint;   // The point before last point

    char _token = ' ';             // Non whitespace character returned by ReadToken 
    #endregion

    static internal char GetNumericListSeparator(IFormatProvider provider)
    {
        char numericSeparator = ',';

        // Get the NumberFormatInfo out of the provider, if possible
        // If the IFormatProvider doesn't not contain a NumberFormatInfo, then 
        // this method returns the current culture's NumberFormatInfo. 
        NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider);

        // Is the decimal separator is the same as the list separator?
        // If so, we use the ";". 
        if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0]))
        {
            numericSeparator = ';';
        }

        return numericSeparator;
    }

    /// <summary>
    /// Turns a string into a PathGeometry.
    /// </summary>
    /// <remarks>
    /// Code could use some refactoring, to expose the logic as an extension method of PathGeometry.
    /// </remarks>
    public PathGeometry Parse(string path)
    {
        PathGeometry _pathGeometry = null;

        _formatProvider = CultureInfo.InvariantCulture;
        _pathString = path;
        _pathLength = path.Length;
        _curIndex = 0;

        _secondLastPoint = new Point(0, 0);
        _lastPoint = new Point(0, 0);
        _lastStart = new Point(0, 0);

        _figureStarted = false;

        bool first = true;

        char last_cmd = ' ';

        while (ReadToken()) // Empty path is allowed in XAML
        {
            char cmd = _token;

            if (first)
            {
                if ((cmd != 'M') && (cmd != 'm') && (cmd != 'f') && (cmd != 'F'))  // Path starts with M|m 
                {
                    ThrowBadToken();
                }

                first = false;
            }

            switch (cmd)
            {
                case 'f':
                case 'F':
                    _pathGeometry = new PathGeometry();
                    double _num = ReadNumber(!AllowComma);
                    _pathGeometry.FillRule = _num == 0 ? FillRule.EvenOdd : FillRule.Nonzero;
                    break;

                case 'm':
                case 'M':
                    // XAML allows multiple points after M/m
                    _lastPoint = ReadPoint(cmd, !AllowComma);

                    _figure = new PathFigure();
                    _figure.StartPoint = _lastPoint;
                    _figure.IsFilled = IsFilled;
                    _figure.IsClosed = !IsClosed;
                    //context.BeginFigure(_lastPoint, IsFilled, !IsClosed);
                    _figureStarted = true;
                    _lastStart = _lastPoint;
                    last_cmd = 'M';

                    while (IsNumber(AllowComma))
                    {
                        _lastPoint = ReadPoint(cmd, !AllowComma);

                        LineSegment _lineSegment = new LineSegment();
                        _lineSegment.Point = _lastPoint;
                        _figure.Segments.Add(_lineSegment);
                        //context.LineTo(_lastPoint, IsStroked, !IsSmoothJoin);
                        last_cmd = 'L';
                    }
                    break;

                case 'l':
                case 'L':
                case 'h':
                case 'H':
                case 'v':
                case 'V':
                    EnsureFigure();

                    do
                    {
                        switch (cmd)
                        {
                            case 'l': _lastPoint = ReadPoint(cmd, !AllowComma); break;
                            case 'L': _lastPoint = ReadPoint(cmd, !AllowComma); break;
                            case 'h': _lastPoint.X += ReadNumber(!AllowComma); break;
                            case 'H': _lastPoint.X = ReadNumber(!AllowComma); break;
                            case 'v': _lastPoint.Y += ReadNumber(!AllowComma); break;
                            case 'V': _lastPoint.Y = ReadNumber(!AllowComma); break;
                        }

                        LineSegment _lineSegment = new LineSegment();
                        _lineSegment.Point = _lastPoint;
                        _figure.Segments.Add(_lineSegment);
                        //context.LineTo(_lastPoint, IsStroked, !IsSmoothJoin);
                    }
                    while (IsNumber(AllowComma));

                    last_cmd = 'L';
                    break;

                case 'c':
                case 'C': // cubic Bezier 
                case 's':
                case 'S': // smooth cublic Bezier
                    EnsureFigure();

                    do
                    {
                        Point p;

                        if ((cmd == 's') || (cmd == 'S'))
                        {
                            if (last_cmd == 'C')
                            {
                                p = Reflect();
                            }
                            else
                            {
                                p = _lastPoint;
                            }

                            _secondLastPoint = ReadPoint(cmd, !AllowComma);
                        }
                        else
                        {
                            p = ReadPoint(cmd, !AllowComma);

                            _secondLastPoint = ReadPoint(cmd, AllowComma);
                        }

                        _lastPoint = ReadPoint(cmd, AllowComma);

                        BezierSegment _bizierSegment = new BezierSegment();
                        _bizierSegment.Point1 = p;
                        _bizierSegment.Point2 = _secondLastPoint;
                        _bizierSegment.Point3 = _lastPoint;
                        _figure.Segments.Add(_bizierSegment);
                        //context.BezierTo(p, _secondLastPoint, _lastPoint, IsStroked, !IsSmoothJoin);

                        last_cmd = 'C';
                    }
                    while (IsNumber(AllowComma));

                    break;

                case 'q':
                case 'Q': // quadratic Bezier 
                case 't':
                case 'T': // smooth quadratic Bezier
                    EnsureFigure();

                    do
                    {
                        if ((cmd == 't') || (cmd == 'T'))
                        {
                            if (last_cmd == 'Q')
                            {
                                _secondLastPoint = Reflect();
                            }
                            else
                            {
                                _secondLastPoint = _lastPoint;
                            }

                            _lastPoint = ReadPoint(cmd, !AllowComma);
                        }
                        else
                        {
                            _secondLastPoint = ReadPoint(cmd, !AllowComma);
                            _lastPoint = ReadPoint(cmd, AllowComma);
                        }

                        QuadraticBezierSegment _quadraticBezierSegment = new QuadraticBezierSegment();
                        _quadraticBezierSegment.Point1 = _secondLastPoint;
                        _quadraticBezierSegment.Point2 = _lastPoint;
                        _figure.Segments.Add(_quadraticBezierSegment);
                        //context.QuadraticBezierTo(_secondLastPoint, _lastPoint, IsStroked, !IsSmoothJoin);

                        last_cmd = 'Q';
                    }
                    while (IsNumber(AllowComma));

                    break;

                case 'a':
                case 'A':
                    EnsureFigure();

                    do
                    {
                        // A 3,4 5, 0, 0, 6,7
                        double w = ReadNumber(!AllowComma);
                        double h = ReadNumber(AllowComma);
                        double rotation = ReadNumber(AllowComma);
                        bool large = ReadBool();
                        bool sweep = ReadBool();

                        _lastPoint = ReadPoint(cmd, AllowComma);

                        ArcSegment _arcSegment = new ArcSegment();
                        _arcSegment.Point = _lastPoint;
                        _arcSegment.Size = new Size(w, h);
                        _arcSegment.RotationAngle = rotation;
                        _arcSegment.IsLargeArc = large;
                        _arcSegment.SweepDirection = sweep ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
                        _figure.Segments.Add(_arcSegment);
                        //context.ArcTo(
                        //    _lastPoint,
                        //    new Size(w, h),
                        //    rotation,
                        //    large,
                        //    sweep ? SweepDirection.Clockwise : SweepDirection.Counterclockwise,
                        //    IsStroked,
                        //    !IsSmoothJoin
                        //    );
                    }
                    while (IsNumber(AllowComma));

                    last_cmd = 'A';
                    break;

                case 'z':
                case 'Z':
                    EnsureFigure();
                    _figure.IsClosed = IsClosed;
                    //context.SetClosedState(IsClosed);

                    _figureStarted = false;
                    last_cmd = 'Z';

                    _lastPoint = _lastStart; // Set reference point to be first point of current figure
                    break;

                default:
                    ThrowBadToken();
                    break;
            }

            if (null != _figure)
            {
                if (_figure.IsClosed)
                {
                    if (null == _pathGeometry)
                        _pathGeometry = new PathGeometry();

                    _pathGeometry.Figures.Add(_figure);

                    _figure = null;
                    first = true;
                }
            }


        }

        if (null != _figure)
        {
            if (null == _pathGeometry)
                _pathGeometry = new PathGeometry();

            if (!_pathGeometry.Figures.Contains(_figure))
                _pathGeometry.Figures.Add(_figure);

        }
        return _pathGeometry;
    }

    // Not used! Just here for the completeness.
    public string ParseBack(PathGeometry geometry)
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        IFormatProvider provider = new System.Globalization.CultureInfo("en-us");
        string format = null;

        sb.Append("F" + (geometry.FillRule == FillRule.EvenOdd ? "0" : "1") + " ");

        foreach (PathFigure figure in geometry.Figures)
        {
            sb.Append("M " + ((IFormattable)figure.StartPoint).ToString(format, provider) + " ");

            foreach (PathSegment segment in figure.Segments)
            {
                char separator = GetNumericListSeparator(provider);

                if (segment.GetType() == typeof(LineSegment))
                {
                    LineSegment _lineSegment = segment as LineSegment;

                    sb.Append("L " + ((IFormattable)_lineSegment.Point).ToString(format, provider) + " ");
                }
                else if (segment.GetType() == typeof(BezierSegment))
                {
                    BezierSegment _bezierSegment = segment as BezierSegment;

                    sb.Append(String.Format(provider,
                            "C{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "} ",
                            separator,
                            _bezierSegment.Point1,
                            _bezierSegment.Point2,
                            _bezierSegment.Point3
                            ));
                }
                else if (segment.GetType() == typeof(QuadraticBezierSegment))
                {
                    QuadraticBezierSegment _quadraticBezierSegment = segment as QuadraticBezierSegment;

                    sb.Append(String.Format(provider,
                            "Q{1:" + format + "}{0}{2:" + format + "} ",
                            separator,
                            _quadraticBezierSegment.Point1,
                            _quadraticBezierSegment.Point2));
                }
                else if (segment.GetType() == typeof(ArcSegment))
                {
                    ArcSegment _arcSegment = segment as ArcSegment;

                    sb.Append(String.Format(provider,
                            "A{1:" + format + "}{0}{2:" + format + "}{0}{3}{0}{4}{0}{5:" + format + "} ",
                            separator,
                            _arcSegment.Size,
                            _arcSegment.RotationAngle,
                            _arcSegment.IsLargeArc ? "1" : "0",
                            _arcSegment.SweepDirection == SweepDirection.Clockwise ? "1" : "0",
                            _arcSegment.Point));
                }
            }

            if (figure.IsClosed)
                sb.Append("Z");
        }

        return sb.ToString();
    }

    #region Privates
    private void SkipDigits(bool signAllowed)
    {
        // Allow for a sign 
        if (signAllowed && More() && ((_pathString[_curIndex] == '-') || _pathString[_curIndex] == '+'))
        {
            _curIndex++;
        }

        while (More() && (_pathString[_curIndex] >= '0') && (_pathString[_curIndex] <= '9'))
        {
            _curIndex++;
        }
    }

    private bool ReadBool()
    {
        SkipWhiteSpace(AllowComma);

        if (More())
        {
            _token = _pathString[_curIndex++];

            if (_token == '0')
            {
                return false;
            }
            else if (_token == '1')
            {
                return true;
            }
        }

        ThrowBadToken();

        return false;
    }

    private Point Reflect()
    {
        return new Point(2 * _lastPoint.X - _secondLastPoint.X,
                            2 * _lastPoint.Y - _secondLastPoint.Y);
    }

    private void EnsureFigure()
    {
        if (!_figureStarted)
        {
            _figure = new PathFigure();
            _figure.StartPoint = _lastStart;

            //_context.BeginFigure(_lastStart, IsFilled, !IsClosed);
            _figureStarted = true;
        }
    }

    private double ReadNumber(bool allowComma)
    {
        if (!IsNumber(allowComma))
        {
            ThrowBadToken();
        }

        bool simple = true;
        int start = _curIndex;

        //
        // Allow for a sign
        //
        // There are numbers that cannot be preceded with a sign, for instance, -NaN, but it's 
        // fine to ignore that at this point, since the CLR parser will catch this later.
        // 
        if (More() && ((_pathString[_curIndex] == '-') || _pathString[_curIndex] == '+'))
        {
            _curIndex++;
        }

        // Check for Infinity (or -Infinity).
        if (More() && (_pathString[_curIndex] == 'I'))
        {
            // 
            // Don't bother reading the characters, as the CLR parser will 
            // do this for us later.
            // 
            _curIndex = Math.Min(_curIndex + 8, _pathLength); // "Infinity" has 8 characters
            simple = false;
        }
        // Check for NaN 
        else if (More() && (_pathString[_curIndex] == 'N'))
        {
            // 
            // Don't bother reading the characters, as the CLR parser will
            // do this for us later. 
            //
            _curIndex = Math.Min(_curIndex + 3, _pathLength); // "NaN" has 3 characters
            simple = false;
        }
        else
        {
            SkipDigits(!AllowSign);

            // Optional period, followed by more digits 
            if (More() && (_pathString[_curIndex] == '.'))
            {
                simple = false;
                _curIndex++;
                SkipDigits(!AllowSign);
            }

            // Exponent
            if (More() && ((_pathString[_curIndex] == 'E') || (_pathString[_curIndex] == 'e')))
            {
                simple = false;
                _curIndex++;
                SkipDigits(AllowSign);
            }
        }

        if (simple && (_curIndex <= (start + 8))) // 32-bit integer
        {
            int sign = 1;

            if (_pathString[start] == '+')
            {
                start++;
            }
            else if (_pathString[start] == '-')
            {
                start++;
                sign = -1;
            }

            int value = 0;

            while (start < _curIndex)
            {
                value = value * 10 + (_pathString[start] - '0');
                start++;
            }

            return value * sign;
        }
        else
        {
            string subString = _pathString.Substring(start, _curIndex - start);

            try
            {
                return System.Convert.ToDouble(subString, _formatProvider);
            }
            catch (FormatException except)
            {
                throw new FormatException(string.Format("Unexpected character in path '{0}' at position {1}", _pathString, _curIndex - 1), except);
            }
        }
    }

    private bool IsNumber(bool allowComma)
    {
        bool commaMet = SkipWhiteSpace(allowComma);

        if (More())
        {
            _token = _pathString[_curIndex];

            // Valid start of a number
            if ((_token == '.') || (_token == '-') || (_token == '+') || ((_token >= '0') && (_token <= '9'))
                || (_token == 'I')  // Infinity
                || (_token == 'N')) // NaN 
            {
                return true;
            }
        }

        if (commaMet) // Only allowed between numbers
        {
            ThrowBadToken();
        }

        return false;
    }

    private Point ReadPoint(char cmd, bool allowcomma)
    {
        double x = ReadNumber(allowcomma);
        double y = ReadNumber(AllowComma);

        if (cmd >= 'a') // 'A' < 'a'. lower case for relative
        {
            x += _lastPoint.X;
            y += _lastPoint.Y;
        }

        return new Point(x, y);
    }

    private bool ReadToken()
    {
        SkipWhiteSpace(!AllowComma);

        // Check for end of string 
        if (More())
        {
            _token = _pathString[_curIndex++];

            return true;
        }
        else
        {
            return false;
        }
    }

    private bool More()
    {
        return _curIndex < _pathLength;
    }

    // Skip white space, one comma if allowed
    private bool SkipWhiteSpace(bool allowComma)
    {
        bool commaMet = false;

        while (More())
        {
            char ch = _pathString[_curIndex];

            switch (ch)
            {
                case ' ':
                case '\n':
                case '\r':
                case '\t': // SVG whitespace 
                    break;

                case ',':
                    if (allowComma)
                    {
                        commaMet = true;
                        allowComma = false; // one comma only
                    }
                    else
                    {
                        ThrowBadToken();
                    }
                    break;

                default:
                    // Avoid calling IsWhiteSpace for ch in (' ' .. 'z']
                    if (((ch > ' ') && (ch <= 'z')) || !Char.IsWhiteSpace(ch))
                    {
                        return commaMet;
                    }
                    break;
            }

            _curIndex++;
        }

        return commaMet;
    }

    private void ThrowBadToken()
    {
        throw new FormatException(string.Format("Unexpected character in path '{0}' at position {1}", _pathString, _curIndex - 1));
    } 
    #endregion
}

XAML

<Border Grid.Row="3"
        Grid.Column="3"
        BorderBrush="Yellow"
        BorderThickness="1"
        Height="200"
        Width="200">
    <local:IconControl Height="200"
                       Width="200"
                       Padding="20"
                       Background="White"
                       Foreground="Red"
                       Data="{StaticResource PrancingString}" />
</Border>
于 2013-11-06T16:45:40.763 回答
0

对我来说最简单的方法:

<ResourceDictionary ...>
    <Canvas x:Key="appbar_tools">
        <Path Fill="#FF000000" Data="F1 M 25.3333,42.75C 26.5189,42.75 27.6436,43.0106 28.6533,43.4777L 34.9459,37.185L 32.5825,34.8217L 30.3433,37.0609L 28.1042,34.8217L 29.0343,33.8915C 27.1425,33.1521 25.7233,31.6492 23.4735,29.3994C 18.836,24.7619 16.1846,19.8945 18.0395,18.0396C 19.8945,16.1846 23.9702,18.0444 28.6077,22.6819C 30.8575,24.9317 33.1521,27.1425 33.8915,29.0344L 34.8217,28.1042L 37.0608,30.3433L 34.8217,32.5825L 37.185,34.9459L 43.4777,28.6533C 43.0106,27.6436 42.75,26.5189 42.75,25.3333C 42.75,20.9611 46.2944,17.4167 50.6667,17.4167C 51.6877,17.4167 52.6636,17.61 53.5597,17.9619L 47.5,24.0216L 51.9783,28.5L 58.0381,22.4403C 58.39,23.3364 58.5833,24.3123 58.5833,25.3333C 58.5833,29.7056 55.0389,33.25 50.6667,33.25C 49.8136,33.25 48.9921,33.1151 48.2222,32.8654L 41.6634,39.4242L 50.8787,48.6395L 51.4384,48.0797L 56.8841,53.5253L 53.5253,56.8841L 48.0797,51.4384L 48.6395,50.8787L 39.4242,41.6634L 32.8654,48.2222C 33.1151,48.9921 33.25,49.8136 33.25,50.6667C 33.25,55.0389 29.7056,58.5833 25.3333,58.5833C 24.3123,58.5833 23.3364,58.39 22.4403,58.0381L 28.5,51.9783L 24.0217,47.5L 17.9619,53.5597C 17.61,52.6636 17.4167,51.6877 17.4167,50.6667C 17.4167,46.2944 20.9611,42.75 25.3333,42.75 Z "/>
    </Canvas>
</ResourceDictionary>

然后在您的控制下:

<Rectangle Width="50" Height="50" Fill="Black">
    <Rectangle.OpacityMask>
        <VisualBrush Stretch="Fill" Visual="{DynamicResource appbar_copy}" />
    </Rectangle.OpacityMask>
</Rectangle>
于 2016-05-09T14:15:56.197 回答