Can one render text using DirectWrite to a PictureBox in a WinForm app?
I'm using SharpDX and have gone through the DirectWrite samples trying to build the simplest working case I can.
I created a Form and added only a pictureBox to it. Then the following code. The form displays, but nothing visible with the PictureBox.
Any guidance would be much appreciated.
Thanks!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
//using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharpDX.Direct2D1;
using SharpDX.DXGI;
using SharpDX;
using SharpDX.DirectWrite;
using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Factory = SharpDX.Direct2D1.Factory;
namespace d2dwTextEdit
{
public partial class Form1 : Form
{
public Factory Factory2D { get; private set; }
public SharpDX.DirectWrite.Factory FactoryDWrite { get; private set; }
public WindowRenderTarget RenderTarget2D { get; private set; }
public SolidColorBrush SceneColorBrush { get; private set; }
public TextFormat TextFormat { get; private set; }
public SharpDX.RectangleF ClientRectangle { get; private set; }
public Form1()
{
InitializeComponent();
Initialize();
Render();
}
protected void Initialize()
{
Factory2D = new SharpDX.Direct2D1.Factory();
FactoryDWrite = new SharpDX.DirectWrite.Factory();
HwndRenderTargetProperties properties = new HwndRenderTargetProperties();
properties.Hwnd = pictureBox1.Handle;
properties.PixelSize = new System.Drawing.Size(pictureBox1.Width, pictureBox1.Height);
properties.PresentOptions = PresentOptions.None;
TextFormat = new TextFormat(FactoryDWrite, "Calibri", 30) { TextAlignment = TextAlignment.Center, ParagraphAlignment = ParagraphAlignment.Center };
RenderTarget2D = new WindowRenderTarget(Factory2D, new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)), properties);
RenderTarget2D.AntialiasMode = AntialiasMode.PerPrimitive;
RenderTarget2D.TextAntialiasMode = TextAntialiasMode.Cleartype;
ClientRectangle = new RectangleF(0, 0, pictureBox1.Width, pictureBox1.Height);
SceneColorBrush = new SolidColorBrush(RenderTarget2D, Colors.White);
SceneColorBrush.Color = Colors.Black;
}
private void Render()
{
RenderTarget2D.Clear(Colors.White);
RenderTarget2D.DrawText("Hello Marshall", TextFormat, ClientRectangle, SceneColorBrush);
}
}
}
Hi I sort of followed your question example but I finally managed to work it out as I would like to independently start writing games, all the draw methods have to be within the beginDraw and endDraw methods of the RenderTarget class'. you also need to implement the RenderLoop class this will give you the callback loop delegate. Apologies for the crudeness of my code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using SharpDX.Direct3D;
using SharpDX.Direct2D1;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using SharpDX.DXGI;
using SharpDX;
using SharpDX.Windows;
using System.Globalization;
using SharpDX.DirectWrite;
namespace dx11
{
public partial class Form1 : Form
{
private SharpDX.Direct3D10.Device _mDevice = null;
WindowRenderTarget wndRender = null;
SharpDX.Direct2D1.Factory fact = new SharpDX.Direct2D1.Factory(SharpDX.Direct2D1.FactoryType.SingleThreaded);
SolidColorBrush scenebrush;
RenderTargetProperties rndTargProperties;
HwndRenderTargetProperties hwndProperties;
SharpDX.Windows.RenderForm form = new RenderForm();
RenderLoop.RenderCallback callback;
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
form.Width = 600;
form.Height = 600;
test();
callback = new RenderLoop.RenderCallback(Render);
RenderLoop.Run(form, callback);
}
private void test()
{
rndTargProperties = new RenderTargetProperties(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied));
hwndProperties = new HwndRenderTargetProperties();
hwndProperties.Hwnd = form.Handle;
hwndProperties.PixelSize = new SharpDX.DrawingSize(form.ClientSize.Width, form.ClientSize.Height);
hwndProperties.PresentOptions = PresentOptions.None;
wndRender = new WindowRenderTarget(fact, rndTargProperties, hwndProperties);
scenebrush = new SolidColorBrush(wndRender, Colors.Red);
// scenebrush.Color = Colors.Cornsilk;
form.Show();
}
public void Render()
{
wndRender.BeginDraw();
wndRender.Clear(Colors.DarkBlue);
wndRender.DrawRectangle(new SharpDX.RectangleF(10, 10, 50, 50), scenebrush, 2.00F);
wndRender.Flush();
wndRender.EndDraw();
}
}
}
This should create a blue form with a red square in the top left corner.
You don't actually want to render "into the PictureBox" with an HwndRenderTarget. You actually want to render into the Bitmap that the PictureBox has the job of rendering. You can do that by creating a "shared bitmap" (I'm not familiar with SharpDX specifically but the underlying method is ID2D1RenderTarget::CreateSharedBitmap()), rendering into that, and then invalidating the PictureBox after you call EndDraw().
Related
I'm trying to make a simple 2d game with C# using forms but when i run it at 60 fps it uses 50% of my CPU and doesn't use my GPU at all. I've been looking for hours and can't find another way to do it. How do i get it to run on my GPU?
Also I'm using a thread to wait in between frames with Thread.Sleep(15). I don't know if this is a good way to do it.
I was following this video for the window https://www.youtube.com/watch?v=JnGM1p2vsbE
This is my code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
namespace RadiantEngine
{
class Canvas : Form
{
public Canvas()
{
this.DoubleBuffered = true;
}
}
public abstract class RadiantWindow
{
public Vector2 ScreenSize;
public string Title;
private Canvas Window;
private Thread GameLoopThread;
public float num;
Bitmap image = (Bitmap)Image.FromFile("F:/VS/RadiantEngine/RadiantEngine/dog.png");
public RadiantWindow(Vector2 ScreenSize, string Title)
{
this.ScreenSize = ScreenSize;
this.Title = Title;
EngineInit();
}
public RadiantWindow(float x, float y, string Title)
{
Vector2 _ScreenSize = new Vector2();
this.ScreenSize = _ScreenSize;
this.Title = Title;
EngineInit();
}
void EngineInit()
{
Window = new Canvas();
Window.Text = Title;
Window.Size = new Size((int)ScreenSize.x, (int)ScreenSize.y);
Window.Paint += Renderer;
GameLoopThread = new Thread(GameLoop);
GameLoopThread.Start();
Application.Run(Window);
}
private void Renderer(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(Color.Aqua);
g.DrawImage(RadiantEngine.engine.renderer.FrameRender(), 0, 0);
}
void GameLoop()
{
OnLoad();
while (GameLoopThread.IsAlive)
{
try
{
OnDraw();
Window.BeginInvoke((MethodInvoker)delegate { Window.Refresh(); });
}
catch
{
Console.WriteLine("Game Is Loading");
}
Thread.Sleep(15);
}
}
public abstract void OnDraw();
public abstract void OnLoad();
}
}
Thanks for any help.
My code for Form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace YAHTZEE
{
public partial class GameMainWindow : Form
{
public GameMainWindow()
{
InitializeComponent();
}
private void buttonRollDice_Click(object sender, EventArgs e)
{
DiceManager dm = new DiceManager();
dm.intDice_1_Roll();
dm.intDice_2_Roll();
dm.intDice_3_Roll();
dm.intDice_4_Roll();
dm.intDice_5_Roll();
Class1 c1 = new Class1();
c1.buttonDice_1Manager();
}
}
}
My class for generating random number for all the dice.
This works perfectly fine.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace YAHTZEE
{
public class DiceManager
{
Random rnd = new Random();
public int intDice_1_Roll()
{
int intDice_1 = rnd.Next(1, 7);
return intDice_1;
}
public int intDice_2_Roll()
{
int intDice_2 = rnd.Next(1, 7);
return intDice_2;
}
public int intDice_3_Roll()
{
int intDice_3 = rnd.Next(1, 7);
return intDice_3;
}
public int intDice_4_Roll()
{
int intDice_4 = rnd.Next(1, 7);
return intDice_4;
}
public int intDice_5_Roll()
{
int intDice_5 = rnd.Next(1, 7);
return intDice_5;
}
}
}
Class to change button background image.
This is where my problem is, the code works if I put it on my form but it does nothing when I make it a method on another class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YAHTZEE.Properties;
namespace YAHTZEE
{
class Class1 : GameMainWindow
{
public void buttonDice_1Manager()
{
DiceManager dm = new DiceManager();
if (dm.intDice_1_Roll() == 1)
{
buttonDice_1.BackgroundImage = Properties.Resources.Dice1;
}
}
}
}
Am I missing something?
P.S. I want them to be separated because there are a lot of things to consider making my code very long.
In class1, try changing your method to:
public System.Drawing.Bitmap dice1Manager(int diceRoll)
{
if(diceRoll == 1)
return Properties.Resources.Dice1;
}
In your main class, change what you have to:
private void buttonRollDice_Click(object sender, EventArgs e)
{
DiceManager dm = new DiceManager();
Class1 c1 = new Class1();
button_dice1.BackgroundImage = c1.dice1Manager(dm.intDice_1_Roll());
dm.intDice_2_Roll();
dm.intDice_3_Roll();
dm.intDice_4_Roll();
dm.intDice_5_Roll();
}
My aim is to save the all form data via button click (as opposed to upon closing). To that end, I've used the example given in the following thread. Saving the form state then opening it back up in the same state
I've tried to adapt my code to the best of my ability, but nothing happens, and there are no errors shown. What am I doing wrong?
Here's the relevant parts of my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Serialization;
namespace WindowsFormsApplication1
{
public partial class frmPayroll : Form
{
SaveData sd = new SaveData();
public frmPayroll()
{
InitializeComponent();
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
writeConfig();
}
private void writeConfig()
{
using (StreamWriter sw = new StreamWriter("config.xml"))
{
sd.Married = rdoMarr.Checked;
sd.PayPd = cbPayPd.Text;
sd.Allow = cbAllow.Text;
sd.Gross = txtGross.Text;
sd.Fit = txtFit.Text;
sd.Soc = txtSoc.Text;
sd.Med = txtMed.Text;
sd.NetPay = txtNet.Text;
sd.PayPd = cbPayPd.Text;
XmlSerializer ser = new XmlSerializer(typeof(SaveData));
ser.Serialize(sw, sd);
}
}
private void frmPayroll_Load(object sender, EventArgs e)
{
if (File.Exists("config.xml"))
{
loadConfig();
}
sd.Married = rdoMarr.Checked;
sd.PayPd = cbPayPd.Text;
sd.Allow = cbAllow.Text;
sd.Gross = txtGross.Text;
sd.Fit = txtFit.Text;
sd.Soc = txtSoc.Text;
sd.Med = txtMed.Text;
sd.NetPay = txtNet.Text;
}
private void loadConfig()
{
XmlSerializer ser = new XmlSerializer(typeof(SaveData));
using (FileStream fs = File.OpenRead("config.xml"))
{
sd = (SaveData)ser.Deserialize(fs);
}
}
}
public struct SaveData
{
public bool Married;
public string PayPd;
public string Allow;
public string Gross;
public string Fit;
public string Soc;
public string Med;
public string NetPay;
}
}
You are loading your object by deserializing.
But Where are you assigning the states back to your controls?
look at frmPayroll_Load function.
You are trying to assign the data back to the object again.
You have to assign data back to form controls.
Should be something like this (you may need to apply data conversions if required):
rdoMarr.Checked = sd.Married;
.
.
.
.
txtFit.Text = sd.Fit;
.
.
.
.
This question already has answers here:
How to use the OnPaint event in C#?
(2 answers)
Closed 6 years ago.
EDIT: This is not a duplicate of "How to use the OnPaint event in C#" because I didn't even know it existed. I am asking a question, when I don't know the answer. It's like asking "How do I check oil in my car" when i don't even know what a dip stick is. Please remove the duplicate sign if this edits it in a way that explains why it is not a duplicate.
I am trying to make a random terrain generator. At the moment, I am using Windows Forms Graphics to draw it all. I can't seem to get the engine to work. The following code ran does not display anything on the form, even though it should draw a black rectangle from 0,0 to 50,50. Can anyone solve?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Random_Terrain_Generator
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Engine e = new Engine(100, 100);
e.Color3(Color.Blue);
e.rect(new Rectangle(0,0,50,50));
Graphics fg = this.CreateGraphics();
fg.DrawImage(e.getBuffer(), 1, 100, 100, 100);
}
}
public class Engine
{
Color fillColor;
Bitmap buffer;
Graphics bufferGraphics;
public Engine(int bufferWidth, int bufferHeight)
{
buffer = new Bitmap(bufferWidth, bufferHeight);
bufferGraphics = Graphics.FromImage(buffer);
}
public void fRect(Rectangle r)
{
bufferGraphics.FillRectangle(new SolidBrush(fillColor), r);
}
public void rect(Rectangle r)
{
bufferGraphics.DrawRectangle(new Pen(fillColor), r);
}
public void Color3(Color c)
{
fillColor = c;
}
public Bitmap getBuffer()
{
return buffer;
}
}
}
This worked! Thanks to TyCobb for the quick answer.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Random_Terrain_Generator
{
public partial class Form1 : Form
{
Engine e;
public Form1()
{
InitializeComponent();
e = new Engine(100, 100);
e.Color3(Color.Blue);
e.fRect(new Rectangle(0,0,50,50));
}
protected override void OnPaint(PaintEventArgs pe)
{
// Call the OnPaint method of the base class.
base.OnPaint(pe);
pe.Graphics.DrawImage(e.getBuffer(), 1, 1, 100, 100);
}
}
public class Engine
{
Color fillColor;
Bitmap buffer;
Graphics bufferGraphics;
public Engine(int bufferWidth, int bufferHeight)
{
buffer = new Bitmap(bufferWidth, bufferHeight);
bufferGraphics = Graphics.FromImage(buffer);
}
public void fRect(Rectangle r)
{
bufferGraphics.FillRectangle(new SolidBrush(fillColor), r);
}
public void rect(Rectangle r)
{
bufferGraphics.DrawRectangle(new Pen(fillColor), r);
}
public void Color3(Color c)
{
fillColor = c;
}
public Bitmap getBuffer()
{
return buffer;
}
}
}
I'm overriding the form's OnPaint event, which redraws the rectangle every frame. The issue was that the form was painting over the rectangle.
How to use the OnPaint event in C#?
I am struggeling with getting my SoundEngine class's function PlaySound to be played where I want it.
The function needs to be a global function but I can't make it static as the function refers to an object that the SoundEngine makes.
I'll show the snippets that are important:
In my GameWorld class:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Input;
using System;
using SoundEngineSpace;
public GameWorld(int width, int height, ContentManager Content)
{
screenWidth = width;
screenHeight = height;
random = new Random();
gameState = GameState.Playing;
block = Content.Load<Texture2D>("block");
font = Content.Load<SpriteFont>("SpelFont");
SoundEffect blockFallSE = Content.Load<SoundEffect>("BlockFallSE");
Song BuildingWallsintheCold = Content.Load<Song>("91 Building Walls in the Cold");
soundEngine = new SoundEngine();
soundEngine.addSound(blockFallSE);
soundEngine.addSong(BuildingWallsintheCold);
soundEngine.SetSoundVolume(20);
soundEngine.SetSongVolume(5);
soundEngine.PlaySong(0);
grid = new TetrisGrid(block);
}
In my TetrisGrid class
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using SoundEngineSpace;
...
public void TetNaarBeneden()
{
soundEngine.PlaySound(0);
InPlayGrid.Velocity = new Vector2(0, gridblock.Height);
CheckValidLocation();
}//Moves tet down
And the soundEngine class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
namespace SoundEngineSpace
{
public class SoundEngine
{
private void soundEngine()
{
}
BackgroundSound backgroundSound = new BackgroundSound();
SoundEffects soundEffects = new SoundEffects();
public void addSong(Song s)
{
backgroundSound.AddBackgroundSound(s);
}
public void addSound(SoundEffect s)
{
soundEffects.AddSound(s);
}
public void PlaySong(int s)
{
backgroundSound.PlayBackgroundSound(s);
}
public void PlaySound(int s)
{
soundEffects.PlaySound(s);
}
public void SetSongVolume(int v)
{
backgroundSound.SetBackGroundSoundVolume(v);
}
public void SetSoundVolume(int v)
{
soundEffects.SetSoundEffectVolume(v);
}
}
}
class BackgroundSound
{
public static void backGroundSound()
{
}
private List<Song> BackgroundSoundEffects = new List<Song>();
private bool PlayingBackGroundSound = false;
public void AddBackgroundSound(Song s)//addsongs to the list
{
BackgroundSoundEffects.Add(s);
}
public void PlayBackgroundSound(int s)//plays BackgroundSound based on location in the list
{
MediaPlayer.IsRepeating = true;//If I use this exact soundengine again, move this to it's own function instead!
if (BackgroundSoundEffects.Count() > s)
{
if (PlayingBackGroundSound)
MediaPlayer.Stop();
MediaPlayer.Play(BackgroundSoundEffects.ElementAt(s));
PlayingBackGroundSound = true;
}
else
{
Console.WriteLine("Couldent find the BackgroundSound");
}
}
public void SetBackGroundSoundVolume(int v)
{
MediaPlayer.Volume = (float)v/100;
}
}
class SoundEffects
{
public static void soundeffects()
{
}
private List<SoundEffect> soundEffects = new List<SoundEffect>();
public void AddSound(SoundEffect s)//addsongs to the list
{
soundEffects.Add(s);
}
public void PlaySound(int s)//plays sound based on location in the list
{
if (soundEffects.Count() > s)
{
SoundEffect ToPlaySound = soundEffects.ElementAt(s);
ToPlaySound.Play();
}
else
{
Console.WriteLine("Couldent find the sound");
}
}
public void SetSoundEffectVolume(int v)
{
SoundEffect.MasterVolume = (float)v/100;
}
}
I ended up making the constructor of objects that needed acces to the SounEngine pass the SoundEngine as a paramter.
Then I could create a new soundEngine within the object I needed it to exsist and call: this.soundEngine = soundEngine.
That succesfully copied the soundEngine and let me use it's fucntions.