C# Help drawing letter (Word Game) - c#

I'm creating a word game(lingo).This is how it works: A random word is selected from an array and the user has to guess the correct word. Then the word is printed out.
Red: wrong letter
Yellow: correct letter but wrong position
Green :position and letter correct
Right now the problem is that it only draws a few letter of the guessed word. I also need to change the y value so that everything is not on the same line.
This is what i got so far.
public string CorrectPlace; // Correct letter and postion
public string WrongPlace; // Correct letter but incorrect postion
public string NoneExist; // Wrong
public string inputwordstring; // user input
public string CorrectWord; // Correct word
public char[] CorrectWordchar;
public char[] InputWord;
int x;// position
int y; //position
public int points = 0;
char replacemt = '#';
public string[] Wordarray = null; // Declare string array
public string getRandomWord() // generate random word
{
Random ran = new Random();
return Wordarray[(ran.Next(0, Wordarray.Length - 1))];
}
public void Gamers() //
{
Wordarray = new string[] { "radar", "andar", "axars", "rapar", "raser", "matar", "rikas", "ratas", "bakar", "bruka" }; // Dictionary
CorrectWord = getRandomWord(); // store the random word in a string
}
public void CheckWords(string name, Graphics g)
{
if (CorrectWord == inputwordstring)
{
points += 100;
MessageBox.Show("AMAZING");
}
for (int i = 0; i < CorrectWord.Length; i++)
{
for (int b = 0; b < CorrectWord.Length; b++)
{
CorrectWordchar = CorrectWord.ToCharArray();
InputWord = inputwordstring.ToCharArray();
if (InputWord[i] == CorrectWordchar[b]) // check if the have the same index
{
if (i == b)
{
CorrectPlace = CorrectWord;
SolidBrush s = new SolidBrush(Color.Green);
FontFamily ff = new FontFamily("Arial");
System.Drawing.Font font = new System.Drawing.Font(ff, 20);
g.DrawString(InputWord[i].ToString(), font, s, new PointF(x, y));
x += 20;
// draw out green letters
break;
}
else
{
if (InputWord[b] != CorrectWordchar[b])
{
SolidBrush s = new SolidBrush(Color.DarkOrange);
FontFamily ff = new FontFamily("Arial");
System.Drawing.Font font = new System.Drawing.Font(ff, 20);
g.DrawString(InputWord[b].ToString(), font, s, new PointF(x, y));
x += 20;
// Yellow letters
CorrectWordchar[b] = replacemt; // ersätter rätt bokstav med #
break;
}
else
{
b = 4;
SolidBrush s = new SolidBrush(Color.Red);
FontFamily ff = new FontFamily("Arial");
System.Drawing.Font font = new System.Drawing.Font(ff, 20);
g.DrawString(InputWord[b].ToString(), font, s, new PointF(x, y));
x += 20;
// Red letters
break;
}
}
}
}
}
}
}
}

Some comments and sample code.
You have lots of duplicated code just because you are drawing red and green. This can be summarized.
Most of the classes used in WinForms drawing have to be disposed. Otherwise you run into memory leaks and GDI+ leaks. Brushes, Fonts and others should be disposed.
Use Graphics.MeasureString to get the size of each character. The resulting size can be used to forward in X and Y.
The chars of a string can be accessed by an index directly. You don't need to convert them into char arrays.
void YourDrawMethod(Graphics g)
{
var wrongBrush = new SolidBrush(Color.Red);
var correctBrush = new SolidBrush(Color.Green);
var ff = new FontFamily("Arial");
using(var font = new System.Drawing.Font(ff, 20))
{
int x = 0;
int y = 0;
foreach(car letter in InputWord)
{
SolidBrush brush = InputWord[i] == CorrectWord[b] ? correctBrush : wrongBrush;
g.DrawString(letter.ToString(), font, brush, new PointF(x, y));
Size sizeOfLetter = g.MeasureString(letter.ToString(), font);
x += sizeOfLetter.Width;
}
}
wrongBrush.Dispose();
correctBrush.Dispose();
}

Related

Graphics.DrawString to multiple rectangles

I am trying to write a function that needs to draw a string to an image. The image has anywhere from 1-5 textboxes, which each have a x,y, width, and height. These details are defined in an XML file which I am parsing, so I have access to these for each box.
My question is whether or not I can use the graphics.DrawString (or a similar) method to do this easily. The sample function below will create a rectangle with specified x,y, width, height, and then draw a string within. If the string doesn't fit, it truncates.
public void DrawStringRectangleFormat(Graphics g)
{
// Create string to draw.
String drawString = "Sample Text is too long to fit into this tiny lil rectangle area right here";
// Create font and brush.
Font drawFont = new Font("Arial", 16);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Create rectangle for drawing.
float x = 150.0F;
float y = 150.0F;
float width = 200.0F;
float height = 50.0F;
RectangleF drawRect = new RectangleF(x, y, width, height);
// Set format of string.
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
// Draw string to screen.
g.DrawString(drawString, drawFont, drawBrush, drawRect, drawFormat);
}
What I want instead of this, is rather than truncating, it will stop at the last fitting word, and go to the next rectangle(textbox). This way I can use all the available textboxes.
Is there a method already made to do this? Otherwise I will need to implement my own drawString method.
OK, what you would have to do is loop through each char in the string, and concatenate to a final string..
so basically foreach (char c in mystring)...
then using measurestring, you check to see if the string is over the box length, if it is, start on the next rect...
https://msdn.microsoft.com/en-us/library/6xe5hazb(v=vs.110).aspx
This solution uses the StringFormat's settings to ensure that each call to DrawString only draws the words that fit. Then, Graphics.MeasureCharacterRanges calculates the words that don't fit into the rectangle, and the remaining text overflows into the next layout rectangle.
You might need to customize how the input string is split into words. Right now I'm just splitting it apart at whitespace boundaries.
using System.Text.RegularExpressions;
using System.Drawing;
/// <summary>
/// Draw a string using one or more layout rectangles. Words which don't fit will overflow into the next layout rectangle.
/// </summary>
private static void DrawOverflowString(Graphics graphics, string drawString, RectangleF[] layoutRectangles, StringAlignment alignment)
{
var drawFont = new Font("Arial", 16.0f);
var black = new SolidBrush(Color.Black);
var format = new StringFormat()
{
Alignment = alignment,
Trimming = StringTrimming.Word,
FormatFlags = StringFormatFlags.LineLimit
};
var wordRegex = new Regex("[^\\s]+");
string remainingText = drawString;
foreach (var layoutRect in layoutRectangles)
{
// Draw everything that will fit into this text box
graphics.DrawString(remainingText, drawFont, black, layoutRect, format);
// calculate which words did not fit
var wordMatches = wordRegex.Matches(remainingText);
var ranges = wordMatches.OfType<Match>().Select(x => new CharacterRange(x.Index, x.Length)).ToArray();
format.SetMeasurableCharacterRanges(ranges);
var wordRegions = graphics.MeasureCharacterRanges(remainingText, drawFont, layoutRect, format);
var allfit = true;
for (int i = 0; i < wordRegions.Length; i++)
{
if (wordRegions[i].GetBounds(graphics).Width == 0.0f)
{
allfit = false;
remainingText = remainingText.Substring(wordMatches[i].Index);
break;
}
}
if (allfit)
break;
}
drawFont.Dispose();
black.Dispose();
}
protected override void OnPaint(PaintEventArgs e)
{
// Call the OnPaint method of the base class.
base.OnPaint(e);
List<Rectanglestring> testrecs = new List<Rectanglestring>();
testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 12, 40, 12), whattodraw = "" });
testrecs.Add(new Rectanglestring {targetrect= new Rectangle(0, 25, 35, 12),whattodraw="" });
testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 35, 35, 12), whattodraw = "" });
testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 45, 35, 12), whattodraw = "" });
testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 65, 35, 12), whattodraw = "" });
testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 85, 35, 12), whattodraw = "" });
testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 95, 55, 12), whattodraw = "" });
string mystringtofit = "This is just an example";
foreach (Rectanglestring rect in testrecs)
{
for (int i = 0; i < mystringtofit.Length; i++)
{
if (mystringtofit[i] == ' ' && rect.whattodraw.Length > 0) break;
if (mystringtofit[i] == ' ') continue;
string teststring = rect.whattodraw + mystringtofit[i];
SizeF stringSize = stringSize = e.Graphics.MeasureString(teststring, new Font("Ariel", 12));
if (stringSize.Width >= rect.targetrect.Width) break;
rect.whattodraw += mystringtofit[i];
}
mystringtofit = mystringtofit.Substring(rect.whattodraw.Length);
if (mystringtofit.StartsWith(" "))
{
mystringtofit = mystringtofit.Substring(1);
}
e.Graphics.DrawString(rect.whattodraw, Font, new SolidBrush(ForeColor), rect.targetrect);
}
// Call methods of the System.Drawing.Graphics object.
}
public class Rectanglestring
{
public Rectangle targetrect = new Rectangle();
public string whattodraw = "";
}
This is what I have. It does what I described. Thanks for the answers.
public void DrawStringInTextboxes(string text, Graphics g)
{
String drawString = text;
PrivateFontCollection fontCollection = new PrivateFontCollection();
fontCollection.AddFontFile(System.Web.Hosting.HostingEnvironment.MapPath("~/Content/Fonts/Squidgingtons.ttf"));
var squidingtonsFontFamily = fontCollection.Families[0];
Font squidingtons = new Font(squidingtonsFontFamily, textParameters[0].MaxFontSize);
Font drawFont = new Font("Arial", 60);
SolidBrush drawBrush = new SolidBrush(Color.Black);
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
char[] delimiterChars = { ' ' };
string[] words = drawString.Split(delimiterChars);
string finalString = "";
int textBoxIndex = 0;
foreach (string word in words)
{
//set the dimensions for the first textbox and create a rectangle with those specifications.
float x = textParameters[textBoxIndex].Left;
float y = textParameters[textBoxIndex].Top;
float width = textParameters[textBoxIndex].Width;
float height = textParameters[textBoxIndex].Height;
RectangleF Rect = new RectangleF(x, y, width, height);
//if the current finalString + the next word fits in the current box, add the word to finalString.
if (g.MeasureString(finalString + word + " ", squidingtons).Width < textParameters[textBoxIndex].Width)
{
finalString = finalString + " " + word;
//if this is the last word, print the finalString and we are done.
if (word == words[words.Length - 1])
{
g.DrawString(finalString, squidingtons, drawBrush, Rect, drawFormat);
break;
}
}
//the current finalString + next word did not fit in the box. Draw what we have to the first box.
else {
g.DrawString(finalString, squidingtons, drawBrush, Rect, drawFormat);
//Hold onto the word that didnt fit. It will be the first word of the next box.
finalString = word;
if (textBoxIndex +1 >= textParameters.Length)
{
//if we are out of textboxes, we are done.
break;
}
else
{
//move on to the next textbox. The loop begins again with new specifications set for the textbox.
textBoxIndex++;
}
}
}
squidingtons.Dispose();
drawBrush.Dispose();
drawFont.Dispose();
}

Colliding picture boxes, random location not working

I've been trying to make a array of images that are created randomly in a space, the thing is when they overlap, they are not changing they're location.
int number;
PictureBox[] X = new PictureBox[100];
public Form1()
{
InitializeComponent();
}
private void addX(int number)
{
Random randomNumber = new Random(DateTime.Now.Millisecond);
int x = randomNumber.Next(0, reprezentare.Height - 40);
int y = randomNumber.Next(0, reprezentare.Width - 40);
X[number] = new PictureBox();
X[number].Height = 41;
X[number].Width = 41;
X[number].SizeMode = PictureBoxSizeMode.Zoom;
X[number].Image = Properties.Resources.X;
if(number >= 1)
{
while (pictureBox1.Bounds.IntersectsWith(X[number - 1].Bounds)) x = randomNumber.Next(0, reprezentare.Height - 40);
while (pictureBox1.Bounds.IntersectsWith(X[number - 1].Bounds)) y = randomNumber.Next(0, reprezentare.Width - 40);
}
X[number].Location = new Point(x, y);
reprezentare.Controls.Add(X[number]);
number++;
richTextBox1.Text += x + " : " + y;
richTextBox1.Text += Environment.NewLine;
}
private void button1_Click(object sender, EventArgs e)
{
addX(number);
}
Does anyone know how to fix this?
Couple of issues. First, you have a variable number and a parameter number. Not good:
int number;
private void addX(int number)
Just change it to:
private void addX()
Secondly, you are only comparing against PictureBox1, so all of the PictureBoxes you are adding aren't checking the other PictureBoxes, so you can try something like this:
bool ok = false;
while (!ok) {
ok = true;
int x = randomNumber.Next(0, reprezentare.Width - 40);
int y = randomNumber.Next(0, reprezentare.Height - 40);
for (int i = 0; i < number; ++i) {
if (X[i].Bounds.IntersectsWith(new Rectangle(x, y, 41, 41))) {
ok = false;
break;
}
}
if (ok) {
X[number].Location = new Point(x, y);
}
}
reprezentare.Controls.Add(X[number]);
number++;
You would have to add a check to see if any space is still available or not to avoid the loop going to infinity.

Why am i getting a System.NullReferenceException error in my code? [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 7 years ago.
I'm currently working on an exercise for my c# class. I am having some trouble with one particular part and would really appreciate some help.
I am working on an exercise in which we are given an incomplete project file.
The project has multiple classes, this class is for controlling squares that are placed on the board.
namespace HareAndTortoise {
public partial class SquareControl : PictureBox {
public const int SQUARE_SIZE = 100;
private Square square; // A reference to the corresponding square object
private BindingList<Player> players; // References the players in the overall game.
private bool[] containsPlayers = new bool[6];//HareAndTortoiseGame.MAX_PLAYERS];
public bool[] ContainsPlayers {
get {
return containsPlayers;
}
set {
containsPlayers = value;
}
}
// Font and brush for displaying text inside the square.
private Font textFont = new Font("Microsoft Sans Serif", 8);
private Brush textBrush = Brushes.White;
public SquareControl(Square square, BindingList<Player> players) {
this.square = square;
this.players = players;
// Set GUI properties of the whole square.
Size = new Size(SQUARE_SIZE, SQUARE_SIZE);
Margin = new Padding(0); // No spacing around the cell. (Default is 3 pixels.)
Dock = DockStyle.Fill;
BorderStyle = BorderStyle.FixedSingle;
BackColor = Color.CornflowerBlue;
SetImageWhenNeeded();
}
private void SetImageWhenNeeded()
{
if (square is Square.Win_Square)
{
LoadImageFromFile("Win.png");
textBrush = Brushes.Black;
}
else if (square is Square.Lose_Square)
{
LoadImageFromFile("Lose.png");
textBrush = Brushes.Red;
}
else if (square is Square.Chance_Square)
{
LoadImageFromFile("monster-green.png");
}
else if (square.Name == "Finish")
{
LoadImageFromFile("checkered-flag.png");
}
else
{
// No image needed.
}
}
private void LoadImageFromFile(string fileName) {
Image image = Image.FromFile(#"Images\" + fileName);
Image = image;
SizeMode = PictureBoxSizeMode.StretchImage; // Zoom is also ok.
}
protected override void OnPaint(PaintEventArgs e) {
// Due to a limitation in WinForms, don't use base.OnPaint(e) here.
if (Image != null)
e.Graphics.DrawImage(Image, e.ClipRectangle);
string name = square.Name;
// Create rectangle for drawing.
float textWidth = textFont.Size * name.Length;
float textHeight = textFont.Height;
float textX = e.ClipRectangle.Right - textWidth;
float textY = e.ClipRectangle.Bottom - textHeight;
RectangleF drawRect = new RectangleF(textX, textY, textWidth, textHeight);
// When debugging this method, show the drawing-rectangle on the screen.
//Pen blackPen = new Pen(Color.Black);
//e.Graphics.DrawRectangle(blackPen, textX, textY, textWidth, textHeight);
// Set format of string.
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Far; // Right-aligned.
// Draw string to screen.
e.Graphics.DrawString(name, textFont, textBrush, drawRect, drawFormat);
// Draw player tokens (when any players are on this square).
const int PLAYER_TOKENS_PER_ROW = 3;
const int PLAYER_TOKEN_SIZE = 30; // pixels.
const int PLAYER_TOKEN_SPACING = (SQUARE_SIZE - (PLAYER_TOKEN_SIZE * PLAYER_TOKENS_PER_ROW)) / (PLAYER_TOKENS_PER_ROW - 1);
for (int i = 0; i < containsPlayers.Length; i++) {
if (containsPlayers[i]) {
int xPosition = i % PLAYER_TOKENS_PER_ROW;
int yPosition = i / PLAYER_TOKENS_PER_ROW;
int xPixels = xPosition * (PLAYER_TOKEN_SIZE + PLAYER_TOKEN_SPACING);
int yPixels = yPosition * (PLAYER_TOKEN_SIZE + PLAYER_TOKEN_SPACING);
Brush playerTokenColour = players[i].PlayerTokenColour;
e.Graphics.FillEllipse(playerTokenColour, xPixels, yPixels, PLAYER_TOKEN_SIZE, PLAYER_TOKEN_SIZE);
}
}//endfor
}
}
}
The program trips up at:
else if (square.Name == "Finish")
{
LoadImageFromFile("checkered-flag.png");
}
I know it is because of square.name but from going through the code, I cant see why square.Name is not recognizable.
Square is passed from another class using this method
private void SetUpGuiGameBoard()
{
for (int i = 0; i <= 55; i++)
{
Square q = Board.GetGameBoardSquare(i);
SquareControl sq = new SquareControl(q, null);
int coloumn;
int row;
if (i == 0)
{
BackColor = Color.BurlyWood;
}
if (i == 55)
{
BackColor = Color.BurlyWood;
}
MapSquareNumToTablePanel(i, out coloumn, out row);
tableLayoutPanel1.Controls.Add(sq, coloumn, row);
}
and Squares are created in this class
private static Square[] gameBoard = new Square[56];
static public void SetUpBoard()
{
for (int i = 1; i == 55; i++)
{
gameBoard[i] = new Square("Ordinary Square", i);
}
gameBoard[0] = new Square("Start", 0);
gameBoard[4] = new Square.Lose_Square("Lose Square", 4);
gameBoard[5] = new Square.Chance_Square("Chance Square", 5);
gameBoard[9] = new Square.Win_Square("Win Square", 9);
gameBoard[11] = new Square.Chance_Square("Chance Square", 11);
gameBoard[14] = new Square.Lose_Square("Lose Square", 14);
gameBoard[17] = new Square.Chance_Square("Chance Square", 17);
gameBoard[19] = new Square.Win_Square("Win Square", 19);
gameBoard[24] = new Square.Lose_Square("Lose Square", 24);
gameBoard[29] = new Square.Win_Square("Win Square", 29);
gameBoard[34] = new Square.Lose_Square("Lose Square", 34);
gameBoard[35] = new Square.Chance_Square("Chance Square", 35);
gameBoard[39] = new Square.Win_Square("Win Square", 39);
gameBoard[44] = new Square.Lose_Square("Lose Square", 44);
gameBoard[47] = new Square.Chance_Square("Chance Square", 47);
gameBoard[49] = new Square.Win_Square("Win Square", 49);
gameBoard[53] = new Square.Chance_Square("Chance Square", 53);
gameBoard[55] = new Square("Finish", 56);
}
public static Square GetGameBoardSquare(int n)
{
return gameBoard[n];
}
public static Square StartSquare()
{
return gameBoard[0];
}
public static Square NextSquare(int n)
{
return gameBoard[(n+1)];
}
}
The answer already provided is the best way for prevention of any null reference exception. For more clarification I can suggest you to check the call stack at the point the debugger reaches the SquareControl constructor. At this point you should check why the Square object being passed in is a 'NULL'. That will lead you to the root cause of the problem. Hope this helps.

Drawing a Chart for non-numerical variables in C# [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have a issue with chart drawing in my Windows form application project.
I want to draw a chart like this:
http://upload7.ir/imgs/2014-12/84930837744513480976.jpg
the words in axis X and axis Y is related and that letters in colorful rectangles are each relation's value. I want a chart exactly like this.
anyone can help me to draw something like that?
I''l be thankful.
Regards.
Here is a class AreaChart which draws a chart like the one you showed.
Here is what it looks like, with the built-in values:
Add the class to your project and compile. It'll show up in the toolbox and you can place it on your form. (Do a Backup of your project before you do that!)
You can set many values in the designer or in code, including the dimensions, BackColor, Font, and Labels.. Set the Colors and Texts in code using the methods provided!
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
class AreaChart : Panel
{
[Category("Appearance"), Description("Padding around the ChartArea")]
public Padding ChartPadding { get; set; }
[Category("Appearance"),
Description("Axixs Origin offsets from Bottom Left of the Chart")]
public Point AxisOriginOffset { get; set; }
[Category("Appearance"),Description("Number of Rows")]
public int RowNum { get; set; }
[Category("Appearance"), Description("Number of Columns")]
public int ColNum { get; set; }
[Category("Appearance"), Description("Labeltexts for Y-Axis")]
public string[] labelsY { get; set; }
[Category("Appearance"), Description("Labeltexts for X-Axis")]
public string[] labelsX { get; set; }
Color[][] colors { get; set; }
string[][] texts { get; set; }
Rectangle chartArea = Rectangle.Empty;
Point axisOrigin = Point.Empty
public void Init()
{
chartArea = new Rectangle(ChartPadding.Left, ChartPadding.Top,
this.Width - ChartPadding.Left - ChartPadding.Right,
this.Height - ChartPadding.Top - ChartPadding.Bottom);
axisOrigin = new Point(AxisOriginOffset.X, this.Height - AxisOriginOffset.Y);
colors = new Color[RowNum][];
for (int r = 0; r < RowNum; r++) colors[r] = new Color[ColNum];
texts = new string[RowNum][];
for (int r = 0; r < RowNum; r++) texts[r] = new string[ColNum];
labelsX = new string[ColNum]; //*
labelsY = new string[RowNum]; //*
}
public AreaChart()
{
ChartPadding = new Padding(80, 40, 40, 40);
AxisOriginOffset = new Point(60, 20);
RowNum = 3;
ColNum = 2;
BackColor = Color.AntiqueWhite;
Init();
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.DesignMode) Init(); // make the designer show the current
if (this.DesignMode) InitDemo(); // ...sizes, colors and texts
int x = chartArea.X;
int y = chartArea.Y;
SizeF tSize = e.Graphics.MeasureString("XX", this.Font, 9999);
int th = (int)tSize.Height / 2;
int tw = (int)tSize.Width / 2;
int h = chartArea.Height / RowNum;
int w = chartArea.Width / ColNum; e.Graphics.Clear(BackColor);
e.Graphics.DrawLine(Pens.Black, axisOrigin,
new Point(axisOrigin.X, chartArea.Top));
e.Graphics.DrawLine(Pens.Black, axisOrigin,
new Point( chartArea.Right, axisOrigin.Y));
for (int r = 0; r < RowNum; r++)
for (int c = 0; c < ColNum; c++)
{
e.Graphics.FillRectangle(new SolidBrush(colors[r][c]),
x + c * w, y + r * h, w, h);
e.Graphics.DrawRectangle(Pens.Black, x + c * w, y + r * h, w, h);
e.Graphics.DrawString(texts[r][c], this.Font, Brushes.Black,
x + c * w + w / 2 - tw, y + r * h + h / 2 - th);
}
for (int r = 0; r < RowNum; r++)
e.Graphics.DrawString(labelsY[r], this.Font, Brushes.Black,
AxisOriginOffset.X - tw * 2, y + r * h + h / 2 - th); //*
for (int c = 0; c < ColNum; c++)
e.Graphics.DrawString(labelsX[c], this.Font, Brushes.Black,
x + c * w + w / 2 - tw, axisOrigin.Y ); //*
base.OnPaint(e);;
}
public void setColor (int row, int col, Color color)
{
try
{
colors[row][col] = color;
} catch { throw new Exception("setColor : array index out of bounds!"); }
}
public void setText(int row, int col, string text)
{
try
{
texts[row][col] = text;
} catch { throw new Exception("setText: array index out of bounds!"); }
}
public void setLabelX(int col, string text) //*
{
try
{
labelsX[col] = text;
} catch { throw new Exception("array index out of bounds!"); }
}
public void setLabelY(int row, string text) //*
{
try
{
labelsY[row] = text;
} catch { throw new Exception("array index out of bounds!"); }
}
public void InitDemo()
{
setColor(0, 0, Color.Plum);
setColor(1, 0, Color.GreenYellow);
setColor(2, 0, Color.Gold);
setColor(0, 1, Color.LightSkyBlue);
setColor(1, 1, Color.NavajoWhite);
setColor(2, 1, Color.Pink);
setText(0, 0, "AA");
setText(1, 0, "BA");
setText(2, 0, "CA");
setText(0, 1, "AB");
setText(1, 1, "BB");
setText(2, 1, "BC");
setLabelY(0, "A"); //*
setLabelY(1, "B"); //*
setLabelY(2, "C"); //*
setLabelX(0, "A"); //*
setLabelX(1, "B"); //*
}
}
After your Form's Initializecomponent() you should call
areaChart1.Init();
areaChart1.InitDemo();
to display the demo chart I show. To change it use calls like these:
areaChart1.ColNum = 3;
areaChart1.Init();
areaChart1.InitDemo();
areaChart1.setColor(0, 2, Color.Yellow);
areaChart1.setLabelY(2, "ZZ");
//..
Your question is pretty vague, but if you want EXACTLY that chart, does that mean you don't need the values to be dynamic? If so, wouldn't the simplest solution be to save it as an image and display the image in your application where you need it. Obviously this won't work if you need the values or colors to be dynamic.

DrawImage fails to position sliced images correctly

For a couple of days now I've tried to figure out why my nine-slice code does not work as expected. As far as I can see, there seems to be an issue with the Graphics.DrawImage method which handles my nine slice images incorrectly. So my problem is how to compensate for the incorrect scaling that is performed when running my code on the compact framework. I might add that this code of course works perfectly when running in the full framework environment. The problem only occurs when scaling the image to a larger image not the other way around. Here is the snippet:
public class NineSliceBitmapSnippet
{
private Bitmap m_OriginalBitmap;
public int CornerLength { get; set; }
/// <summary>
/// Initializes a new instance of the NineSliceBitmapSnippet class.
/// </summary>
public NineSliceBitmapSnippet(Bitmap bitmap)
{
CornerLength = 5;
m_OriginalBitmap = bitmap;
}
public Bitmap ScaleSingleBitmap(Size size)
{
Bitmap scaledBitmap = new Bitmap(size.Width, size.Height);
int[] horizontalTargetSlices = Slice(size.Width);
int[] verticalTargetSlices = Slice(size.Height);
int[] horizontalSourceSlices = Slice(m_OriginalBitmap.Width);
int[] verticalSourceSlices = Slice(m_OriginalBitmap.Height);
using (Graphics graphics = Graphics.FromImage(scaledBitmap))
{
using (Brush brush = new SolidBrush(Color.Fuchsia))
{
graphics.FillRectangle(brush, new Rectangle(0, 0, size.Width, size.Height));
}
int horizontalTargetOffset = 0;
int verticalTargetOffset = 0;
int horizontalSourceOffset = 0;
int verticalSourceOffset = 0;
for (int x = 0; x < horizontalTargetSlices.Length; x++)
{
verticalTargetOffset = 0;
verticalSourceOffset = 0;
for (int y = 0; y < verticalTargetSlices.Length; y++)
{
Rectangle destination = new Rectangle(horizontalTargetOffset, verticalTargetOffset, horizontalTargetSlices[x], verticalTargetSlices[y]);
Rectangle source = new Rectangle(horizontalSourceOffset, verticalSourceOffset, horizontalSourceSlices[x], verticalSourceSlices[y]);
graphics.DrawImage(m_OriginalBitmap, destination, source, GraphicsUnit.Pixel);
verticalTargetOffset += verticalTargetSlices[y];
verticalSourceOffset += verticalSourceSlices[y];
}
horizontalTargetOffset += horizontalTargetSlices[x];
horizontalSourceOffset += horizontalSourceSlices[x];
}
}
return scaledBitmap;
}
public int[] Slice(int length)
{
int cornerLength = CornerLength;
if (length <= (cornerLength * 2))
throw new Exception("Image to small for sliceing up");
int[] slices = new int[3];
slices[0] = cornerLength;
slices[1] = length - (2 * cornerLength);
slices[2] = cornerLength;
return slices;
}
}
So, my question is, does anybody now how I could compensate the incorrect scaling?
/Dan
After some more trial and error I've finally found a solution to my problem. The scaling problems has always been to the top-center, right-center, bottom-center and left-center slices since they're always stretched in only one direction according to the logic of nine slice scaling. If I apply a temporarely square stretch to those slices before applying the correct stretch the final bitmap will be correct. Once again the problem is only visible in the .Net Compact Framework of a Windows CE device (Smart Device). Here's a snippet with code adjusting for the bug in CF. My only concern now is that the slices that get square stretched will take much more memory due to the correction code. On the other hand this step is only a short period of time so I might get away with it. ;)
public class NineSliceBitmapSnippet
{
private Bitmap m_OriginalBitmap;
public int CornerLength { get; set; }
public NineSliceBitmapSnippet(Bitmap bitmap)
{
CornerLength = 5;
m_OriginalBitmap = bitmap;
}
public Bitmap Scale(Size size)
{
if (m_OriginalBitmap != null)
{
return ScaleSingleBitmap(size);
}
return null;
}
public Bitmap ScaleSingleBitmap(Size size)
{
Bitmap scaledBitmap = new Bitmap(size.Width, size.Height);
int[] horizontalTargetSlices = Slice(size.Width);
int[] verticalTargetSlices = Slice(size.Height);
int[] horizontalSourceSlices = Slice(m_OriginalBitmap.Width);
int[] verticalSourceSlices = Slice(m_OriginalBitmap.Height);
using (Graphics graphics = Graphics.FromImage(scaledBitmap))
{
using (Brush brush = new SolidBrush(Color.Fuchsia))
{
graphics.FillRectangle(brush, new Rectangle(0, 0, size.Width, size.Height));
}
int horizontalTargetOffset = 0;
int verticalTargetOffset = 0;
int horizontalSourceOffset = 0;
int verticalSourceOffset = 0;
for (int x = 0; x < horizontalTargetSlices.Length; x++)
{
verticalTargetOffset = 0;
verticalSourceOffset = 0;
for (int y = 0; y < verticalTargetSlices.Length; y++)
{
Rectangle destination = new Rectangle(horizontalTargetOffset, verticalTargetOffset, horizontalTargetSlices[x], verticalTargetSlices[y]);
Rectangle source = new Rectangle(horizontalSourceOffset, verticalSourceOffset, horizontalSourceSlices[x], verticalSourceSlices[y]);
bool isWidthAffectedByVerticalStretch = (y == 1 && (x == 0 || x == 2) && destination.Height > source.Height);
bool isHeightAffectedByHorizontalStretch = (x == 1 && (y == 0 || y == 2) && destination.Width > source.Width);
if (isHeightAffectedByHorizontalStretch)
{
BypassDrawImageError(graphics, destination, source, Orientation.Horizontal);
}
else if (isWidthAffectedByVerticalStretch)
{
BypassDrawImageError(graphics, destination, source, Orientation.Vertical);
}
else
{
graphics.DrawImage(m_OriginalBitmap, destination, source, GraphicsUnit.Pixel);
}
verticalTargetOffset += verticalTargetSlices[y];
verticalSourceOffset += verticalSourceSlices[y];
}
horizontalTargetOffset += horizontalTargetSlices[x];
horizontalSourceOffset += horizontalSourceSlices[x];
}
}
return scaledBitmap;
}
private void BypassDrawImageError(Graphics graphics, Rectangle destination, Rectangle source, Orientation orientationAdjustment)
{
Size adjustedSize = Size.Empty;
switch (orientationAdjustment)
{
case Orientation.Horizontal:
adjustedSize = new Size(destination.Width, destination.Width);
break;
case Orientation.Vertical:
adjustedSize = new Size(destination.Height, destination.Height);
break;
default:
break;
}
using (Bitmap quadScaledBitmap = new Bitmap(adjustedSize.Width, adjustedSize.Height))
{
using (Graphics tempGraphics = Graphics.FromImage(quadScaledBitmap))
{
tempGraphics.Clear(Color.Fuchsia);
tempGraphics.DrawImage(m_OriginalBitmap, new Rectangle(0, 0, adjustedSize.Width, adjustedSize.Height), source, GraphicsUnit.Pixel);
}
graphics.DrawImage(quadScaledBitmap, destination, new Rectangle(0, 0, quadScaledBitmap.Width, quadScaledBitmap.Height), GraphicsUnit.Pixel);
}
}
public int[] Slice(int length)
{
int cornerLength = CornerLength;
if (length <= (cornerLength * 2))
throw new Exception("Image to small for sliceing up");
int[] slices = new int[3];
slices[0] = cornerLength;
slices[1] = length - (2 * cornerLength);
slices[2] = cornerLength;
return slices;
}
}

Categories

Resources