I'm trying to move my project from WinForms to WPF, but i couldn't succeed in this. I had a picture box, with a full of lines one after another and i could just use DrawLine(pen,point,point) in a loop. But when i tried to do this by using WPF, i couldn't menage.
This is the code snippet that i've used in WinForms :
for (x = 0; x < X_COORD_END - X_COORD_START; x += 1)
{
System.Drawing.Point point1 = new System.Drawing.Point(x, 30);
System.Drawing.Point point2 = new System.Drawing.Point(x, 60);
e.Graphics.DrawLine(myPen, point1, point2);
}
And the result was :
I've tried to use Line array on WPF, but i gave an error on canvas.Children.Add line. Now i'm trying to use collection of points, but i can't arrange points as i want to. Here is what i've tried :
private void DrawLine(Point[] points)
{
Polyline line = new Polyline();
PointCollection collection = new PointCollection();
foreach (Point p in points)
{
collection.Add(p);
}
line.Points = collection;
line.Stroke = new SolidColorBrush(Colors.Black);
line.StrokeThickness = 20;
scanCanvas.Children.Add(line);
}
for (int counter = 0; counter < 1000; counter++ )
{
points[counter] = new Point(counter, 30);
}
DrawLine(points);
Use Stackpanel for your scenario. I have used a line array, which is similar to the thing which you have done in winforms.
MainWindow.xaml
<Window x:Class="SO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="stackGraphicsArea" Orientation="Horizontal"/>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Line[] lines = new Line[1000];
for(int i=0; i<1000;i++)
{
lines[i] = new Line();
lines[i].Stroke = new SolidColorBrush(Colors.Red);
lines[i].StrokeThickness = 5;
lines[i].X1 = 0;
lines[i].X2 = 0;
lines[i].Y1 = 30;
lines[i].Y2 = 20;
}
DrawLines(lines);
}
private void DrawLines(Line[] _lines)
{
foreach (Line _line in _lines)
{
stackGraphicsArea.Children.Add(_line);
}
}
}
Sriman Reddy has answered my question already but i think i should post my own solution too :
private void DrawLine(int x_coord_start, int y_coord_start, int x_coord_end, int thickness, Color color)
{
Random rand = new Random();
for (int x = 0; x < x_coord_end - x_coord_start; x++)
{
double newTop = rand.Next();
Line top = new Line();
top.Stroke = new SolidColorBrush(color);
top.StrokeThickness = x + 1;
top.X1 = x_coord_start + x;
top.Y1 = y_coord_start;
top.X2 = x_coord_start + x;
top.Y2 = y_coord_start + thickness;
Canvas.SetTop(top, 0);
Canvas.SetLeft(top, 0 /*x * top.Width*/);
scanCanvas.Children.Add(top);
}
}
Related
I have a Grid with a lot of buttons inside. These buttons are supposed to be seamlessly connecting. In most of the cases, this is actually working, but sometimes there's a white stripe between the columns / rows of the grid:
I'm adding the buttons to the grid via the code behind like this (I'm sorry for non-minimal code, but I really don't know what might be relevant here):
public partial class MainWindow
{
private int _xCount;
private int _yCount;
public MainWindow ()
{
InitializeComponent ();
SetSize (40, 40);
}
public void SetSize (int x, int y)
{
_xCount = x;
_yCount = y;
Grid.ColumnDefinitions.Clear ();
Grid.RowDefinitions.Clear ();
for (var i = 0; i < x; i++)
Grid.ColumnDefinitions.Add (new ColumnDefinition {Width = new GridLength (100, GridUnitType.Star)});
for (var i = 0; i < y; i++)
Grid.RowDefinitions.Add (new RowDefinition {Height = new GridLength (100, GridUnitType.Star)});
for (var xI = 0; xI < x; xI++)
for (var yI = 0; yI < y; yI++)
{
var button = new Button
{
BorderThickness = new Thickness (1),
BorderBrush = Brushes.Gray,
Foreground = Brushes.DarkGray,
Content = "",
Background = Brushes.DarkGray
};
Grid.Children.Add (button);
Grid.SetColumn (button, xI);
Grid.SetRow (button, yI);
}
SetButtonSizes ();
}
private void SetButtonSizes ()
{
var gridWidth = Grid.Width;
var gridHeight = Grid.Height;
var buttonWidth = gridWidth / _xCount;
var buttonHeight = gridHeight / _yCount;
foreach (var button in Grid.Children)
{
((Button) button).Width = buttonWidth;
((Button) button).Height = buttonHeight;
}
}
protected override void OnRenderSizeChanged (SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged (sizeInfo);
SetButtonSizes ();
}
}
The WPF is pretty trivial and looks like that:
<Window x:Class="Minesweeper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow"
Height="1000"
Width="1000">
<Grid Name="Grid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Window>
I already tried Pixel Snapping, which didn't make any difference.
You should set UseLayoutRounding to true on the Grid and not programmatically resize the Buttons.
However, you could greatly simplify your code by using a UniformGrid
<Window ...>
<UniformGrid x:Name="grid"/>
</Window>
and add Buttons like this:
public MainWindow()
{
InitializeComponent();
SetSize(40, 40);
}
private void SetSize(int x, int y)
{
grid.Children.Clear();
grid.Columns = x;
for (int i = 0; i < x * y; i++)
{
grid.Children.Add(new Button
{
BorderThickness = new Thickness(1),
BorderBrush = Brushes.Gray,
Background = Brushes.DarkGray,
Foreground = Brushes.DarkGray,
Content = ""
});
}
}
I have FlowLayoutPanel with
AutoScroll = True
FlowDirection = LeftToRight
WrapContents = True
Added controls dynamically have same Width but AutoSize in Height. So the panel will be like this, which has vertical spaces between items. As the height of row managed by the greatest height of controls. So I want to remove these unneeded spaces, and the final result will be like this.
If there's no way to do it with FlowLayoutPanel, What's the proper idea to done it perfectly ?
Its a matrix and should be treated like a matrix.
my opinion is that Panel is more appropriate than a FlowLayoutpanel here.
please see my suggestion and output to achieve such a behavior.
clarification: this code needs improvements to be adapted to all possible cases but you can learn from it the basic idea how to deal with such problem.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Example();
}
// space beetween controls (top and right)
public int MarginSpace = 8;
// first element location
public Point StartPoint = new Point(10, 10);
private void Example()
{
var fixesWidth = 70;
List<Label> randomLables = new List<Label>();
Random rand = new Random();
// generate lables with random heights
for (int i = 1; i < 10; i++)
{
Label lr = new Label();
var randheight = rand.Next(60, 120);
lr.Size = new Size(fixesWidth, randheight);
lr.Text = i.ToString();
lr.BackColor = Color.Black;
lr.ForeColor = Color.White;
randomLables.Add(lr);
}
// check how many elements in one "column" (possible also to add right+left margin)
var cols = panel1.Width / fixesWidth;
// create matrix object to get locations of each label
MyMatrix m = new MyMatrix(cols, randomLables.Count, 15, 70, StartPoint);
m.SetMatrix(randomLables);
int counter = 0;
// pupulate all lables with the points from MyMatrix object
foreach (Point p in m.pointsMatrix)
{
randomLables[counter].Location = p;
panel1.Controls.Add(randomLables[counter]);
counter++;
}
}
}
class MyMatrix
{
private int Rows;
private int TotalElements;
private int Cols;
private int Margin;
private int ElementWidth;
private Point StartPoint;
public MyMatrix(int cols, int totalelements, int margin, int elementwidth, Point startingpoint)
{
this.Cols = cols;
this.TotalElements = totalelements;
this.Margin = margin;
this.ElementWidth = elementwidth;
this.StartPoint = startingpoint;
// calculate number of rows
Rows = totalelements / cols;
}
public List<Point> pointsMatrix = new List<Point>();
int cellCounter = 0;
public void SetMatrix(List<Label> Labels)
{
for (int i = 0; i < Rows; i++)
{
for (int j = 0; j < Cols; j++)
{
var x = StartPoint.X + j * (Margin + ElementWidth);
var y = StartPoint.Y;
if (cellCounter >= Cols)
{
// find the parallel cell in the row above
y = pointsMatrix[cellCounter - Cols].Y + Labels[cellCounter - Cols].Height + Margin;
}
else
{
// do nothing it is first row
}
Point p = new Point(x, y);
pointsMatrix.Add(p);
cellCounter += 1;
}
}
}
}
Output:
I am trying to make a simple five in a row (gomoku) game for two players using windows forms and c#. I put a picturebox with a picture and stretched it out on the form. Now I want to put labels at all the intersections on the picture board so a user can click them and change their background color to black or white.
How can I make the labels created clickable on the form?
public partial class Form1 : Form
{
int labelCount = 0;
int iteration = 0;
public Form1()
{
InitializeComponent();
Label[] board = new Label[361];
for (int i = 0; i < 361; i++)
{
board[i] = new Label
{
Name = "label" + i,
Height = 55,
Width = 55,
MinimumSize = new Size(55, 55),
Text = "label " + i
};
}
int x = 0;
int y = 0;
foreach (var Label in board)
{
if (x >= 580)
{
x = 0;
y = y + Label.Height + 55;
}
Label.Location = new Point(x, y);
this.Controls.Add(Label);
x += Label.Width;
}
}
}
Should I make a one-dimensional [361] or two-dimensional array[{A,1}, {A,2}....{D,1}] to easily check for a winner? How can I connect it to the created labels so the array data corresponds to the objects on the board?
Well Sorry If don`t understand your question. For the Q.1 to add 361 labels you can try the code below. I hope it will help you.
public int x = 0;
public int y = 0;
private Label[] moku = new Label[361];
private void Form1_Load(object sender, EventArgs e)
{
try
{
for (int i = 0; i < 361; i++)
{
moku[i] = new Label();
moku[i].Parent = pictureBox1;//make the picturebox parent
moku[i].Location = new Point(x, y);
moku[i].Text = "O";
moku[i].Name = "moku" + i;
moku[i].BackColor = Color.Transparent;
pictureBox1.Controls.Add(moku[i]);
y += 55;
if (y >= 361) { x += 55; y = 0; x+=55; }
}
}catch(Exception er)
{
MessageBox.Show(er.ToString());
}
}
I prefer using a 2D array because it's easier if you want to check the surrounding boxes.
Form design:
Full source:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public enum Player
{
Empty = 0,
White,
Black
}
public partial class Form1 : Form
{
// initialize board of 5x5
private Player[,] board = new Player[5, 5];
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
DrawBoard();
}
private void DrawBoard()
{
for (var i = 0; i <= board.GetUpperBound(0); i++)
{
for (var j = 0; j <= board.GetUpperBound(1); j++)
{
// for name and text
var name = string.Format("{0}, {1}", i, j);
var label = new Label()
{
Name = name, // name of label
Size = new Size(55, 55),
BorderStyle = BorderStyle.FixedSingle,
Location = new Point(i * 55, j * 55), // location depends on iteration
Text = name
};
label.Click += ClickLabel; // subscribe the Click event handler
pictureBox1.Controls.Add(label); // add label to a container
}
}
}
// this event handler will handle all the labels click event
private void ClickLabel(object sender, EventArgs e)
{
var label = (Label)sender; // this is the label that you click
var x = Convert.ToInt32(label.Name.Split(',')[0]);
var y = Convert.ToInt32(label.Name.Split(',')[1]);
// change the color
if (radPlayerBlack.Checked)
{
// Player Black
label.ForeColor = Color.White;
label.BackColor = Color.Black;
board[x, y] = Player.Black;
}
else
{
// Player White
label.ForeColor = Color.Black;
label.BackColor = Color.White;
board[x, y] = Player.White;
}
}
}
}
You can check the value of the 2D array for black or white. Here's the value when I QuickWatch it in Visual Studio.
I want that all my actions for drawing graph were in my control, inherited from Canvas. But window don't display MyCanvas. I don't know why.
class MyCanvas : Canvas
{
private Double XTimeScale;
private Double YAmpSacle;
private Double YTopLimit;
private Double YBotLimit;
private List<Point> DotsGraph;
public MyCanvas(Double XTimeScale,
Double YAmpSacle,
Double YTopLimit,
Double YBotLimit)
{
this.XTimeScale = XTimeScale;
this.YAmpSacle = YAmpSacle;
this.YTopLimit = YTopLimit;
this.YBotLimit = YBotLimit;
this.Height = (YTopLimit + YBotLimit);
}
public List<Line> DrawNet(Double Width, Double Height)
{
List<Line> temp = new List<Line>();
SolidColorBrush brush = new SolidColorBrush();
brush.Color = Colors.Gray;
for (int i = 0; i < Width; i+= 10)
{
Line Y = new Line();
Y.Stroke = brush;
Y.StrokeThickness = 1;
Y.Y1 = 0;
Y.Y2 = Height;
Y.X1 = i;
Y.X2 = i;
temp.Add(Y);
}
for (int j = 0; j < Height; j += 10)
{
Line X = new Line();
X.Stroke = brush;
X.StrokeThickness = 1;
X.X1 = 0;
X.X2 = Width;
X.Y1 = j;
X.Y2 = j;
temp.Add(X);
}
foreach (var t in temp)
this.Children.Add(t);
return temp;
}
}
And the class of windows where MyCanvas should be displayed. I added it to Grid:
public partial class ShowCanvas : Window
{
public ShowCanvas()
{
InitializeComponent();
MyCanvas EAP = new MyCanvas(300, 300, 300, 300);
Base.Children.Add(EAP); // Base is Grid on window ShowCanvas
}
}
Your window did display your canvas, but your canvas didn't have any children. Call DrawNet method.
You can use an open-source program called "Snoop" to navigate through the visual tree and see properties of visuals
I'm trying to add some ellipces with random positions into my canvas, but i can see them on my canvas. Progmab is compilling quite saccesfull. Code:
for (int i = 0; i < FirefliesCount; ++i)
{
Firefly CurrentFirefly = new Firefly();
CurrentFirefly.Speed = Randomer.Next(1, 3);
CurrentFirefly.Body = new Ellipse();
CurrentFirefly.Body.Margin = new Thickness(Randomer.Next(10, (int)MainCanvas.Width - 10),
Randomer.Next(10, (int)MainCanvas.Height - 10),
0, 0);
CurrentFirefly.Body.Fill = Brushes.Black;
CurrentFirefly.Body.Height = MainCanvas.Height / 4;
CurrentFirefly.Body.Width = 1.5 * CurrentFirefly.Body.Height;
MainCanvas.Children.Add(CurrentFirefly.Body);
}
And Fireflie class:
class Firefly
{
public Ellipse Body { get; set; }
public int Speed { get; set; }
}
Probably you did not set the Width and Height properties of your MainCanvas; then they have the value NaN and therefore you will not see the ellipses.
My suggestion is to use ActualWidth and ActualHeight instead and to delay the adding of the ellipses until the canvas is loaded. Here is an example:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainCanvas.Loaded += MainCanvas_Loaded;
}
void MainCanvas_Loaded(object sender, RoutedEventArgs e)
{
Init();
}
private void Init()
{
const int FirefliesCount = 100;
Random Randomer = new Random();
for (int i = 0; i < FirefliesCount; ++i)
{
Firefly CurrentFirefly = new Firefly();
CurrentFirefly.Speed = Randomer.Next(1, 3);
CurrentFirefly.Body = new Ellipse();
CurrentFirefly.Body.Margin = new Thickness(Randomer.Next(10, (int)MainCanvas.ActualWidth - 10),
Randomer.Next(10, (int)MainCanvas.ActualHeight - 10),
0, 0);
CurrentFirefly.Body.Fill = Brushes.Black;
CurrentFirefly.Body.Height = MainCanvas.ActualHeight / 4;
CurrentFirefly.Body.Width = 1.5 * CurrentFirefly.Body.Height;
MainCanvas.Children.Add(CurrentFirefly.Body);
}
}
}
The corresponding xaml file looks like this:
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Canvas x:Name="MainCanvas"/>
</Window>