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

  • 相关阅读:
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-点击激活配置进入到运行模式直接死机或蓝屏怎么办
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-有时候项目会无法编译,重新生成就自动卡死或者自动退出怎么办
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-为什么无法打开官方范例的项目,打开tszip文件时提示尝试越过结尾怎么办
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-为什么没有自动识别成标准FBD功能块
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-如何在同一台PC上运行多个TwinCAT程序
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-如何在初始化的时候写入参数
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-如何在程序中添加注释
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-如何在初始化的时候写入参数
    倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)如何在TwinCAT Scope中做变量监控
    js文件中获取${pageContext.request.contextPath}
  • 原文地址:https://www.cnblogs.com/Dincat/p/13437437.html
Copyright © 2011-2022 走看看