Основы HUD с использованием SharpDX: Как рисовать на поверхности экрана?

сценарий

Я планирую изучить основы того, как разработать собственный HUD для сторонней PC-игры (бесплатно, легально), я собираюсь разработать приложение, которое будет рисовать / показывать дополнительную информацию на экране (читая адреса памяти).).

Итак, я исследовал профессиональные проекты и обнаружил, что TurboHUD использует библиотеку SharpDX для рисования объектов / оверлеев / текста по экрану с использованием этой библиотеки на основе DirectX, и результаты очень хорошие (при рисовании он не теряет производительности в любой момент несколько объектов на экране), но, поскольку автор этого проекта не предоставляет источника, чтобы понять, как они это сделали, я пытаюсь научиться использовать ту же профессиональную технику.

Вопрос

Если я нахожусь на неправильном пути, или я упустил какие-то другие лучшие альтернативы, чем SharpDX, чтобы разработать это, пожалуйста, дайте мне знать.

Мой главный вопрос:

В C# или предпочтительно VB.Net, как я мог бы просто эффективно рисовать пользовательскую строку на экране, используя SharpDX?

Обратите внимание, что я мог установить возможность формы равной 0, но я думаю, что она должна существовать надлежащим образом, и я прошу знать, как правильно рисовать прямо на экране "рабочего стола".

Я рассчитываю запустить эту PC-Game, а затем запустить мой собственный HUD, который будет рисовать на поверхности экрана "рабочего стола", чтобы добавить дополнительную информацию об игре, надеюсь, вы меня понимаете.

Исследование

Я должен уточнить, что я совершенно неопытен в этом виде библиотек DirectX, и я использую пакет примеров SharpDX, чтобы попытаться изучить его использование.

Так как все примеры в C#, его более сложно изучить в VB.Net.

Внутри пакета примеров SharpDX есть проект C# ' AdvancedTextRenderingApp ', но, как следует из его названия, это расширенный пример, а также он создает собственную форму (SharpDX.Windows.RenderForm) рисовать на этой форме.

Это перевод кода VB.Net того проекта C#, о котором я упоминал:

http://pastebin.com/KG2c3v09


ОБНОВИТЬ:

Просто с намерением прокомментировать исследование, которое я сделал:

Недавно я обнаружил этот полезный репозиторий GitHub, однако он не компилируется в Visual Studio 2015 (поскольку отсутствует использование пространства имен, что при добавлении они генерируют больше ошибок компилятора), а также ориентирован на опытных пользователей SharpDX, и после анализа полной выборки я до сих пор не имею ни малейшего представления о том, как писать / рисовать поверх поверхности окна процесса 3-ей части... также синтаксис C# затрудняет мне общее понимание использования SharpDX, потому что автор также делал пользовательские реализации большого разнообразия SharpDX члены, тогда... Я более чем потерян со всеми этими примерами.

Официальные образцы SharpDX - еще одна из тех вещей, которая кажется очень полезной... может быть, для опытных пользователей. Некоторые примеры, кажется, демонстрируют, как визуализировать собственное окно / поверхность (с 500 утомительными и непонятными строками кода, чтобы сделать это. И их пример " Hello world " для меня - кошмар.) Однако то, что я хотел бы достичь как Я объяснил в своем вопросе, что нужно рисовать на поверхности существующего окна другого процесса, и я знаю, что для этого мне понадобится с нуля отрисовать "поверхность" с помощью SharpDX, а затем расположить ее в целевом окне, затем сделайте невидимой поверхность, затем нарисуйте ее, но я не знаю, как это сделать.

1 ответ

Мне потребовалось некоторое время, чтобы найти, как загрузить шрифт в XNA для рисования текста, но все работает отлично.

Вам нужно 4 вещи:

  1. Сделать форму сверху
  2. Распространение стиля Aero Glass на всю форму (для прозрачности для работы)
  3. Инициализация XNA, Microsoft XNA Game Studio 4.0
  4. Нарисуйте текстуру и текст

Одно ограничение

Игра не должна быть в полноэкранном режиме. Так же, как ограничение TurboHUD

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using System.Runtime.InteropServices;
using System.Drawing;

namespace XNATransparentWindow
{
    public partial class Form1 : Form
    {
        private ContentBuilder contentBuilder;

        public Form1()
        {
            InitializeComponent();

            TopMost = true;

            FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;

            MARGINS margins = new MARGINS();

            margins.leftWidth = 0;
            margins.rightWidth = 0;
            margins.topHeight = this.Width;
            margins.bottomHeight = this.Height;

            // Extend aero glass style to whole form
            DwmExtendFrameIntoClientArea(this.Handle, ref margins);

            //Load XNA directX
            this.contentBuilder = new ContentBuilder();

            graphicsDeviceService = GraphicsDeviceService.AddRef(Handle, ClientSize.Width, ClientSize.Height);

            //Register the service, so components like ContentManager can find it.
            services.AddService<IGraphicsDeviceService>(graphicsDeviceService);

            //Get the Graphics device
            dev = graphicsDeviceService.GraphicsDevice;
            if(dev == null){/*error message*/}

            //Load texture
            int bufferSize;
            System.IO.MemoryStream memoryStream;
            Bitmap img;

            using (img  = new Bitmap(@"C:\...\.png"))
            {
                bufferSize = img.Height * img.Width * 4;

                memoryStream = new System.IO.MemoryStream(bufferSize);
                img.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);

                memoryStream.Seek(0, SeekOrigin.Begin);
                texture = Texture2D.FromStream(dev, memoryStream, img.Width, img.Height, false);

                memoryStream.Close();
                if(texture == null){/*error message*/}
            }

            //Create sprite
            s_Batch = new SpriteBatch(dev);
            if(s_Batch == null){/*error message*/}

            FontPos = new Vector2(270.0F, 110.0F);

            //Load font
            contentManager = new ContentManager(services, this.contentBuilder.OutputDirectory);

            this.contentBuilder.Clear();

            this.contentBuilder.Add(@"C:\...\my_font1.spritefont","my_font1", "FontDescriptionImporter", "FontDescriptionProcessor");

            //Build spritefont to get the .xbn file
            string error = this.contentBuilder.Build();

            //If build fail
            if (!String.IsNullOrEmpty(error))
            {
                MessageBox.Show(error);
                return;
            }

            //Load the .xbn file
            Font1 = contentManager.Load<SpriteFont>("my_font1");
            if(Font1 == null){/*error message*/}
        }

        [DllImport("dwmapi.dll")]
        static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margin);

        [DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);

        [StructLayout(LayoutKind.Sequential)]
        public struct MARGINS
        {
            public int leftWidth;
            public int rightWidth;
            public int topHeight;
            public int bottomHeight;
        }

        public ServiceContainer Services
        {
            get { return services; }
        }

        ServiceContainer services = new ServiceContainer();

        GraphicsDevice dev;
        SpriteFont Font1;
        Vector2 FontPos;
        SpriteBatch s_Batch;
        Texture2D texture;
        ContentManager contentManager;
        GraphicsDeviceService graphicsDeviceService;
        private const UInt32 WM_NCLBUTTONDOWN = 0xA1;
        private const Int32 HTCAPTION = 0x2;

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                this.Close();
            }
            else //to move the form
            {
                this.Capture = false;
                SendMessage(this.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
            }
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            //There are two buffers. One offscreen-backbuffer and the 
            //frontbuffer which is the actual form in this example. The
            //drawings are done to the backbuffer and in the end the two
            //buffers flip. The backbuffer becomes frontbuffer and the
            //frontbuffer becomes backbuffer.

            //The drawing should start when the last resource is loaded.
            //Since Font1 is the last one in this example I check for this
            if (Font1 == null)
            {
                return;
            }

            //clear the backbuffer with transparent color.
            dev.Clear(Microsoft.Xna.Framework.Color.Transparent);
            //Do all your drawings here

            //draw texture and text with the sprite
            s_Batch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);

            s_Batch.Draw(texture, new Microsoft.Xna.Framework.Rectangle(0, 0, this.Width, this.Height), Microsoft.Xna.Framework.Color.White);

            s_Batch.DrawString(Font1, @"XNA FRAMEWORK", FontPos, Microsoft.Xna.Framework.Color.Black);

            s_Batch.End();

            //here the flip is performed
            dev.Present();
        }

        //Release resources
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            graphicsDeviceService.GraphicsDevice.Dispose();
            graphicsDeviceService.Release(true);
            s_Batch.Dispose();
            texture.Dispose();
        }
    }
}

Следующие классы просто скопированы из примера компилятора содержимого XNA 4.0, который я нашел (с небольшими изменениями). Они используются только для загрузки шрифта для рисования текста:

GraphicsDeviceService.cs

#region File Description
//-----------------------------------------------------------------------------
// GraphicsDeviceService.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
#endregion

// The IGraphicsDeviceService interface requires a DeviceCreated event, but we
// always just create the device inside our constructor, so we have no place to
// raise that event. The C# compiler warns us that the event is never used, but
// we don't care so we just disable this warning.
#pragma warning disable 67

namespace XNATransparentWindow
{
    /// <summary>
    /// Helper class responsible for creating and managing the GraphicsDevice.
    /// All GraphicsDeviceControl instances share the same GraphicsDeviceService,
    /// so even though there can be many controls, there will only ever be a single
    /// underlying GraphicsDevice. This implements the standard IGraphicsDeviceService
    /// interface, which provides notification events for when the device is reset
    /// or disposed.
    /// </summary>
    class GraphicsDeviceService : IGraphicsDeviceService
    {
        #region Fields

        // Singleton device service instance.
        static GraphicsDeviceService singletonInstance;

        // Keep track of how many controls are sharing the singletonInstance.
        static int referenceCount;

        #endregion

        /// <summary>
        /// Constructor is private, because this is a singleton class:
        /// client controls should use the public AddRef method instead.
        /// </summary>
        GraphicsDeviceService(IntPtr windowHandle, int width, int height)
        {
            parameters = new PresentationParameters();
            parameters.BackBufferWidth = Math.Max(width, 1);
            parameters.BackBufferHeight = Math.Max(height, 1);
            parameters.BackBufferFormat = SurfaceFormat.Vector4; // SurfaceFormat.Color;
            parameters.DeviceWindowHandle = windowHandle;
            parameters.PresentationInterval = PresentInterval.Immediate;
            parameters.IsFullScreen = false;

            graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, parameters);
        }

        /// <summary>
        /// Gets a reference to the singleton instance.
        /// </summary>
        public static GraphicsDeviceService AddRef(IntPtr windowHandle,
                                                   int width, int height)
        {
            // Increment the "how many controls sharing the device" reference count.
            if (Interlocked.Increment(ref referenceCount) == 1)
            {
                // If this is the first control to start using the
                // device, we must create the singleton instance.
                singletonInstance = new GraphicsDeviceService(windowHandle,
                                                              width, height);
            }

            return singletonInstance;
        }


        /// <summary>
        /// Releases a reference to the singleton instance.
        /// </summary>
        public void Release(bool disposing)
        {
            // Decrement the "how many controls sharing the device" reference count.
            if (Interlocked.Decrement(ref referenceCount) == 0)
            {
                // If this is the last control to finish using the
                // device, we should dispose the singleton instance.
                if (disposing)
                {
                    if (DeviceDisposing != null)
                        DeviceDisposing(this, EventArgs.Empty);

                    graphicsDevice.Dispose();
                }

                graphicsDevice = null;
            }
        }


        /// <summary>
        /// Resets the graphics device to whichever is bigger out of the specified
        /// resolution or its current size. This behavior means the device will
        /// demand-grow to the largest of all its GraphicsDeviceControl clients.
        /// </summary>
        public void ResetDevice(int width, int height)
        {
            if (DeviceResetting != null)
                DeviceResetting(this, EventArgs.Empty);

            parameters.BackBufferWidth = Math.Max(parameters.BackBufferWidth, width);
            parameters.BackBufferHeight = Math.Max(parameters.BackBufferHeight, height);

            graphicsDevice.Reset(parameters);

            if (DeviceReset != null)
                DeviceReset(this, EventArgs.Empty);
        }

        /// <summary>
        /// Gets the current graphics device.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get { return graphicsDevice; }
        }

        GraphicsDevice graphicsDevice;

        // Store the current device settings.
        PresentationParameters parameters;

        // IGraphicsDeviceService events.
        public event EventHandler<EventArgs> DeviceCreated;
        public event EventHandler<EventArgs> DeviceDisposing;
        public event EventHandler<EventArgs> DeviceReset;
        public event EventHandler<EventArgs> DeviceResetting;
    }
}

ServiceContainer.cs

#region File Description
//-----------------------------------------------------------------------------
// ServiceContainer.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
#endregion

namespace XNATransparentWindow
{
    /// <summary>
    /// Container class implements the IServiceProvider interface. This is used
    /// to pass shared services between different components, for instance the
    /// ContentManager uses it to locate the IGraphicsDeviceService implementation.
    /// </summary>
    public class ServiceContainer : IServiceProvider
    {
        Dictionary<Type, object> services = new Dictionary<Type, object>();


        /// <summary>
        /// Adds a new service to the collection.
        /// </summary>
        public void AddService<T>(T service)
        {
            services.Add(typeof(T), service);
        }

        /// <summary>
        /// Looks up the specified service.
        /// </summary>
        public object GetService(Type serviceType)
        {
            object service;

            services.TryGetValue(serviceType, out service);

            return service;
        }
    }
}

ContentBuilder.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;

namespace XNATransparentWindow
{
    public class ContentBuilder : IDisposable
    {
        #region Fields

        // What importers or processors should we load?
        const string xnaVersion = ", Version=4.0.0.0, PublicKeyToken=842cf8be1de50553";

        static string[] pipelineAssemblies =
        {
            "Microsoft.Xna.Framework.Content.Pipeline.FBXImporter" + xnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.XImporter" + xnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.TextureImporter" + xnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.EffectImporter" + xnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.AudioImporters" + xnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.VideoImporters" + xnaVersion,

            // If you want to use custom importers or processors from
            // a Content Pipeline Extension Library, add them here.
            //
            // If your extension DLL is installed in the GAC, you should refer to it by assembly
            // name, eg. "MyPipelineExtension, Version=1.0.0.0, PublicKeyToken=1234567812345678".
            //
            // If the extension DLL is not in the GAC, you should refer to it by
            // file path, eg. "c:/MyProject/bin/MyPipelineExtension.dll".
        };
        // MSBuild objects used to dynamically build content.
        Project buildProject;
        ProjectRootElement projectRootElement;
        BuildParameters buildParameters;
        List<ProjectItem> projectItems = new List<ProjectItem>();
        //ErrorLogger errorLogger;

        // Temporary directories used by the content build.
        string buildDirectory;
        string processDirectory;
        string baseDirectory;

        // Generate unique directory names if there is more than one ContentBuilder.
        static int directorySalt;

        // Have we been disposed?
        bool isDisposed;

        #endregion

        #region Properties

        /// Gets the output directory, which will contain the generated .xnb files.
        public string OutputDirectory
        {
            get { return Path.Combine(buildDirectory, "bin/Content"); }
        }

        #endregion

        #region Initialization
        /// Creates a new content builder.
        public ContentBuilder()
        {
            CreateTempDirectory();
            CreateBuildProject();
        }

        /// Finalizes the content builder.
        ~ContentBuilder()
        {
            Dispose(false);
        }

        /// Disposes the content builder when it is no longer required.
        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        /// Implements the standard .NET IDisposable pattern.
        protected virtual void Dispose(bool disposing)
        {
            if (!isDisposed)
            {
                isDisposed = true;

                DeleteTempDirectory();
            }
        }

        #endregion

        #region MSBuild
        /// Creates a temporary MSBuild content project in memory.
        void CreateBuildProject()
        {
            string projectPath = Path.Combine(buildDirectory, "content.contentproj");
            string outputPath = Path.Combine(buildDirectory, "bin");

            // Create the build project.
            projectRootElement = ProjectRootElement.Create(projectPath);

            // Include the standard targets file that defines how to build XNA Framework content.
            projectRootElement.AddImport("$(MSBuildExtensionsPath)\\Microsoft\\XNA Game Studio\\" +
                                         "v4.0\\Microsoft.Xna.GameStudio.ContentPipeline.targets");

            buildProject = new Project(projectRootElement);

            buildProject.SetProperty("XnaPlatform", "Windows");
            buildProject.SetProperty("XnaProfile", "Reach");
            buildProject.SetProperty("XnaFrameworkVersion", "v4.0");
            buildProject.SetProperty("Configuration", "Release");
            buildProject.SetProperty("OutputPath", outputPath);

            // Register any custom importers or processors.
            foreach (string pipelineAssembly in pipelineAssemblies)
            {
                buildProject.AddItem("Reference", pipelineAssembly);
            }

            // Hook up our custom error logger.
            //errorLogger = new ErrorLogger();

            buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection);
            //buildParameters.Loggers = new ILogger[] { errorLogger };
        }

        /// Adds a new content file to the MSBuild project. The importer and
        /// processor are optional: if you leave the importer null, it will
        /// be autodetected based on the file extension, and if you leave the
        /// processor null, data will be passed through without any processing.
        public void Add(string filename, string name, string importer, string processor)
        {
            ProjectItem item = buildProject.AddItem("Compile", filename)[0];

            item.SetMetadataValue("Link", Path.GetFileName(filename));
            item.SetMetadataValue("Name", name);

            if (!string.IsNullOrEmpty(importer))
                item.SetMetadataValue("Importer", importer);

            if (!string.IsNullOrEmpty(processor))
                item.SetMetadataValue("Processor", processor);

            projectItems.Add(item);
        }

        /// Removes all content files from the MSBuild project.
        public void Clear()
        {
            buildProject.RemoveItems(projectItems);

            projectItems.Clear();
        }

        /// Builds all the content files which have been added to the project,
        /// dynamically creating .xnb files in the OutputDirectory.
        /// Returns an error message if the build fails.
        public string Build()
        {
            // Create and submit a new asynchronous build request.
            BuildManager.DefaultBuildManager.BeginBuild(buildParameters);

            BuildRequestData request = new BuildRequestData(buildProject.CreateProjectInstance(), new string[0]);
            BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(request);

            submission.ExecuteAsync(null, null);

            // Wait for the build to finish.
            submission.WaitHandle.WaitOne();

            BuildManager.DefaultBuildManager.EndBuild();

            // If the build failed, return an error string.
            if (submission.BuildResult.OverallResult == BuildResultCode.Failure)
            {
                //return string.Join("\n", errorLogger.Errors.ToArray());
            }

            return null;
        }

        #endregion

        #region Temp Directories

        /// Creates a temporary directory in which to build content.
        void CreateTempDirectory()
        {
            // Start with a standard base name:
            //
            //  %temp%\WinFormsContentLoading.ContentBuilder

            baseDirectory = Path.Combine(Path.GetTempPath(), GetType().FullName);

            // Include our process ID, in case there is more than
            // one copy of the program running at the same time:
            //
            //  %temp%\WinFormsContentLoading.ContentBuilder\<ProcessId>

            int processId = Process.GetCurrentProcess().Id;

            processDirectory = Path.Combine(baseDirectory, processId.ToString());

            // Include a salt value, in case the program
            // creates more than one ContentBuilder instance:
            //
            //  %temp%\WinFormsContentLoading.ContentBuilder\<ProcessId>\<Salt>

            directorySalt++;

            buildDirectory = Path.Combine(processDirectory, directorySalt.ToString());

            // Create our temporary directory.
            Directory.CreateDirectory(buildDirectory);

            PurgeStaleTempDirectories();
        }

        /// <summary>
        /// Deletes our temporary directory when we are finished with it.
        /// </summary>
        void DeleteTempDirectory()
        {
            Directory.Delete(buildDirectory, true);

            // If there are no other instances of ContentBuilder still using their
            // own temp directories, we can delete the process directory as well.
            if (Directory.GetDirectories(processDirectory).Length == 0)
            {
                Directory.Delete(processDirectory);

                // If there are no other copies of the program still using their
                // own temp directories, we can delete the base directory as well.
                if (Directory.GetDirectories(baseDirectory).Length == 0)
                {
                    Directory.Delete(baseDirectory);
                }
            }
        }


        /// <summary>
        /// Ideally, we want to delete our temp directory when we are finished using
        /// it. The DeleteTempDirectory method (called by whichever happens first out
        /// of Dispose or our finalizer) does exactly that. Trouble is, sometimes
        /// these cleanup methods may never execute. For instance if the program
        /// crashes, or is halted using the debugger, we never get a chance to do
        /// our deleting. The next time we start up, this method checks for any temp
        /// directories that were left over by previous runs which failed to shut
        /// down cleanly. This makes sure these orphaned directories will not just
        /// be left lying around forever.
        /// </summary>
        void PurgeStaleTempDirectories()
        {
            // Check all subdirectories of our base location.
            foreach (string directory in Directory.GetDirectories(baseDirectory))
            {
                // The subdirectory name is the ID of the process which created it.
                int processId;

                if (int.TryParse(Path.GetFileName(directory), out processId))
                {
                    try
                    {
                        // Is the creator process still running?
                        Process.GetProcessById(processId);
                    }
                    catch (ArgumentException)
                    {
                        // If the process is gone, we can delete its temp directory.
                        Directory.Delete(directory, true);
                    }
                }
            }
        }

        #endregion
    }
}

Загружаемый шрифт представляет собой файл XML: any_name.spritefont

Пример:

<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">

    <!--
    Modify this string to change the font that will be imported.
    -->
    <FontName>Segoe UI Mono</FontName>

    <!--
    Size is a float value, measured in points. Modify this value to change
    the size of the font.
    -->
    <Size>14</Size>

    <!--
    Spacing is a float value, measured in pixels. Modify this value to change
    the amount of spacing in between characters.
    -->
    <Spacing>0</Spacing>

    <!--
    UseKerning controls the layout of the font. If this value is true, kerning information
    will be used when placing characters.
    -->
    <UseKerning>true</UseKerning>

    <!--
    Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
    and "Bold, Italic", and are case sensitive.
    -->
    <Style>Regular</Style>

    <!--
    If you uncomment this line, the default character will be substituted if you draw
    or measure text that contains characters which were not included in the font.
    -->
    <!-- <DefaultCharacter>*</DefaultCharacter> -->

    <!--
    CharacterRegions control what letters are available in the font. Every
    character from Start to End will be built and made available for drawing. The
    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
    character set. The characters are ordered according to the Unicode standard.
    See the documentation for more information.
    -->
    <CharacterRegions>
      <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>

Если вы хотите, чтобы форма проходила по клику, вам нужно добавить в форму случайный цвет прозрачности ключа и:

[DllImport("user32.dll", EntryPoint="GetWindowLong")]
static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

В public Form1()

IntPtr initialStyle = GetWindowLongPtr(this.Handle, -20);
SetWindowLong(this.Handle, -20, (IntPtr)((int)initialStyle | 0x20));

Не стесняйтесь исправлять любые ошибки, которые я сделал.

Другие вопросы по тегам