How to center a bunch of controls programmatically in c# - c#

I'm pretty new to this community, and I have this app that adds controls programmatically.
I would like to center all of the added controls sort of like selecting them and pressing center on Visual Studio. And no I don't want to center each one aside.
Here's the code I used to get all the controls:
private void GetAllControl(Control c, List<Control> list)
{
//gets all controls and saves them to a list
foreach (Control control in c.Controls)
{
list.Add(control);
}
}
//And then call it like this
List<Control> list = new List<Control>();
GetAllControl(PNL_custom, list);
foreach (Play_panel m in list)
{
//And here I want to insert that center code
}
Thanks in advance,
VBTheory

Here's how to center the controls as a GROUP. It's almost the same as before except we compute how far the center of mass for the group has to move to become the center of the parent control. Then we iterate over all the controls and offset their locations by that much. This centers them all while maintaining their positions relative to each other:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
List<Control> list = new List<Control>();
GetAllControl(PNL_custom, list);
CenterControlsAsGroup(list, Direction.Both); // center group in the center of the parent
}
public enum Direction
{
Vertical,
Horizontal,
Both
}
private void CenterControlsAsGroup(List<Control> controls, Direction direction)
{
if (controls.Count > 1)
{
int xSum = 0;
int ySum = 0;
Point center;
foreach (Control ctl in controls)
{
center = new Point(ctl.Location.X + ctl.Width / 2, ctl.Location.Y + ctl.Height / 2);
xSum = xSum + center.X;
ySum = ySum + center.Y;
}
Point average = new Point(xSum / controls.Count, ySum / controls.Count);
center = new Point(controls[0].Parent.Width / 2, controls[0].Parent.Height / 2);
int xOffset = center.X - average.X;
int yOffset = center.Y - average.Y;
foreach (Control ctl in controls)
{
switch (direction)
{
case Direction.Vertical:
ctl.Location = new Point(ctl.Location.X + xOffset, ctl.Location.Y);
break;
case Direction.Horizontal:
ctl.Location = new Point(ctl.Location.X, ctl.Location.Y + yOffset);
break;
case Direction.Both:
ctl.Location = new Point(ctl.Location.X + xOffset, ctl.Location.Y + yOffset);
break;
}
}
}
}
private void GetAllControl(Control c, List<Control> list)
{
//gets all controls and saves them to a list
foreach (Control control in c.Controls)
{
list.Add(control);
}
}
}

"And no I don't want to center each one aside."
So you want to "Align" the List of Controls?...as in:
Format --> Align --> Centers
Format --> Align --> Middles
If yes , then compute the center of each control and add up the X, Y coords so you can compute an "average" point (the center of mass). Now you can iterate over the controls and use that as the aligning X or Y value, depending on your desired direction. Simply subtract half the width or height and keep the other value.
Something like:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
List<Control> list = new List<Control>();
GetAllControl(PNL_custom, list);
CenterControls(list, Direction.Vertical);
}
public enum Direction
{
Vertical,
Horizontal
}
private void CenterControls(List<Control> controls, Direction direction)
{
if (controls.Count > 1)
{
int xSum = 0;
int ySum = 0;
Point center;
foreach (Control ctl in controls)
{
center = new Point(ctl.Location.X + ctl.Width / 2, ctl.Location.Y + ctl.Height / 2);
xSum = xSum + center.X;
ySum = ySum + center.Y;
}
Point average = new Point(xSum / controls.Count, ySum / controls.Count);
foreach (Control ctl in controls)
{
switch (direction)
{
case Direction.Vertical:
ctl.Location = new Point(average.X - ctl.Width / 2, ctl.Location.Y);
break;
case Direction.Horizontal:
ctl.Location = new Point(ctl.Location.X, average.Y - ctl.Height / 2);
break;
}
}
}
}
private void GetAllControl(Control c, List<Control> list)
{
//gets all controls and saves them to a list
foreach (Control control in c.Controls)
{
list.Add(control);
}
}
}

Get the width and height of the control's container (which is either another control or the form). The coordinates of controls are distances in pixels, relative to the upper left corner of their containers (which is (0,0)). So all you have to do is set a control's x coordinate to be (form width - control width) / 2. Same goes for height.

All your controls will be added to a container (most likely the Form) though they could easily be in a group box etc.
To align them center inside the container you need to do a little maths and position them yourself :)
Before you can center the control in the container you need to find the midpoint of the container. This will be the container's width / 2 : container's height / 2.
I will use a single control called cmdButton1 to highlight - you will want to iterate through your list of controls and do this to all of them in turn.
int midParentX = cmdButton1.Parent.width / 2;
int midParentX = cmdButton1.Parent.height / 2;
Then you can position a control at this midpoint:
cmdButton1.Location.X = midParentX;
cmdButton1.Location.Y = midParentY;
However, your control (cmdButton in my example) is anchored at (0,0) being the top left corner of the control, so we need to move it back and up by half its own width and height
cmdButton1.Location.X -= (cmdButton1.width / 2);
cmdButton1.Location.Y -= (cmdButton1.height / 2);
To be more relevant:
foreach (Play_panel m in list)
{
int pX = m.Parent.width;
int pY = m.Parent.height;
m.Location.X = (pX / 2) - (m.width / 2);
m.Location.Y = (pY / 2) - (m.height / 2);
}

So i did it on this way:
public enum ArrangeOrientation : int {
None,
Horizonatal,
Vertical,
HorizontalGrid,
VerticalGrid,
TopLeftGrid,
TopRightGrid,
BottomLeftGrid,
BottomRightGrid
}
private void ArrangeButtons(List<Control> controls, List<Control> parents, ArrangeOrientation orientation, double shrinkFactor = 1d) {
if(controls == null) return;
if(parents == null) parents = new List<Control>();
List<Control> childs = new List<Control>();
Control parent = null;
foreach(Control ctrl in controls) {
if(parent == null && !parents.Contains(ctrl.Parent)) {
parents.Add(ctrl.Parent);
parent = ctrl.Parent;
}
if(parent == ctrl.Parent)
childs.Add(ctrl);
}
if(parent != null && childs.Count > 0) {
ArrangeControlsToGridLayout(childs, orientation, shrinkFactor);
ArrangeButtons(controls, parents, orientation, shrinkFactor);
}
}
private void ArrangeControlsToGridLayout(List<Control> controls, ArrangeOrientation orientation, double shrinkFactor = 1d) {
// do nothing if nothing set
if(orientation == ArrangeOrientation.None) return;
if(shrinkFactor == 0d|| shrinkFactor > 1d) shrinkFactor = 1d;
// buffer controls in separate list to avoid manipulating parameter
List<Control> ctrl = new List<Control>(controls.ToArray());
// remove invisible controls
int j = 0;
while(j < ctrl.Count) {
if(!ctrl[j].Visible) ctrl.RemoveAt(j);
else j++;
}
// loop arrangement
int count = ctrl.Count;
int xDelta, yDelta, xOffs, yOffs, y, x, columns, rows, parentWidth, parentHeight, xShrinkOffs, yShrinkOffs;
if(count >= 1) {
// parents size
parentWidth = ctrl[0].Parent.Width;
parentHeight = ctrl[0].Parent.Height;
// shrink factor offset
parentWidth = Convert.ToInt32(parentWidth * shrinkFactor);
parentHeight = Convert.ToInt32(parentHeight * shrinkFactor);
// shrink factor offset
xShrinkOffs = Convert.ToInt32((ctrl[0].Parent.Width - parentWidth) / 2d);
yShrinkOffs = Convert.ToInt32((ctrl[0].Parent.Height - parentHeight) / 2d);
// calculate columns rows grid layout
if(orientation == ArrangeOrientation.Horizonatal) {
rows = 1;
columns = count;
}
else if(orientation == ArrangeOrientation.Vertical) {
rows = count;
columns = 1;
}
else if(orientation == ArrangeOrientation.TopLeftGrid
|| orientation == ArrangeOrientation.TopRightGrid
|| orientation == ArrangeOrientation.BottomLeftGrid
|| orientation == ArrangeOrientation.BottomRightGrid) {
rows = 1;
columns = count;
}
else {
rows = Convert.ToInt32(Math.Floor(Math.Sqrt(count)));
if(Math.Sqrt(count) % 1d != 0d) rows++;
columns = count / rows + (count % rows != 0 ? 1 : 0);
}
if(orientation == ArrangeOrientation.HorizontalGrid) {
int swap = columns;
columns = rows;
rows = columns;
}
// calculate position offsets, grid distance
xDelta = parentWidth / count;
yDelta = parentHeight / count;
xOffs = xDelta / 2;
yOffs = yDelta / 2;
if(orientation == ArrangeOrientation.TopLeftGrid) {
}
else if(orientation == ArrangeOrientation.TopRightGrid) {
xOffs = parentWidth - xOffs;
xDelta = -xDelta;
}
else if(orientation == ArrangeOrientation.BottomLeftGrid) {
yOffs = parentHeight - yOffs;
yDelta = -yDelta;
}
else if(orientation == ArrangeOrientation.BottomRightGrid) {
xOffs = parentWidth - xOffs;
yOffs = parentHeight - yOffs;
xDelta = -xDelta;
yDelta = -yDelta;
}
else {
xDelta = parentWidth / columns;
yDelta = parentHeight / rows;
xOffs = xDelta / 2;
yOffs = yDelta / 2;
}
// fit controls in grid layout
Point pRoot = new Point(/*ctrl[0].Parent.Location.X + */xOffs, /*ctrl[0].Parent.Location.Y + */yOffs);
y = 0; x = 0;
for(int i = 0; i < count; i++) {
if(orientation == ArrangeOrientation.VerticalGrid) {
// actual x/y - points zero based index
y = Convert.ToInt32(Math.Floor((double)i % rows));
// next row? zero based index
if(i % rows == 0 && i != 0) x++;
}
else {
// actual x/y - points zero based index
x = Convert.ToInt32(Math.Floor((double)i % columns));
// next row? zero based index
if(i % columns == 0 && i != 0) y++;
if(orientation == ArrangeOrientation.TopLeftGrid
|| orientation == ArrangeOrientation.TopRightGrid
|| orientation == ArrangeOrientation.BottomLeftGrid
|| orientation == ArrangeOrientation.BottomRightGrid)
y = x;
} // assign controls to grid
ctrl[i].Location = new Point(pRoot.X + x * xDelta - ctrl[i].Size.Width / 2 + xShrinkOffs, pRoot.Y + y * yDelta - ctrl[i].Size.Height / 2 + yShrinkOffs);
}
}
}

When there are multiple controls,above code was placing all controls on top of each other.
All i am trying to do is to center all the controls inside a panel but to put them next to each other instead of putting one over the other.
Here is my modified code (FYI this will center controls in 1 line not multiple line):
public enum Direction
{
Vertical,
Horizontal,
Both
}
public void CenterControls(List<Control> controls, Direction direction)
{
if (controls.Count > 1)
{
int controls_sum_width = 0;
int controls_seperation = 20;
int parentwidth = 0;
Point center;
foreach (Control ctl in controls)
{
controls_sum_width = controls_sum_width + ctl.Width + controls_seperation;
}
Point Container_center = new Point(controls[0].Parent.Width / 2, controls[0].Parent.Height / 2);
parentwidth = controls[0].Parent.Width;
int xoffset = (parentwidth - controls_sum_width) / 2;
int Location_X = 0;
foreach (Control ctl in controls)
{
center = new Point( ctl.Width / 2, ctl.Height / 2);
int yOffset = Container_center.Y - center.Y;
switch (direction)
{
case Direction.Vertical:
ctl.Location = new Point(ctl.Location.X + xoffset, ctl.Location.Y);
break;
case Direction.Horizontal:
ctl.Location = new Point(ctl.Location.X, yOffset);
break;
case Direction.Both:
ctl.Location = new Point(Location_X + xoffset, yOffset);
break;
}
Location_X = Location_X + ctl.Width+ controls_seperation;
}
}
else
{
Point parent_center;
Point center;
parent_center = new Point(controls[0].Parent.Width / 2, controls[0].Parent.Height / 2);
center = new Point(controls[0].Location.X + controls[0].Width / 2, controls[0].Location.Y + controls[0].Height / 2);
int xOffset = parent_center.X - center.X;
int yOffset = parent_center.Y - center.Y;
switch (direction)
{
case Direction.Vertical:
controls[0].Location = new Point(controls[0].Location.X + xOffset, controls[0].Location.Y);
break;
case Direction.Horizontal:
controls[0].Location = new Point(controls[0].Location.X, controls[0].Location.Y + yOffset);
break;
case Direction.Both:
controls[0].Location = new Point(controls[0].Location.X + xOffset, controls[0].Location.Y + yOffset);
break;
}
}
}
public void GetAllControl(Control c, List<Control> list)
{
//gets all controls and saves them to a list
foreach (Control control in c.Controls)
{
list.Add(control);
}
}

Related

A function in my program seems like it should work but occasionally gives the wrong output

I wrote a function that checks for a given cell in a grid if it is a legal move following the rules of the board-game called Reversi otherwise known as Othello. The rules are that a circle can only be placed on the grid whenever the newly placed circle and a previous placed circle bounds one of the opponents circles. The majority of the time the function gives the right output (i.e. true when it is a legal move and false when it is a legal move), but some moves that are legal by the previously stated rules are not deemed as a legal move by the function.
I've tried to use the console to check at every step at which cell the function is currently looking and what the value of the cell is to determine what goes wrong. This has only led me to being even more confused.
The following code is the doomed-function:
bool legalMove(int row, int col)
{
// Check if the cell is occupied
if (board[row,col] != 0)
return false;
// Check if there's an opponents circle somewhere around it
for (int i = -1; i<=1; i++)
for (int j = -1; j<=1; j++)
{
if (i == 0 && j == 0)
continue;
int currentRow = row + i;
int currentCol = col + j;
if (currentRow >= 0 && currentRow < board.GetLength(0) && currentCol >= 0 && currentCol < board.GetLength(1) && board[currentRow,currentCol] == -turn)
{
// Now we know that there's an opponents circle somewhere around this space, we now check if it can be captured
while(true)
{
currentRow += i;
currentCol += j;
Console.WriteLine($"currentRow: {currentRow}, currentCol: {currentCol}, value: {board[currentRow,currentCol]}");
if (currentRow < 0 || currentRow >= board.GetLength(0) || currentCol < 0 || currentCol >= board.GetLength(1) || board[currentRow, currentCol] == 0)
return false; // Outside of the board or an empty space
else if (board[currentRow,currentCol] == turn)
return true; // No empty spaces between our cell and another cell of ours
}
}
}
return false; // No cell found around ours
}
What am I missing here?
Thanks in advance!
EDIT:
The entire program is the following (hope it can help):
/* TO-DO
* Make function out of no legal move and tidy up
* Calculate amount of circles of player to determine the winner
* Make victory label better
!!!Tidy up flipCircles method and fix legalMove method
Create a slider and make it change the gridSize
Make GUI change empty space when board gets smaller or bigger
Tidy up the 2x calling to check what the score is
*/
// Library imports
using System;
using System.Drawing;
using System.Security.Policy;
using System.Windows.Forms;
// Game class
class Game : Form
{
// Declare variables
private Board board;
private Button newGame, help;
private Font font;
private Label countRed, countBlue, gameState;
private TrackBar sizeBoard;
public int gridSize = 6;
public Game()
{
// Set the form properties
ClientSize = new Size(520, 670); Text = "Reversi";
// Creating the GUI and adding it to the form
newGame = new Button(); Controls.Add(newGame);
help = new Button(); Controls.Add(help);
font = new Font("Arial", 14);
countBlue = new Label(); Controls.Add(countBlue); countBlue.Font = font;
countRed = new Label(); Controls.Add(countRed); countRed.Font = font;
gameState = new Label(); Controls.Add(gameState); gameState.Font = font;
board = new Board(gridSize); Controls.Add(board);
// Settings of the GUI
newGame.Size = new Size(100, 30); newGame.Location = new Point(150, 10); newGame.Text = "New Game"; newGame.BackColor = Color.LightSlateGray;
help.Size = new Size(100, 30); help.Location = new Point(270, 10); help.Text = "Help"; help.BackColor = Color.LightSlateGray;
countBlue.Size = new Size(110, 30); countBlue.Location = new Point(150, 50); countBlue.Text = $"{board.countBlue} stones"; countBlue.ForeColor = Color.CornflowerBlue;
countRed.Size = new Size(110, 30); countRed.Location = new Point(150, 90); countRed.Text = $"{board.countRed} stones"; countRed.ForeColor = Color.Firebrick;
gameState.Size = new Size(150, 30); gameState.Location = new Point(270, 90); gameState.Text = $"{board.playersTurn}";
// Events //
// Label events
newGame.Click += reset;
help.Click += calculateHelp;
// Board events
board.MouseClick += clicked;
//Paint event
Paint += paint;
}
// Event-handlers //
// Label event-handlers
private void reset(object e, EventArgs ea)
{
board.Reset();
countBlue.Text = $"{board.countBlue} stones";
countRed.Text = $"{board.countRed} stones";
gameState.Text = $"{board.playersTurn}";
}
private void calculateHelp(object e, EventArgs ea)
{
board.SetHelp();
}
// Board event-handlers
private void clicked(object e, MouseEventArgs mea)
{
board.Clicked(mea.Location);
countBlue.Text = $"{board.countBlue} stones";
countRed.Text = $"{board.countRed} stones";
gameState.Text = $"{board.playersTurn}";
// Now check if there's a legalMove if not
//if (timesNoLegalMove > 1)
gameState.Text = $"{board.playersTurn}";
}
// Paint event-handler
private void paint(object e, PaintEventArgs pea)
{
Graphics gr = pea.Graphics;
gr.FillEllipse(Brushes.CornflowerBlue, 100, 45, 31, 31);
gr.FillEllipse(Brushes.Firebrick, 100, 85, 31, 31);
}
}
// Board class
class Board : Label
{
// Declare all global variables used in this class
private int[,] board;
private int size;
private int turn = 1; // 1 is blue, -1 is red
private bool legalMoveExists = true;
public int timesNoLegalMove = 0;
private bool help = false;
// Create the board and set settings + events
public Board(int gridSize)
{
size = gridSize;
Size = new Size(size * 50, size * 50);
Location = new Point(10 + (25 * (10 - size)), 120 + (25 * (10 - size)));
BackColor = Color.White;
board = new int[size, size];
startingState();
Paint += Draw;
}
// Sets the values of the center 4 squares to that of the starting circles
private void startingState()
{
board[(size / 2)-1, (size / 2)-1] = 1;
board[(size / 2), (size / 2)] = 1;
board[(size / 2), (size / 2) - 1] = -1;
board[(size / 2) - 1, (size / 2)] = -1;
}
// Resets the board when New Game is clicked
public void Reset()
{
for (int row = 0; row < board.GetLength(0); row++)
for (int col = 0; col < board.GetLength(1); col++)
board[row, col] = 0;
startingState();
turn = 1;
Invalidate();
}
public void SetHelp()
{
if (help)
help = false;
else
help = true;
Invalidate();
}
public string playersTurn
{
get
{
if (turn == 1)
return "It's Blue's turn";
if (turn == -1)
return "It's Red's turn";
else
{
if (countBlue > countRed)
return "Blue has won the game!";
if (countRed > countBlue)
return "Red has won the game!";
else
return "It's a draw!";
}
}
}
public int countRed
{
get
{
return board.Cast<int>().Count(n => n == -1);
}
}
public int countBlue
{
get
{
return board.Cast<int>().Count(n => n == 1);
}
}
bool legalMove(int row, int col)
{
// Check if the cell is occupied
if (board[row,col] != 0)
return false;
// Check if there's an opponents circle somewhere around it
for (int i = -1; i<=1; i++)
for (int j = -1; j<=1; j++)
{
if (i == 0 && j == 0)
continue;
int currentRow = row + i;
int currentCol = col + j;
if (currentRow >= 0 && currentRow < board.GetLength(0) && currentCol >= 0 && currentCol < board.GetLength(1) && board[currentRow,currentCol] == -turn)
{
// Now we know that there's an opponents circle somewhere around this space, we now check if it can be captured
while(true)
{
currentRow += i;
currentCol += j;
if (currentRow < 0 || currentRow >= board.GetLength(0) || currentCol < 0 || currentCol >= board.GetLength(1) || board[currentRow, currentCol] == 0)
return false; // Outside of the board or an empty space
else if (board[currentRow,currentCol] == turn)
return true; // No empty spaces between our cell and another cell of ours
}
}
}
return false; // No cell found around ours
}
private void flipCircles(int row, int col)
{
// Check all eight directions from the current position
for (int r = row - 1; r <= row + 1; r++)
{
for (int c = col - 1; c <= col + 1; c++)
{
// Skip the current position
if (r == row && c == col)
continue;
int rr = r;
int cc = c;
// Check if the next position in this direction is a valid position on the board
// and if it is occupied by the opponent's piece
if (rr >= 0 && rr < board.GetLength(0) && cc >= 0 && cc < board.GetLength(1) && board[rr, cc] == -turn)
{
// Keep moving in this direction until we find the current player's piece or an empty cell
while (true)
{
rr += r - row;
cc += c - col;
// If we have reached an invalid position or an empty cell, break out of the loop
if (rr < 0 || rr >= board.GetLength(0) || cc < 0 || cc >= board.GetLength(1) || board[rr, cc] == 0)
break;
// If we have found the current player's piece, flip all the pieces between the current position and the player's piece
if (board[rr, cc] == turn)
{
while (rr != r || cc != c)
{
rr -= r - row;
cc -= c - col;
board[rr, cc] = turn;
}
break;
}
}
}
}
}
}
// Sets the value of a clicked cell to either 1 (Blue), or -1 (Red)
public void Clicked(Point mea)
{
int rowClicked = mea.X / 50;
int colClicked = mea.Y /50;
if (legalMove(rowClicked, colClicked))
{
board[rowClicked, colClicked] = turn;
flipCircles(rowClicked, colClicked);
help = false;
turn = -turn;
legalMoveExists = false;
Invalidate();
}
}
// Draws the entire board and all circles
void Draw(object e, PaintEventArgs pea)
{
Graphics gr = pea.Graphics;
legalMoveExists = false;
for (int row = 0; row < board.GetLength(0); row++)
for (int col = 0; col < board.GetLength(1); col++)
{
// Draw the background tiles
if (row % 2 == 0 && col % 2 == 0 || row % 2 != 0 && col % 2 != 0)
gr.FillRectangle(Brushes.DarkGray, 50 * row, 50 * col, 50, 50);
// Draw circles
if (board[row, col] == 1) // Blue circles
gr.FillEllipse(Brushes.CornflowerBlue, 50 * row - 1, 50 * col - 1, 51, 51);
else if (board[row, col] == -1) // Red circles
gr.FillEllipse(Brushes.Firebrick, 50 * row - 1, 50 * col - 1, 51, 51);
// Check for legal moves and draw help circles if the help button has been pressed
else if (legalMove(row, col))
{
legalMoveExists = true;
timesNoLegalMove = 0;
if (help) // Help circles
gr.DrawEllipse(Pens.Black, 50 * row + 9, 50 * col + 9, 31, 31);
}
}
// Make this a function
if (!legalMoveExists)
{
turn = -turn;
timesNoLegalMove++;
Invalidate();
if (timesNoLegalMove > 1)
turn = 0;
}
}
}
// Main run
class Program
{
static void Main()
{
Application.Run(new Game());
}
}
Your question states that the function you wrote to scan the surrounding cells is failing sporadically and that it's difficult to diagnose. It was easy to reproduce the failures by running your code, but I wasn't able to see an obvious way to effectively debug it.
It "might" be more effective to improve the algorithm where it's more methodical in how it inspects the surrounding cells in the first place, which would also be easier to debug if necessary. One solid way to do this would be to use custom Iterators where you could use a standard foreach pattern to inspect virtual "lines" radiating in the eight directions. At each 'yield' you can check to see whether a determination can be made in terms of either a "legal move" or a "capture".
Here's a proof-of-concept grid that is intended to demonstrate how the iterators work. It doesn't evaluate the cells in terms of game play in any way but you can see how it would lend itself to doing that. The idea here is to click any cell and observe the markup of U-R-D-L. It may also help to see it working so you can clone this sample and set breakpoints.
Left, Right, Up, Down iterator examples are shown - diagonals would follow the same pattern. The mouse down control passes the starting cell coordinate position as a Point:
public IEnumerable<Point> CellsUp(Point point)
{
while (true)
{
point = new Point(point.X, point.Y - 1);
if (point.Y < 0) break;
yield return point;
}
}
public IEnumerable<Point> CellsRight(Point point, int max)
{
while (true)
{
point = new Point(point.X + 1, point.Y);
if (point.X == max) break;
yield return point;
}
}
public IEnumerable<Point> CellsDown(Point point, int max)
{
while (true)
{
yield return point;
point = new Point(point.X, point.Y + 1);
if (point.Y == max) break;
}
}
public IEnumerable<Point> CellsLeft(Point point)
{
while (true)
{
yield return point;
point = new Point(point.X - 1, point.Y);
if (point.X < 0) break;
}
}
The code lays the groundwork for a methodical scan outward from any given point.
private void legalMoveIterationStub(object? sender, EventArgs e)
{
clear();
if(sender is Control control)
{
control.BackColor = Color.Blue;
control.Refresh();
var pos = board.GetCellPosition(control);
var pt = new Point(pos.Column, pos.Row);
Control ctrl;
foreach (var point in CellsUp(pt))
{
ctrl = board.GetControlFromPosition(point.X, point.Y);
ctrl.Text = "U";
ctrl.Refresh();
Thread.Sleep(DEMO_DELAY_MS);
// This is where the cell inspects e.g. for "empty square"
// or color. Chances are, some condition will be met
// and you will break from here rather than iterate
// all the way to the edge of the board each time.
}
foreach (var point in CellsRight(pt, board.ColumnCount))
{
ctrl = board.GetControlFromPosition(point.X, point.Y);
ctrl.Text = "R";
ctrl.Refresh();
Thread.Sleep(DEMO_DELAY_MS);
}
foreach (var point in CellsDown(pt, board.ColumnCount))
{
ctrl = board.GetControlFromPosition(point.X, point.Y);
ctrl.Text = "D";
ctrl.Refresh();
Thread.Sleep(DEMO_DELAY_MS);
}
foreach (var point in CellsLeft(pt))
{
ctrl = board.GetControlFromPosition(point.X, point.Y);
ctrl.Text = "L";
ctrl.Refresh();
Thread.Sleep(DEMO_DELAY_MS);
}
}
}
The demo board has been mocked like this for testing purposes:
public partial class Game : Form
{
public Game()
{
InitializeComponent();
for (int col = 0; col < board.ColumnCount; col++)
{
for (int row = 0; row < board.RowCount; row++)
{
var tile = new Label
{
BorderStyle = BorderStyle.FixedSingle,
Anchor = (AnchorStyles)0xF,
Margin = new Padding(1),
TextAlign = ContentAlignment.MiddleCenter
};
board.Controls.Add(tile, col, row);
tile.MouseDown += legalMove;
}
}
}
void clear()
{
foreach (Control control in board.Controls)
{
control.Text = string.Empty;
control.BackColor = SystemColors.Control;
}
board.Refresh();
}
.
.
.
}

C# winforms - How to combine scrollbar with mouse wheel zooming?

Problem - I'm writing a program that draws graphics, and zooming is one of the features. Currently, a picturebox is placed on a panel, and the picturebox has vertical and horizontal scroll bars on the right and bottom. How to combine scrollbar with mouse wheel zooming? And I'm not sure if I should use paint to draw the graphics or set a bitmap to draw the graphics onto it?
Expected - When the mouse wheel is scrolled, the entire canvas(picturebox) include drawn graphics are scaled according to the current mouse position as the center (the horizontal and vertical scroll bars change according to the zoom center). When the mouse wheel is pressed and moved, the canvas can be dragged freely.
Expected as follows:
The initial code
private List<Point> _points;
private int _pointRadius = 50;
private float _scale = 1f;
private float _offsetX = 0f;
private float _offsetY = 0f;
private void picturebox_MouseDown(object sender, MouseEventArgs e)
{
_points.Add(e.Location);
}
private void picturebox_MouseWheel(object sender, MouseEvnetArgs e)
{
if(e.Delta < 0)
{
_scale += 0.1f;
_offsetX = e.X * (1f - _scale);
_offsetY = e.X * (1f - _scale);
}
else
{
_scale -= 0.1f;
_offsetX = e.X * (1f - _scale);
_offsetY = e.X * (1f - _scale);
}
picturebox.Invalidate();
}
private void picturebox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TranslateTransform(_offsetX, _offsetY);
e.Graphics.ScaleTransform(_scaleX, _scaleY);
foreach (Point p in _points)
{
e.Graphics.FillEllipse(Brushes.Black, p.X, - _pointRadius, p.Y - _pointRadius, 2 * _pointRadius, 2 * _pointRadius);
}
}
Hope the answer is modified based on the initial code.
Thanks in advance to everyone who helped me.
Would it be easier if I drew the graphics on a Bitmap?
Considering the nature of your task and the already implemented solutions in my ImageViewer I created a solution that draws the result in a Metafile, which is both elegant, consumes minimal memory and allows zooming without quality issues.
Here is the stripped version of my ImageViewer:
public class MetafileViewer : Control
{
private HScrollBar sbHorizontal = new HScrollBar { Visible = false };
private VScrollBar sbVertical = new VScrollBar { Visible = false };
private Metafile? image;
private Size imageSize;
private Rectangle targetRectangle;
private Rectangle clientRectangle;
private float zoom = 1;
private bool sbHorizontalVisible;
private bool sbVerticalVisible;
private int scrollFractionVertical;
public MetafileViewer()
{
Controls.AddRange(new Control[] { sbHorizontal, sbVertical });
sbHorizontal.ValueChanged += ScrollbarValueChanged;
sbVertical.ValueChanged += ScrollbarValueChanged;
}
void ScrollbarValueChanged(object? sender, EventArgs e) => Invalidate();
public Metafile? Image
{
get => image;
set
{
image = value;
imageSize = image?.Size ?? default;
InvalidateLayout();
}
}
public bool TryTranslate(Point mouseCoord, out PointF canvasCoord)
{
canvasCoord = default;
if (!targetRectangle.Contains(mouseCoord))
return false;
canvasCoord = new PointF((mouseCoord.X - targetRectangle.X) / zoom, (mouseCoord.Y - targetRectangle.Y) / zoom);
if (sbHorizontalVisible)
canvasCoord.X += sbHorizontal.Value / zoom;
if (sbVerticalVisible)
canvasCoord.Y += sbVertical.Value / zoom;
return true;
}
private void InvalidateLayout()
{
Invalidate();
if (imageSize.IsEmpty)
{
sbHorizontal.Visible = sbVertical.Visible = sbHorizontalVisible = sbVerticalVisible = false;
targetRectangle = Rectangle.Empty;
return;
}
Size clientSize = ClientSize;
if (clientSize.Width < 1 || clientSize.Height < 1)
{
targetRectangle = Rectangle.Empty;
return;
}
Size scaledSize = imageSize.Scale(zoom);
// scrollbars visibility
sbHorizontalVisible = scaledSize.Width > clientSize.Width
|| scaledSize.Width > clientSize.Width - SystemInformation.VerticalScrollBarWidth && scaledSize.Height > clientSize.Height;
sbVerticalVisible = scaledSize.Height > clientSize.Height
|| scaledSize.Height > clientSize.Height - SystemInformation.HorizontalScrollBarHeight && scaledSize.Width > clientSize.Width;
if (sbHorizontalVisible)
clientSize.Height -= SystemInformation.HorizontalScrollBarHeight;
if (sbVerticalVisible)
clientSize.Width -= SystemInformation.VerticalScrollBarWidth;
if (clientSize.Width < 1 || clientSize.Height < 1)
{
targetRectangle = Rectangle.Empty;
return;
}
Point clientLocation = Point.Empty;
var targetLocation = new Point((clientSize.Width >> 1) - (scaledSize.Width >> 1),
(clientSize.Height >> 1) - (scaledSize.Height >> 1));
// both scrollbars
if (sbHorizontalVisible && sbVerticalVisible)
{
sbHorizontal.Dock = sbVertical.Dock = DockStyle.None;
sbHorizontal.Width = clientSize.Width;
sbHorizontal.Top = clientSize.Height;
sbHorizontal.Left = 0;
sbVertical.Height = clientSize.Height;
sbVertical.Left = clientSize.Width;
}
// horizontal scrollbar
else if (sbHorizontalVisible)
sbHorizontal.Dock = DockStyle.Bottom;
// vertical scrollbar
else if (sbVerticalVisible)
sbVertical.Dock = DockStyle.Right;
// adjust scrollbar values
if (sbHorizontalVisible)
{
sbHorizontal.Minimum = targetLocation.X;
sbHorizontal.Maximum = targetLocation.X + scaledSize.Width;
sbHorizontal.LargeChange = clientSize.Width;
sbHorizontal.SmallChange = 32;
sbHorizontal.Value = Math.Min(sbHorizontal.Value, sbHorizontal.Maximum - sbHorizontal.LargeChange);
}
if (sbVerticalVisible)
{
sbVertical.Minimum = targetLocation.Y;
sbVertical.Maximum = targetLocation.Y + scaledSize.Height;
sbVertical.LargeChange = clientSize.Height;
sbVertical.SmallChange = 32;
sbVertical.Value = Math.Min(sbVertical.Value, sbVertical.Maximum - sbVertical.LargeChange);
}
sbHorizontal.Visible = sbHorizontalVisible;
sbVertical.Visible = sbVerticalVisible;
clientRectangle = new Rectangle(clientLocation, clientSize);
targetRectangle = new Rectangle(targetLocation, scaledSize);
if (sbVerticalVisible)
clientRectangle.X = SystemInformation.VerticalScrollBarWidth;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
InvalidateLayout();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (image == null || e.ClipRectangle.Width <= 0 || e.ClipRectangle.Height <= 0)
return;
if (targetRectangle.IsEmpty)
InvalidateLayout();
if (targetRectangle.IsEmpty)
return;
Graphics g = e.Graphics;
g.IntersectClip(clientRectangle);
Rectangle dest = targetRectangle;
if (sbHorizontalVisible)
dest.X -= sbHorizontal.Value;
if (sbVerticalVisible)
dest.Y -= sbVertical.Value;
g.DrawImage(image, dest);
g.DrawRectangle(SystemPens.ControlText, Rectangle.Inflate(targetRectangle, 1, 1));
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
switch (ModifierKeys)
{
// zoom
case Keys.Control:
float delta = (float)e.Delta / SystemInformation.MouseWheelScrollDelta / 5;
if (delta.Equals(0f))
return;
delta += 1;
SetZoom(zoom * delta);
break;
// vertical scroll
case Keys.None:
VerticalScroll(e.Delta);
break;
}
}
private void VerticalScroll(int delta)
{
// When scrolling by mouse, delta is always +-120 so this will be a small change on the scrollbar.
// But we collect the fractional changes caused by the touchpad scrolling so it will not be lost either.
int totalDelta = scrollFractionVertical + delta * sbVertical.SmallChange;
scrollFractionVertical = totalDelta % SystemInformation.MouseWheelScrollDelta;
int newValue = sbVertical.Value - totalDelta / SystemInformation.MouseWheelScrollDelta;
SetValueSafe(sbVertical, newValue);
}
internal static void SetValueSafe(ScrollBar scrollBar, int value)
{
if (value < scrollBar.Minimum)
value = scrollBar.Minimum;
else if (value > scrollBar.Maximum - scrollBar.LargeChange + 1)
value = scrollBar.Maximum - scrollBar.LargeChange + 1;
scrollBar.Value = value;
}
private void SetZoom(float value)
{
const float maxZoom = 10f;
float minZoom = image == null ? 1f : 1f / Math.Min(imageSize.Width, imageSize.Height);
if (value < minZoom)
value = minZoom;
if (value > maxZoom)
value = maxZoom;
if (zoom.Equals(value))
return;
zoom = value;
InvalidateLayout();
}
}
And then the updated version of your initial code (add a new point by right click, zoom by Ctrl + mouse scroll):
public partial class RenderMetafileForm : Form
{
private static Size canvasSize = new Size(300, 200);
private List<PointF> points = new List<PointF>();
private const float pointRadius = 5;
public RenderMetafileForm()
{
InitializeComponent();
metafileViewer.MouseClick += MetafileViewer_MouseClick;
UpdateMetafile();
}
private void MetafileViewer_MouseClick(object? sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && metafileViewer.TryTranslate(e.Location, out var coord))
{
points.Add(coord);
UpdateMetafile();
}
}
private void UpdateMetafile()
{
Graphics refGraph = Graphics.FromHwnd(IntPtr.Zero);
IntPtr hdc = refGraph.GetHdc();
Metafile result;
try
{
result = new Metafile(hdc, new Rectangle(Point.Empty, canvasSize), MetafileFrameUnit.Pixel, EmfType.EmfOnly, "Canvas");
using (var g = Graphics.FromImage(result))
{
foreach (PointF point in points)
g.FillEllipse(Brushes.Navy, point.X - pointRadius, point.Y - pointRadius, pointRadius * 2, pointRadius * 2);
}
}
finally
{
refGraph.ReleaseHdc(hdc);
refGraph.Dispose();
}
Metafile? previous = metafileViewer.Image;
metafileViewer.Image = result;
previous?.Dispose();
}
}
Result:
⚠️ Note: I did not add panning by keyboard or by grabbing the image but you can extract those from the original ImageViewer. Also, I removed DPI-aware scaling but see the ScaleSize extensions in the linked project.

how to control mouse pointer with head movement using emguCV C#?

I am developing a system just like Camera mouse or other face control mouse, I have implemented all the functionality, the mouse pointer is also moving well, but I want to create the movement smooth just like the mouse control the pointer. the code I am using is:
if (startButton == true)
{
try
{
cap = new Capture();
pictureBox1.Image = cap.QueryFrame().ToImage<Bgr, Byte>().Bitmap;
}
catch (Exception exp)
{
MessageBox.Show("Error:" + exp);
}
_cascadeClassifier = new CascadeClassifier(Application.StartupPath + "/haarcascade_frontalface_default.xml");
eye_cascadeClassifier = new CascadeClassifier(Application.StartupPath + "/haarcascade_eye.xml");
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
using (var imageFrame = cap.QueryFrame().ToImage<Bgr, Byte>().Flip(FlipType.Horizontal))
{
if (imageFrame != null)
{
var grayframe = imageFrame.Convert<Gray, byte>();
var faces = _cascadeClassifier.DetectMultiScale(grayframe, 1.1, 10, Size.Empty); //the actual face detection happens here
foreach (var face in faces)
{
if(Configure.FaceBoxCheck==true)
imageFrame.Draw(face, new Bgr(Color.LightGreen), 2); //the detected face(s) is highlighted here using a box that is drawn around it/them
Int32 yCoordStartSearchEyes = face.Top + (face.Height * 3 / 11);
Point startingPointSearchEyes = new Point(face.X, yCoordStartSearchEyes);
Size searchEyesAreaSize = new Size(face.Width, (face.Height * 3 / 11));
Rectangle possibleROI_eyes = new Rectangle(startingPointSearchEyes, searchEyesAreaSize);
int widthNav = (imageFrame.Width / 11 * 3);
int heightNav = (imageFrame.Height / 11 * 3);
Rectangle nav = new Rectangle(new Point(imageFrame.Width / 2 - widthNav / 2, imageFrame.Height / 2 - heightNav / 2), new Size(widthNav, heightNav));
imageFrame.Draw(nav, new Bgr(Color.Lavender), 3);
Point cursor = new Point(face.X + searchEyesAreaSize.Width / 2, yCoordStartSearchEyes + searchEyesAreaSize.Height / 2);
grayframe.ROI = possibleROI_eyes;
var eyes = eye_cascadeClassifier.DetectMultiScale(grayframe, 2.15, 3, Size.Empty);
foreach (var eye in eyes)
{
//imageFrame.Draw(eye, new Bgr(Color.Red), 2);
if(Configure.EyeBoxCheck==true)
imageFrame.Draw(possibleROI_eyes, new Bgr(Color.DarkGreen), 2);
if (nav.Left < cursor.X && cursor.X < (nav.Left + nav.Width) && nav.Top < cursor.Y && cursor.Y < nav.Top + nav.Height)
{
LineSegment2D CursorDraw = new LineSegment2D(cursor, new Point(cursor.X, cursor.Y + 1));
imageFrame.Draw(CursorDraw, new Bgr(Color.White), 3);
//we compute new cursor coordinate using a simple scale based on frame width and height
int xCoord = (imageFrame.Width * (cursor.X - nav.Left)) / nav.Width;
int yCoord = (imageFrame.Height * (cursor.Y - nav.Top)) / nav.Height;
//We set our new cursor position
Cursor.Position = new Point(xCoord * 2, yCoord *2);
}
}
}
Ok, I'm sure there are a lot of other better ways, but this is a quick&dirty way of moving cursor position in a "Smooth" way from point a to point b. Of course this implementation can and should be optimized using a different thread instead of using Application.DoEvents() to avoid blocking the UI thread, but i hope this gets you on the track. First, how you should use it. Instead of:
Cursor.Position = new Point(xCoord * 2, yCoord *2);
You should do this:
MoveCursorSmooth(Cursor.Position, new Point(xCoord * 2, yCoord *2));
Now, the implementation of MoveCursorSmooth:
private void MoveCursorSmooth(Point a, Point b)
{
var step = 5;
var left = Math.Min(a.X, b.X);
var right = Math.Max(a.X, b.X);
int width = right - left;
var top = a.Y;
var bottom = b.Y;
int height = bottom - top;
if (width > height)
{
double slope = (double)height / (double)width;
if (a.X <= b.X)
for (int x = 1; x < width; ++x)
{
Cursor.Position = new Point((left + x), (a.Y + ((int)(slope * x + 0.5))));
System.Threading.Thread.Sleep(step);
Application.DoEvents();
}
else
for (int x = 1; x < width; ++x) // xOffset
{
Cursor.Position = new Point((right - x), (a.Y + ((int)(slope * x + 0.5))));
System.Threading.Thread.Sleep(step);
Application.DoEvents();
}
}
else
{
double slope = (double)width / (double)height;
if (a.X <= b.X)
{
for (int y = 1; y < height; ++y)
{
Cursor.Position = new Point((a.X + ((int)(slope * y + 0.5))), (top + y));
System.Threading.Thread.Sleep(step);
Application.DoEvents();
}
}
else
{
for (int y = 1; y < height; ++y)
{
Cursor.Position = new Point((b.X + ((int)(slope * y + 0.5))), (bottom - y));
System.Threading.Thread.Sleep(step);
Application.DoEvents();
}
}
}
}
This method is based on this answer

Best way to draw a line without using a canvas C#

I am wondering what the best way to draw a line, on a maximum value, from a list is without using a canvas?
I have identified the Max, Min and Median I'm wondering what the best way to draw a line/point without using a canvas would be?
public partial class SpectrumControl : UserControl
{
private double Highest;
private double Minimum;
private double Median;
private int Total;
private int CellWidth;
public int Width { get; set; }
public SpectrumControl()
{
InitializeComponent();
}
public void Bind(KLayer klayer)
{
if (Width == 0)
{
Width = 300;
}
Highest = klayer.Values.Max();
Minimum = klayer.Values.Min();
Median = ((Highest - Minimum) / 2) + Minimum;
Total = klayer.Values.Count;
CellWidth = Width / Total;
int rowNumber = 0;
foreach (var item in klayer.Values)
{
var label = CreateLabel(item, rowNumber);
Color backgroundColour = GetColour(item);
stk1.Children.Add(label);
rowNumber++;
}
}
private Label CreateLabel(double item, int rowNumber)
{
var label = new Label()
{
Background = new SolidColorBrush(GetColour(item)),
Width = CellWidth
};
return label;
}
private Color GetColour(double item)
{
byte a = Convert.ToByte(GetTransparency(item)*255);
Color backgroundColour;
if (item < Median)
{
backgroundColour = Color.FromArgb(a, 128, 128, 255);
}
else if (item > Median)
{
backgroundColour = Color.FromArgb(a, 255, 128, 128);
}
else
{
backgroundColour = Colors.White;
}
return backgroundColour;
}
private double GetTransparency(double item)
{
double x = Highest - Minimum;
double difference;
if (item > Median)
{
difference = item - Median;
}
else
{
difference = Median - item;
}
var fraction = difference / x;
return fraction;
}
}
Well, assuming you are going to use something like GridPanel or any other panel, really, you could do this:
var line = new Line();
line.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
line.X1 = 1;
line.X2 = 50;
line.Y1 = 1;
line.Y2 = 50;
line.HorizontalAlignment = HorizontalAlignment.Left;
line.VerticalAlignment = VerticalAlignment.Center;
line.StrokeThickness = 2;
grid.Children.Add(line);
Same thing may be achieved in XAML, but it looks like you prefer to work in code-behind, so that is what I posted here.
Reference: https://msdn.microsoft.com/en-us/library/system.windows.shapes.line%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
I'm not sure why you'd avoid canvas, though (well, why someone told you to do that). I've created plenty of plots using canvas.

Zoom in and out to the center of the viewport in a canvas

I need to zoom in and out a canvas. But the zoom should always be centered to the center of the screen (not the canvas). And this doesnt work with my current code! It always zooms to the center of the canvas and not the current screen.
The canvas should also be moveable, but should not be aloweded to move away from it's borders.
This is what I have so far:
XAML
<Grid>
<Canvas HorizontalAlignment="Left" Name="canvas1" VerticalAlignment="Top">
</Canvas>
</Grid>
C#
// x & y arent the position, it's how many pixel the object should move to the left/right etc
public void setPosition(double x, double y)
{
Thickness t = new Thickness(canvas1.Margin.Left + x, canvas1.Margin.Top + y, 0, 0);
if (t.Left > 0)
{
t.Left = 0;
}
else if(t.Left < -(canvas1.Width - System.Windows.SystemParameters.PrimaryScreenWidth))
{
t.Left = -(canvas1.Width - System.Windows.SystemParameters.PrimaryScreenWidth);
}
if (t.Top > 0)
{
t.Top = 0;
}
else if (t.Top < -(canvas1.Height - System.Windows.SystemParameters.PrimaryScreenHeight))
{
t.Top = -(canvas1.Height - System.Windows.SystemParameters.PrimaryScreenHeight);
}
canvas1.Margin = t;
}
public void setZoom(double zoom)
{
double tempW = canvas1.Width;
double tempH = canvas1.Height;
canvas1.Width *= (1 + zoom);
canvas1.Height *= (1 + zoom);
Debug.WriteLine("tempW: " + tempW + " tempH: " + tempH + " canvas1.Width: " + canvas1.Width + " canvas1.Height: " + canvas1.Height);
setPosition((tempW - canvas1.Width) / 2, ((tempH - canvas1.Height) / 2));
}
And yes I know there is stuff like ScaleTransform but for some reason this is more performant.. dont ask me why!
Can someone help me please?
Here is my solution for Pan and Zoom, both works fine in my complex application (12000px x 12000px).
You need to have a Zoom dependency property (double) and a ScaleTransform property
private double m_dCurZoom = 1.0;
public double Zoom
{
get { return m_dCurZoom; }
set
{
double oldzoom = m_dCurZoom;
if (m_dCurZoom != value)
{
m_dCurZoom = value;
OnPropertyChanged("Zoom");
UpdateZoom(oldzoom);
}
}
}
private ScaleTransform m_transZoom;
public ScaleTransform TransZoom
{
get { return m_transZoom; }
}
private TranslateTransform m_transPan;
/// <summary>
/// Gets or sets the Panning in X axis.
/// </summary>
public double PanX
{
get { return m_transPan.X; }
set
{
if (m_transPan.X != value)
{
m_transPan.X = value;
ResizeElementContents();
}
}
}
/// <summary>
/// Gets or sets the Panning in Y axis.
/// </summary>
public double PanY
{
get { return m_transPan.Y; }
set
{
if (m_transPan.Y != value)
{
m_transPan.Y = value;
ResizeElementContents();
}
}
}
When you update the Zoom, you call this method to calculate the center point:
public void UpdateZoom(double oldzoom)
{
// Are we visible?
if (m_root == null)
return;
// Get parent of VirtualPanel
FrameworkElement parent = GetRootParent();
if (parent == null)
return;
// Center point of the window
Point ptCenter = new Point(parent.RenderSize.Width / 2, parent.RenderSize.Height / 2);
// Translate into canvas coordinates
ptCenter = m_root.TranslatePoint(ptCenter, m_canvas);
// Update the zoom
m_transZoom.ScaleX = m_dCurZoom;
m_transZoom.ScaleY = m_dCurZoom;
m_transPan.X -= ptCenter.X * m_dCurZoom - ptCenter.X * oldzoom;
m_transPan.Y -= ptCenter.Y * m_dCurZoom - ptCenter.Y * oldzoom;
ResizeElementContents();
OnPropertyChanged("Zoom");
}
/// <summary>
/// Calculate the transform for given zoom & region-to-display for the given root's render-size
/// </summary>
public bool ResizeElementContents()
{
FrameworkElement parent = GetRootParent();
if (parent == null || m_transPan == null)
return false;
// Calculate the total region of the root
Rect? region = WtoHelper.CalcElementRect(Root);
if (region == null || region.HasValue == false)
return false;
// Scale the region to get the actal size with the zoom transformation.
double rgnWid = region.Value.Width * m_dCurZoom;
double rgnHei = region.Value.Height * m_dCurZoom;
// Image fits within our window width?
if (parent.RenderSize.Width > rgnWid)
m_transPan.X = (parent.RenderSize.Width - rgnWid) / 2;
else
{
// Image needs to be resized
if (m_transPan.X < -(rgnWid - parent.RenderSize.Width))
m_transPan.X = -(rgnWid - parent.RenderSize.Width);
else if (m_transPan.X > 0)
m_transPan.X = 0;
}
// Image fits within our window height?
if (parent.RenderSize.Height > rgnHei)
m_transPan.Y = (parent.RenderSize.Height - rgnHei) / 2;
else
{
// Image needs to be resized
if (m_transPan.Y < -(rgnHei - parent.RenderSize.Height))
m_transPan.Y = -(rgnHei - parent.RenderSize.Height);
else if (m_transPan.Y > 0)
m_transPan.Y = 0;
}
return true;
}
I don't provide all the code but it's a base if you can understand what I wrote.
If you need more information (I know that this question is old, this is why I'm not detailing everything) let me know.

Categories