zoukankan      html  css  js  c++  java
  • 在WPF应用程序中从字体符号创建光标

    介绍 我必须为我正在工作的应用程序创建一个大的光标,因为我已经使用了符号字体,所以决定使用字体中的符号来表示光标。 请注意 自初始版本以来,代码已经进行了相当程度的更新,以修复游标的问题。光标中的图像没有正确居中,因此光标的实际大小不接近调用中指定的值。沙漏现在也有三张沙子向下的图像,然后旋转90度。 的代码 我开始尝试使用代码来从按钮等的字体生成图像,这包括在代码中,以及用于在示例中生成图像的代码。但这并没有起作用,我不得不在互联网上搜索,并且玩了很多。不幸的是,似乎有几个微软的绘图库,我想没有太多的思想投入到创建不同的。下面的代码最终是有效的。 GlyphRun的创建 这段代码与用于为示例中显示的按钮创建图像的代码类似,但略有不同,因为我需要的对象与WPF中的图像所需的对象略有不同。它的作用是将字体字符转换为位图: 隐藏,复制Code

    public static GlyphRun GetGlyphRun(double size, FontFamily fontFamily, string text)
    {
     Typeface typeface = new Typeface(fontFamily, FontStyles.Normal,
      FontWeights.Normal, FontStretches.Normal);
     GlyphTypeface glyphTypeface;
     if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
      throw new InvalidOperationException("No glyphtypeface found");
     ushort[] glyphIndexes = new ushort[text.Length];
     double[] advanceWidths = new double[text.Length];
    
     for (int n = 0; n < text.Length; n++)
     {
      advanceWidths[n] = glyphTypeface.AdvanceWidths[glyphIndexes[n]
       = GetGlyph(text[n], glyphTypeface)];
     }
     var centerX = (1 - advanceWidths[0]) * size / 2;
     Point origin = new Point(centerX, size * .85);
    
     GlyphRun glyphRun = new GlyphRun(glyphTypeface, 0, false, size,
       glyphIndexes, origin, advanceWidths, null, null, null, null,
       null, null);
    
     return glyphRun;
    }
    

    该方法使用另一种方法获取字符,并处理当字体没有与位置关联的字符时抛出的异常,用空格符号替换字符: 隐藏,复制Code

    private static ushort GetGlyph(char text, GlyphTypeface glyphTypeface)
    {
     try { return glyphTypeface.CharacterToGlyphMap[text]; }
     catch { return 42; }
    }
    

    在创建GlyphRun时,GetGlyphRun方法使用默认的FontSyle、FontWeight和FontStretch。它会获取一个字符串并创建一个包含所有字符的GlyphRun。 创建内存流对象 下一个方法用于创建游标对象。游标需要一种特定的二进制格式,因此使用一个MemoryStream来创建二进制对象(结构参见https://en.wikipedia.org/wiki/ICO_(file_format))。这也可以用不安全的代码完成,但是MemoryStream方法不需要不安全关键字就可以工作。 隐藏,收缩,复制Code

    private static Cursor CreateCursorObject(int size, double xHotPointRatio, double yHotPointRatio,
        BitmapSource rtb)
    {
     using (var ms1 = new MemoryStream())
     {
      var penc = new PngBitmapEncoder();
      penc.Frames.Add(BitmapFrame.Create(rtb));
      penc.Save(ms1);
    
      var pngBytes = ms1.ToArray();
      var byteCount = pngBytes.GetLength(0);
    
      //.cur format spec <a href="http://en.wikipedia.org/wiki/ICO_(file_format"><font color="#0066cc">http://en.wikipedia.org/wiki/ICO_(file_format</font></a>)
      using (var stream = new MemoryStream())
      {
       //ICONDIR Structure
       stream.Write(BitConverter.GetBytes((Int16) 0), 0, 2); //Reserved must be zero; 2 bytes
       stream.Write(BitConverter.GetBytes((Int16) 2), 0, 2); //image type 1 = ico 2 = cur; 2 bytes
       stream.Write(BitConverter.GetBytes((Int16) 1), 0, 2); //number of images; 2 bytes
       //ICONDIRENTRY structure
       stream.WriteByte(32); //image width in pixels
       stream.WriteByte(32); //image height in pixels
    
       stream.WriteByte(0); //Number of Colors. Should be 0 if the image doesn't use a color palette
       stream.WriteByte(0); //reserved must be 0
    
       stream.Write(BitConverter.GetBytes((Int16) (size*xHotPointRatio)), 0, 2);
       //2 bytes. In CUR format: Specifies the number of pixels from the left.
       stream.Write(BitConverter.GetBytes((Int16) (size*yHotPointRatio)), 0, 2);
       //2 bytes. In CUR format: Specifies the number of pixels from the top.
    
       //Specifies the size of the image's data in bytes
       stream.Write(BitConverter.GetBytes(byteCount), 0, 4);
       stream.Write(BitConverter.GetBytes((Int32) 22), 0, 4);
       //Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
       stream.Write(pngBytes, 0, byteCount); //write the png data.
       stream.Seek(0, SeekOrigin.Begin);
       return new System.Windows.Input.Cursor(stream);
      }
     }
    }
    

    用于翻转和旋转的变换 当发生水平或垂直翻转或旋转时,将调用TransformImage。 隐藏,复制Code

    private static void TransformImage(DrawingGroup drawingGroup, double angle, FlipValues flip)
    {
     if (flip == FlipValues.None && Math.Abs(angle) < .1) return;
     if (flip == FlipValues.None)
      drawingGroup.Transform = new RotateTransform(angle);
     if (Math.Abs(angle) < .1)
      drawingGroup.Transform = new ScaleTransform(flip == FlipValues.Vertical ? -1 : 1, flip == FlipValues.Horizontal ? -1 : 1);
     else
     {
      var transformGroup = new TransformGroup();
      transformGroup.Children.Add(new ScaleTransform(flip == FlipValues.Vertical ? -1 : 1, flip == FlipValues.Horizontal ? -1 : 1));
      transformGroup.Children.Add(new RotateTransform(angle));
      drawingGroup.Transform = transformGroup;
     }
    }
    

    基本公共方法调用 CreateCursor使用这两个方法返回使用FontFamily中指定符号的游标对象。这是创建游标对象时调用的公共方法: 隐藏,复制Code

    public static System.Windows.Input.Cursor CreateCursor(int size, double xHotPointRatio,
     double yHotPointRatio, FontFamily fontFamily, string symbol, Brush brush, double rotationAngle = 0)
    {
     var vis = new DrawingVisual();
     using (var dc = vis.RenderOpen())
     {
      dc.DrawGlyphRun(brush, GetGlyphRun(size, fontFamily, symbol));
      dc.Close();
     }/*CreateGlyphRun(symbol, fontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal)*/
     if (Math.Abs(rotationAngle) > .1)
       vis.Transform = new RotateTransform(rotationAngle, size / 2, size / 2);
     var renderTargetBitmap = new RenderTargetBitmap(size, size, 96, 96, PixelFormats.Pbgra32);
     renderTargetBitmap.Render(vis);
    
     return CreateCursorObject(size, xHotPointRatio, yHotPointRatio, renderTargetBitmap);
    }
    

    使用的代码 在WPF中,你可能想要在窗口初始化时设置光标: 隐藏,复制Code

    public MainWindow()
    {
     InitializeComponent();
     Mouse.OverrideCursor = FontSymbolCursor.CreateCursor(100, .5, .03, "arial",
      'A'.ToString, System.Windows.Media.Brushes.Black);
    }
    

    BaseWindow类 在示例中,MainWindow继承了BaseWindow类。 隐藏,复制Code

    <fontAwesomeImageSample:BaseWindow x:Class="FontAwesomeImageSample.MainWindow"
            xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation</a>"
            xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml</a>"
            xmlns:d="<a href="http://schemas.microsoft.com/expression/blend/2008">http://schemas.microsoft.com/expression/blend/2008</a>"
            xmlns:fontAwesomeImageSample="clr-namespace:FontAwesomeImageSample"
            xmlns:mc="<a href="http://schemas.openxmlformats.org/markup-compatibility/2006">http://schemas.openxmlformats.org/markup-compatibility/2006</a>"
            Title="Font Awesome Icon Image &amp; Cursor"
            Width="525"
            Height="350"
            mc:Ignorable="d">
     <Grid>
      <Button Margin="50"
              HorizontalAlignment="Center"
              VerticalAlignment="Center"
              Click="ButtonBase_OnClick">
       <fontAwesomeImageSample:FontSymbolImage Foreground="HotPink"
                                               FontFamily="{StaticResource FontAwesomeTtf}"
                           Flip="Horizontal"
                           Rotation="10"
                                               FontAwesomeSymbol="fa_bar_chart_o" />
      </Button>
     </Grid>
    </fontAwesomeImageSample:BaseWindow>

    BaseWindow类具有IsBusy DependencyProperty并创建一个箭头光标和几个Busy光标。当IsBusy从true变为false时,光标将从它的普通箭头变为沙漏,沙漏将清空并旋转。要完成动画,需要使用DispatchTimer。当IsBusy DependencyProperty设置为true时启动,当设置为false时停止: 隐藏,收缩,复制Code

     public class BaseWindow : Window
     {
      private const int CursorSize = 32;
      private readonly DispatcherTimer _updateTimer;
      private readonly System.Windows.Input.Cursor _normalCursor;
      private readonly System.Windows.Input.Cursor _busyCursor;
      private readonly System.Windows.Input.Cursor[] _busyCursors;
      private int _busyCursorNumber;  public BaseWindow()
      {
       _updateTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 1) };
       _updateTimer.Tick += UpdateBusyCursor;
       System.Windows.Input.Mouse.OverrideCursor = _normalCursor 
            = FontSymbolCursor.CreateCursor(CursorSize, .2, 0, "FontAwesome",
              FontSymbolImage.FontAwesomeSymbols.fa_mouse_pointer, System.Windows.Media.Brushes.Black);
       _busyCursors = new[] {
         FontSymbolCursor.CreateCursor(CursorSize, .5, .5, "FontAwesome",
           FontSymbolImage.FontAwesomeSymbols.fa_hourglass_start, System.Windows.Media.Brushes.Black),
         FontSymbolCursor.CreateCursor(CursorSize, .5, .5, "FontAwesome",
           FontSymbolImage.FontAwesomeSymbols.fa_hourglass_half, System.Windows.Media.Brushes.Black),
         FontSymbolCursor.CreateCursor(CursorSize, .5, .5, "FontAwesome",
           FontSymbolImage.FontAwesomeSymbols.fa_hourglass_end, System.Windows.Media.Brushes.Black),
         FontSymbolCursor.CreateCursor(CursorSize, .5, .5, "FontAwesome",
           FontSymbolImage.FontAwesomeSymbols.fa_hourglass_end, System.Windows.Media.Brushes.Black, 90.0)};
      }
    
      public static readonly DependencyProperty IsBusyProperty =
        DependencyProperty.Register("IsBusy", typeof(bool), typeof(BaseWindow), 
        new PropertyMetadata(false, PropertyChangedCallback));
      public bool IsBusy { 
        get { return (bool)GetValue(IsBusyProperty); } 
        set { SetValue(IsBusyProperty, value); } 
      }
    
      private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
       var window = (BaseWindow)d;
       if (window.IsBusy)
       {
        window._busyCursorNumber = 0;
        window._updateTimer.Start();
        System.Windows.Input.Mouse.OverrideCursor = window._busyCursors[0];
       }
       else
       {
        window._updateTimer.Stop();
        System.Windows.Input.Mouse.OverrideCursor = window._normalCursor;
       }
      }
    
      private void UpdateBusyCursor(object sender, EventArgs e)
      {
       _busyCursorNumber = ++_busyCursorNumber % _busyCursors.Length;
       System.Windows.Input.Mouse.OverrideCursor = _busyCursors[_busyCursorNumber];
      }
     }

    这个示例是一个简单的表单,只有一个大按钮,其中包含一个非常棒的字体字符。如果单击此按钮,光标将在两秒钟内变为一个旋转的沙漏。 上面光标的实际XAML为: 隐藏,复制Code

    <Button Margin="50"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Click="ButtonBase_OnClick">
     <fontAwesomeImageSample:FontSymbolImage Foreground="HotPink"
                                             FontFamily="{StaticResource FontAwesomeTtf}"
                                     Flip="Horizontal"
                                     Rotation="10"
                                             FontAwesomeSymbol="fa_bar_chart_o" />
    </Button>
    

    额外的 示例包括从字体符号创建WPF图像的代码。这是在为WPF创建一个从字体符号(字体Awesome)的图像文件 历史 更新05/12/2015:更新样例更改光标时按钮点击05/17/2016:更新代码与改进的等待光标实现05/20/2016:更新代码与新的BaseWindow控件 本文转载于:http://www.diyabc.com/frontweb/news286.html

  • 相关阅读:
    数据库表结构变动发邮件脚本
    .net程序打包部署
    无法登陆GitHub解决方法
    netbeans 打包生成 jar
    第一次值班
    RHEL6 纯命令行文本界面下安装桌面
    C语言中格式化输出,四舍五入类型问题
    I'm up to my ears
    How to boot ubuntu in text mode instead of graphical(X) mode
    the IP routing table under linux@school
  • 原文地址:https://www.cnblogs.com/Dincat/p/13437437.html
Copyright © 2011-2022 走看看