how do I make beautiful flipping images? - c#

When I opened the game launcher, I noticed how the news was implemented there.
And I really liked this idea, so I decided to do it in my project, first of all I made a panel, stuffed a few images into it, and then actually made two buttons with which I plan to flip through the images. BUT how to do it smoothly? Here is where the problem is, I do not understand how to make a smooth flipping of
the image

I can't tell how the images are sliding, flipping, stretching or whatever, but I think WinForms with GDI+ isn't the best choice. I think WPF would be better. I would also recommend using a suitable library for those kind of image manipulations.
However, if you want it very(!) simple you could use this class:
public class SlideAnimation
{
public event EventHandler<AnimationEventArgs> AnimationFinished;
private readonly Control Control;
private readonly Timer Timer;
private float fromXPosition;
public SlideAnimation(Control ctrl)
{
Control = ctrl;
Timer = new Timer();
Timer.Interval = 10;
Timer.Tick += Timer_Tick;
Control.Paint += Control_Paint;
}
public float Speed { get; set; }
public Image From { get; set; }
public Image To { get; set; }
public AnimationDirection Direction { get; set; }
public bool IsRunning
{
get
{
return Timer.Enabled;
}
}
public void StartAnimation()
{
// maybe move those checks into the setter of the corresponding property
if (this.From == null)
throw new InvalidOperationException();
if (this.To == null)
throw new InvalidOperationException();
if (this.Speed <= 0)
throw new InvalidOperationException();
fromXPosition = 0;
Timer.Enabled = true;
}
protected void OnAnimationFinished(AnimationEventArgs e)
{
AnimationFinished?.Invoke(this, e);
}
private void Timer_Tick(object sender, EventArgs e)
{
// increase or decrease the position of the first image
fromXPosition = fromXPosition + (this.Speed * this.Direction);
Control.Invalidate();
if (Math.Abs(fromXPosition) >= this.From.Width)
{
Timer.Enabled = false;
OnAnimationFinished(new AnimationEventArgs(this.Direction));
}
}
private void Control_Paint(object sender, PaintEventArgs e)
{
if (!Timer.Enabled)
return;
// draw both images next to each other depending on the direction
e.Graphics.DrawImage(this.From, new PointF(fromXPosition, 0));
e.Graphics.DrawImage(this.To, new PointF(fromXPosition - (this.From.Width * this.Direction), 0));
}
}
public enum AnimationDirection
{
Forward = -1,
Backward = 1
}
public class AnimationEventArgs : EventArgs
{
public AnimationEventArgs(AnimationDirection direction)
{
Direction = direction;
}
public AnimationDirection Direction { get; }
}
This class will only draw the images while the animation is active. Every other invalidation will not trigger the Control_Paint method.
Use following code for your Form:
public class Form1
{
private List<Image> imgList = new List<Image>();
private int currentIndex = 0;
private SlideAnimation animation;
public Slideshow()
{
InitializeComponent();
imgList.Add(Image.FromFile("pic1.bmp"));
imgList.Add(Image.FromFile("pic2.bmp"));
imgList.Add(Image.FromFile("pic3.bmp"));
imgList.Add(Image.FromFile("pic4.bmp"));
imgList.Add(Image.FromFile("pic5.bmp"));
animation = new SlideAnimation(this.Panel1);
animation.Speed = 20;
animation.AnimationFinished += AnimationFinished;
}
private void btnPrev_Click(object sender, EventArgs e)
{
if (currentIndex == 0)
return;
if (animation.IsRunning)
return;
animation.Direction = AnimationDirection.Backward;
animation.From = imgList[currentIndex];
animation.To = imgList[currentIndex - 1];
animation.StartAnimation();
}
private void btnNext_Click(object sender, EventArgs e)
{
if (currentIndex == imgList.Count - 1)
return;
if (animation.IsRunning)
return;
animation.Direction = AnimationDirection.Forward;
animation.From = imgList[currentIndex];
animation.To = imgList[currentIndex + 1];
animation.StartAnimation();
}
private void AnimationFinished(object sender, AnimationEventArgs e)
{
currentIndex = currentIndex - (1 * e.Direction);
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(imgList[currentIndex], 0, 0);
}
}
Since there are a lot of drawing operations you may use a panel which supports DoubleBuffer.
public class DoubleBufferedPanel : Panel
{
public DoubleBufferedPanel()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
}
Keep in mind that this example is very simple and far from "fancy".

Related

How to change the image in picturebox based on timer?

In my user control I'm trying to change the image in a PictureBox when the timer is stopped. But it works only for the first time. If I start the timer again the image is not updating. I used the below code to change the image.
public partial class CANMsgLEDControl : UserControl
{
#region Constants
private const int kLedOn = 2;
private const int kLedOff = 4;
#endregion
private bool _blinkEnabled = false;
private int _blinkRate = 500;
private GridStyleInfo _currentCell = null;
private System.Timers.Timer _timer = new System.Timers.Timer();
public CANMsgLEDControl()
{
}
public CANMsgLEDControl(GridStyleInfo cell)
{
InitializeComponent();
picBxLed.Image = imageList1.Images[kLedOff];
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_currentCell = cell;
if (_currentCell != null)
this.BackColor = _currentCell.BackColor;
Create Handle
if (!this.IsHandleCreated)
{
CreateHandle();
}
}
#region Properties
[Browsable(true)]
public bool BlinkEnabled
{
get { return _blinkEnabled; }
set { _blinkEnabled = value; }
}
[Browsable(true)]
public int BlinkRate
{
get { return _blinkRate; }
set
{
_blinkRate = value;
_timer.Interval = _blinkRate;
}
}
#endregion
public void ResetLedColor()
{
this.picBxLed.Image = imageList1.Images[kLedOff];
}
public void BlinkStart()
{
_currentCell.BeginUpdate();
picBxLed.Image = imageList1.Images[kLedOn];
_currentCell.EndUpdate();
_timer.Start();
}
#region Events
private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new ElapsedEventHandler(_timer_Elapsed), new object[] { sender, e });
}
else
{
if (this.BlinkEnabled)
{
_timer.Stop();
_currentCell.BeginUpdate();
picBxLed.Image = imageList1.Images[kLedOff];
_currentCell.EndUpdate();
}
}
}
#endregion
}
How to change the image when the timer is stopped? Can anyone suggest a way to overcome this issue?
Regards,
Arulpriya

How let WPF UI slide animation more physical?

If we want to slide the screen, we need some events.
Like Mouse Move Event, Mouse Down Event, Mouse UP event
But the problem is we only can do some animation only when the finger touch the screen, I mean: when It touch the screen move, it will move, when we release it, it will stop(or give it a position let it stop to there). But I want some animation like IPhone main screen do. If our finger slide more fast, the animation more fast (or the animation slide to more far place).
Just like now we use the Photoshop, when the picture zoom out to very large, when we move the hand more fast, it will move to very far.
Another example is it will slow down very slowly, not immediately.
Also it will know my finger sliding fast , or slow....then it will slide slow or fast...
I use a modified version of the the code found here in my own programs. Usage is simple as it is an attached behavior and can be applied to a style so that all of your scroll viewers automatically behave in this way. It works by using the same (tunneled) events you mention (OnPreviewMouseDown, OnPreviewMouseUp, and OnPreviewMouseMove). During the handling of OnPreviewMouseMove, inertia is calculated and the scrollbar is moved in a simulated physical manner. There is also a friction property which can be set to change the length of time a scrollbar "glides".
This is a quick example of a control I did for a test with inertia, using a ScrollViewer.
Hope this helps.
public partial class HomeFeed : BaseControl
{
public HomeFeed()
{
InitializeComponent();
}
private bool IsDragging
{
get { return _isDragging; }
set
{
var start = _isDragging && !value;
_isDragging = value;
if (start)
{
new Thread(x =>
{
var c = 0;
while (ApplyVelocity())
{
c++;
Thread.Sleep(15);
}
}).Start();
}
}
}
private Point _mousePosition;
private Velocity _velocity = new Velocity();
private bool _isDragging;
private void HomeScrollViewer_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
IsDragging = true;
_velocity.Reset();
_mousePosition = e.GetPosition(this);
e.Handled = true;
}
private void HomeScrollViewer_OnPreviewMouseMove(object sender, MouseEventArgs e)
{
if (!IsDragging) return;
var pos = e.GetPosition(this);
var y = pos.Y - _mousePosition.Y;
if (y == 0)
{
return;
}
_velocity.TryUpdate(y);
HomeScrollViewer.ScrollToVerticalOffset(HomeScrollViewer.VerticalOffset - y);
_mousePosition = pos;
}
private void HomeScrollViewer_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!IsDragging) return;
IsDragging = false;
e.Handled = true;
}
private void HomeScrollViewer_OnMouseLeave(object sender, MouseEventArgs e)
{
if (!IsDragging) return;
IsDragging = false;
e.Handled = true;
}
private bool ApplyVelocity()
{
if (IsDragging || _velocity.Value == 0)
{
return false;
}
Dispatcher.BeginInvoke(new Action(() => HomeScrollViewer.ScrollToVerticalOffset(HomeScrollViewer.VerticalOffset - _velocity.Value)));
var size = Math.Abs(_velocity.Value);
var sign = size / _velocity.Value;
_velocity.Value = sign * Math.Max(0, Math.Min(size*0.95, size - 1));
return true;
}
}
public class Velocity
{
private readonly int _timespan;
public double Value { get; set; }
public DateTime SetAt { get; set; }
public Velocity(int timespan = 1000)
{
_timespan = timespan;
Value = 0;
SetAt = DateTime.Now;
}
public void TryUpdate(double value)
{
if (value == 0)
{
return;
}
if (SetAt.Add(TimeSpan.FromMilliseconds(_timespan)) > DateTime.Now)
{
SetAt = DateTime.Now;
Value = value;
return;
}
if (value*Value < 0)
{
SetAt = DateTime.Now;
Value = value;
return;
}
if (Math.Abs(value) > Math.Abs(Value))
{
SetAt = DateTime.Now;
Value = value;
return;
}
}
public void Reset()
{
Value = 0;
SetAt = DateTime.Now;
}
}

Capture user click and typing in a custom WPF TextBox-like control?

Trying to make an equation editor like that in Microsoft Word in C# and WPF. XML cannot be used; it has to be purely programmatic.
Right now I have LineGUIObject : System.Windows.Controls.WrapPanel, which is like System.Windows.Controls.TextBox, except that instead of just showing strings it shows each element of a List<System.Windows.UIElement> in order.
Now I want for a user to be able to click on an instance of LineGUIObject and type into it. The holdup is that I don't know how to capture the user's click or read the input that they type. How can this be done?
Note: This question is not asking how to handle input once captured; just how to get the input in the first place. For example, is there some event that fires off after the user clicks it or something? I can't seem to find one for System.Windows.Controls.WrapPanel, which might imply that I need to use another type of object, or..?
Current code:
public class LineGUIObject
: System.Windows.Controls.WrapPanel
{
private List<System.Windows.UIElement> _uiElementList;
private CursorGUIObject _cursor;
private int? _cursorIndex;
public LineGUIObject(System.Windows.Threading.Dispatcher dispatcher)
: base()
{
this.UIElementList = new List<System.Windows.UIElement>();
this.Cursor = new CursorGUIObject(dispatcher, 25, 1.5, 250);
this.UIElementList.Add(this.Cursor);
this.AddText("[junk string just to see this otherwise invisible object while debugging]");
}
protected void InterpretUserKeyStroke(/* ??? */)
{
//How do we get this method to be called on user input,
//e.g. when the user types "1"?
throw new NotImplementedException();
}
protected void AddText(string text)
{
this.UIElementList.Add(new System.Windows.Controls.TextBlock(new System.Windows.Documents.Run(text)));
this.UpdateDisplay();
}
protected List<System.Windows.UIElement> UIElementList { get { return this._uiElementList; } private set { this._uiElementList = value; } }
protected CursorGUIObject Cursor { get { return this._cursor; } private set { this._cursor = value; } }
protected int? CursorIndex
{
get { return this._cursorIndex; }
set
{
int? nullablePriorIndex = this.CursorIndex;
if (nullablePriorIndex != null)
{
int priorIndex = nullablePriorIndex.Value;
this.UIElementList.RemoveAt(priorIndex);
}
if (value == null)
{
this._cursorIndex = null;
}
else
{
int newIndex = value.Value;
if (newIndex < 0)
{
newIndex = 0;
}
else
{
int thisListCount = this.UIElementList.Count;
if (newIndex > thisListCount) { newIndex = thisListCount; }
}
this.UIElementList.Insert(newIndex, this.Cursor);
this._cursorIndex = newIndex;
}
this.UpdateDisplay();
}
}
protected void UpdateDisplay()
{
this.Children.Clear();
foreach (System.Windows.UIElement uiElement in this.UIElementList) { this.Children.Add(uiElement); }
}
}
public class CursorGUIObject
: System.Windows.Controls.WrapPanel
{
public const double MINIMUM_BLINK_TIME_IN_MS = 5;
public const double MINIMUM_HEIGHT = 0.5;
public const double MINIMUM_WIDTH = 0.5;
private object ToggleVisibilityLock = new object();
private delegate void TimerIntervalDelegate();
private System.Windows.Shapes.Rectangle _rectangle;
private System.Timers.Timer _timer;
private System.Windows.Threading.Dispatcher _dispatcher;
public CursorGUIObject(System.Windows.Threading.Dispatcher dispatcher, double height, double width, double blinkTimeInMS)
{
this.Dispatcher = dispatcher;
System.Windows.Shapes.Rectangle rectangle = new System.Windows.Shapes.Rectangle();
rectangle.Width = width > MINIMUM_WIDTH ? width : MINIMUM_WIDTH;
rectangle.Height = height > MINIMUM_HEIGHT ? height : MINIMUM_HEIGHT;
rectangle.Fill = System.Windows.Media.Brushes.Black;
this.Rectangle = rectangle;
this.Children.Add(rectangle);
System.Timers.Timer timer = new System.Timers.Timer(blinkTimeInMS > MINIMUM_BLINK_TIME_IN_MS ? blinkTimeInMS : MINIMUM_BLINK_TIME_IN_MS);
this.Timer = timer;
timer.Elapsed += timer_Elapsed;
timer.Start();
}
~CursorGUIObject()
{
System.Timers.Timer timer = this.Timer;
if (timer != null) { timer.Dispose(); }
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Delegate timerDelegate = new TimerIntervalDelegate(ToggleVisibility);
this.Dispatcher.BeginInvoke(timerDelegate);
}
protected void ToggleVisibility()
{
lock (ToggleVisibilityLock)
{
if (this.Rectangle.Visibility.Equals(System.Windows.Visibility.Hidden))
{
this.Rectangle.Visibility = System.Windows.Visibility.Visible;
}
else
{
this.Rectangle.Visibility = System.Windows.Visibility.Hidden;
}
}
}
protected System.Windows.Shapes.Rectangle Rectangle { get { return this._rectangle; } private set { this._rectangle = value; } }
protected System.Timers.Timer Timer { get { return this._timer; } private set { this._timer = value; } }
protected System.Windows.Threading.Dispatcher Dispatcher { get { return this._dispatcher; } private set { this._dispatcher = value; } }
}
Pretty much all WPF controls provide access to the UIElement.PreviewMouseDown Event, which you can use to monitor mouse clicks. So, this event lets you monitor when each object is clicked on. Next, I'd advise you to use a small Popup control to popup a TextBox that the user could enter a value with:
<Popup Name="Popup">
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Padding="5">
<TextBox Text="{Binding InputText}" />
</Border>
</Popup>
Depending on how you have set up your project, you could open the Popup from the event handler:
private void YourObject_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Popup.IsOpen = true;
}
Turns out that LineGUIObject just needed to have this.Focusable = true; set in its constructor so that it could receive the keyboard's focus when clicked.
Now that it can be focused on, this.KeyUp += LineGUIObject_KeyUp; also in the constructor, and
protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
this.AddText(e.Key.ToString());
}
Even this had a problem at first since my LineGUIObject was nested in a ScrollViewer which kept stealing focus immediately after the LineGUIObject would receive it. This was fixed by making the ScrollViewer to be unable to get focus, i.e. <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Focusable="False"/>.

Mouse down between two pieces of a puzzle

I am building a puzzle game in winforms, and i want to recognize the mousedown over any piece, and move it with the mousemove.
The issue is that when i touch the transparent part of the puzzle piece, i want to verify if there is any piece behind that one, and if so, start the mousemove of that other piece, got it?
I am also able to recognize if the mousedown was over the image, or if it happens in the transparent part of the puzzle piece. My problem is to get the best way to pass the mouse event to the piece behind.
Many Thanks in advance.
UPDATE 1
The following is the class for the puzzle piece:
class Peça : DrawingArea
{
private Point _Offset = Point.Empty;
public Image imagem
{
get;
set;
}
protected override void OnDraw()
{
Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
this.graphics.DrawImage(imagem, location);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_Offset != Point.Empty)
{
Point newlocation = this.Location;
newlocation.X += e.X - _Offset.X;
newlocation.Y += e.Y - _Offset.Y;
this.Location = newlocation;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
_Offset = Point.Empty;
}
protected override void OnMouseDown(MouseEventArgs e)
{
Down(e);
//Console.WriteLine(color.ToString());
}
public void Down(MouseEventArgs e)
{
Bitmap b = new Bitmap(imagem);
Color? color = null;
try
{
color = b.GetPixel(e.X, e.Y);
if (color.Value.A != 0 && color != null)
{
if (e.Button == MouseButtons.Left)
{
_Offset = new Point(e.X, e.Y);
this.BringToFront();
}
}
}
catch {
}
}
}
The following code, is my DrawingArea (Panel) that i create in order to work with transparency:
abstract public class DrawingArea : Panel
{
protected Graphics graphics;
abstract protected void OnDraw();
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
public DrawingArea()
{
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
protected override void OnPaint(PaintEventArgs e)
{
this.graphics = e.Graphics;
this.graphics.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.AntiAlias;
this.graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
this.graphics.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
this.graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.HighQuality;
OnDraw();
}
}
And you can also see my Form code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
}
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000;
return handleParam;
}
}
}
Those are my pieces, and when i touch the transparent space in the first piece, i want to pick up the second one and move it on mouseMouse instead of doing nothing...
It looks like this:
Apologize my bad english.
UPDATE 2
I think i am getting very close to the solution, but something strange happens now, when i touch the piece behind another one, it disappear...
What am i doing wrong?
SOME CODE UPDATES
Piece Class:
class Peça : DrawingArea
{
private Point _Offset = Point.Empty;
public Boolean movable = false;
public Image imagem
{
get;
set;
}
protected override void OnDraw()
{
Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
this.graphics.DrawImage(imagem, location);
}
public void Move(MouseEventArgs e)
{
if (_Offset != Point.Empty)
{
Point newlocation = this.Location;
newlocation.X += e.X - _Offset.X;
newlocation.Y += e.Y - _Offset.Y;
this.Location = newlocation;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
_Offset = Point.Empty;
movable = false;
}
protected override void OnMouseDown(MouseEventArgs e)
{
Down(e);
//Console.WriteLine(color.ToString());
}
public Boolean Down(MouseEventArgs e, bool propaga=true)
{
Form parentForm = (this.Parent as Form);
Bitmap b = new Bitmap(imagem);
Color? color = null;
Boolean flag = false;
try
{
color = b.GetPixel(e.X, e.Y);
if (color.Value.A != 0 && color != null)
{
if (e.Button == MouseButtons.Left)
{
_Offset = new Point(e.X, e.Y);
this.BringToFront();
flag = true;
movable = true;
}
}
else
{
if(propaga)
(this.Parent as Form1).propagaEvento(this, e);
flag = false;
}
return flag;
}
catch {
return flag; }
}
}
Form1:
public partial class Form1 : Form
{
private List<Peça> peças;
private Point _Offset = Point.Empty;
public Form1()
{
InitializeComponent();
peças = new List<Peça>();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
criaListaPecas();
associaEventosPecas();
}
private void associaEventosPecas()
{
foreach (Peça p in peças)
{
p.MouseMove += Form1_MouseMove;
}
}
private void criaListaPecas()
{
peças.Clear();
foreach (Control p in this.Controls)
{
if (p.GetType() == typeof(Peça))
peças.Add((Peça)p);
}
Console.WriteLine(peças[0].Name);
Console.WriteLine(peças[1].Name);
Console.WriteLine(peças[2].Name);
}
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000;
return handleParam;
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
label1.Text = e.Location.ToString();
gereMovimento(e);
}
private void gereMovimento(MouseEventArgs e)
{
foreach (Peça p in peças)
{
if (p.movable)
{
p.Move(e);
}
}
}
internal void propagaEvento(Peça peça, MouseEventArgs e)
{
foreach (Peça p in peças)
{
if (p != peça)
{
if (p.Down(e, false))
break;
}
}
}
}
Thanks in advance again :)
Pieces can be represent as:
public class Piece
{
public Point Location {get; set;}
public int Z {get; set;}
public int ID {get; set;} // to be bound to control or a control itself?
public Image Image {get; set;} // texture?
public DockStyle PlusArea {get; set;}
public DockStyle MinusArea {get; set;} // can be None
...
public bool HitTest(Point point)
{
// assuming all of same size
if((new Rectangle(Location, new Size(...)).Contains(point))
{
switch(MinusArea)
{
case Top:
if((new Rectangle(...)).Contains(point))
return false;
...
}
}
switch(MinusArea)
{
case Top:
if((new Rectangle(...)).Contains(point))
return true;
...
}
return false;
}
Then puzzle is
public class Puzzle
{
public List<Piece> Pieces {get; set;}
public void Draw(Graphics graphics)
{
// draw all pictures with respect to z order
}
public Piece HitTest(Point point)
{
... // hittest all pieces, return highest z-order or null
}
}
It is not a complete solution, but should give you idea.
Basically:
In mouse event you call Figure.HitTest() to get figure to start moving (that's what you need).
You draw everything into owner drawn control by calling Figure.Draw().
Obviously, drag-n-drop operations are calling Invalidate().
You may have special flag to indicate figure being dragged and draw it differently (with shadows, a bit offsetted to simulate it's pulled over other pieces, etc).
Each figure is represented as rectangle and PlusArea or MinusArea (don't know how to call them better, it's this extra or missing area of piece connectors), this is simplification, you can improve it.
In general, keep a list of all your puzzle piece controls, sorted top down. When you get a mouse down event on one piece, check the transparency at that point, if it it not transparent handle the event on that piece. If it is transparent forward the event to the next piece down in your list (directly calling the event handler is probably the easiest way). Keep doing this until you either find a non transparent point, or run out of pieces.
UPDATE
Here is a link to a project showing an example of how to do this in pure GDI.
https://drive.google.com/file/d/0B42fIyGTLNv3WlJwNGVRN2txTGs/edit?usp=sharing
SOLVED :)
i have figured it out...
Here's the code to anybody how needs
(I have made it right now, so the code is not clean yet):
Piece Class:
class Peça : DrawingArea
{
private Point _Offset = Point.Empty;
public Boolean movable = false;
public Image imagem
{
get;
set;
}
protected override void OnDraw()
{
Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
this.graphics.DrawImage(imagem, location);
}
public Boolean Down(Point e, bool propaga = true)
{
Bitmap b = new Bitmap(imagem);
Color? color = null;
Boolean flag = false;
try
{
color = b.GetPixel(e.X, e.Y);
if (color.Value.A != 0 && color != null)
{
flag = true;
}
else
{
flag = false;
}
return flag;
}
catch
{
return flag;
}
}
}
Form1:
public partial class Form1 : Form
{
private List<Peça> peças;
private Point _Offset = Point.Empty;
private Peça peça1, peça2, peça3, peça4;
private bool canMove;
private Peça atual;
private bool other=false;
public Form1()
{
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
InitializeComponent();
atual = new Peça();
peça1 = new Peça();
peça2 = new Peça();
peça3 = new Peça();
peça4 = new Peça();
peça1.imagem = Properties.Resources._4p1_1;
peça2.imagem = Properties.Resources._4p1_2;
peça3.imagem = Properties.Resources._4p1_3;
peça4.imagem = Properties.Resources._4p1_4;
peças = new List<Peça>();
peça1.Name = "peça1";
peça2.Name = "peça2";
peça3.Name = "peça3";
peça4.Name = "peça4";
this.Controls.Add(peça1);
this.Controls.Add(peça2);
this.Controls.Add(peça3);
this.Controls.Add(peça4);
criaListaPecas();
foreach (Peça p in peças)
{
p.Size = new Size(p.imagem.Width, p.imagem.Height);
}
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
associaEventosPecas();
canMove = false;
}
private void associaEventosPecas()
{
foreach (Peça p in peças)
{
p.MouseMove += Form1_MouseMove;
p.MouseDown += Form1_MouseDown;
p.MouseUp += Form1_MouseUp;
}
}
private void criaListaPecas()
{
peças.Clear();
foreach (Control p in this.Controls)
{
if (p.GetType() == typeof(Peça))
peças.Add((Peça)p);
}
Console.WriteLine(peças[0].Name);
Console.WriteLine(peças[1].Name);
Console.WriteLine(peças[2].Name);
Console.WriteLine(peças[3].Name);
}
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000;
return handleParam;
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (sender.GetType().Equals(typeof(Peça)))
{
label1.Text = new Point(e.Location.X + (sender as Peça).Location.X, e.Location.Y + (sender as Peça).Location.Y).ToString();
}
else
label1.Text = e.Location.ToString();
gereMovimento(sender, e);
}
private void gereMovimento(object sender, MouseEventArgs e)
{
if (canMove)
{
if (other)
{
Point p = atual.PointToClient(new Point(e.X + (sender as Peça).Location.X, e.Y + (sender as Peça).Location.Y));
Point newlocation = atual.Location;
newlocation.X += p.X - _Offset.X;
newlocation.Y += p.Y - _Offset.Y;
atual.Location = newlocation;
}
else
{
Point newlocation = atual.Location;
newlocation.X += e.X - _Offset.X;
newlocation.Y += e.Y - _Offset.Y;
atual.Location = newlocation;
}
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (sender.GetType().Equals(typeof(Peça)) && e.Button == MouseButtons.Left)
{
atual = sender as Peça;
atual.BringToFront();
criaListaPecas();
if (atual.Down(e.Location))
{
_Offset = new Point(e.X, e.Y);
canMove = true;
other = false;
}
else
{
Console.WriteLine(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)));
Console.WriteLine(atual.Location);
Point p = new Point();
if (peças[1].ClientRectangle.Contains(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
&& peças[1].Down(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
{
p = peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
atual = peças[1];
atual.BringToFront();
criaListaPecas();
_Offset = p;
canMove = true;
other = true;
}
else if (peças[2].ClientRectangle.Contains(peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
&& peças[2].Down(peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
{
p = peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
atual = peças[2];
atual.BringToFront();
criaListaPecas();
_Offset = p;
canMove = true;
other = true;
}
else if (peças[3].ClientRectangle.Contains(peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
&& peças[3].Down(peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
{
p = peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
atual = peças[3];
atual.BringToFront();
criaListaPecas();
_Offset = p;
canMove = true;
other = true;
}
}
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
canMove = false;
}
}
Apologize the repeated and confused code, but as i said, i have made it seconds ago, and did not clean the code yet ;)

Autoheight of vertical column header in C# using DevXpress

I used this http://www.devexpress.com/Support/Center/p/Q233111.aspx (the code is in VB so I converted it to C#) to get vertical column headers. I get the vertical headers but my problem is some of them doesn't fit, so they are not fully visible.
Is it possible to autoheight the column headers ? (all height set to max height)
As shown in Devexpress support center i think this will be the solution to your problem
First add an helper class to your solution
public class AutoHeightHelper
{
GridView view;
public AutoHeightHelper(GridView view)
{
this.view = view;
EnableColumnPanelAutoHeight();
}
public void EnableColumnPanelAutoHeight()
{
SetColumnPanelHeight();
SubscribeToEvents();
}
private void SubscribeToEvents()
{
view.ColumnWidthChanged += OnColumnWidthChanged;
view.GridControl.Resize += OnGridControlResize;
view.EndSorting += OnGridColumnEndSorting;
}
void OnGridColumnEndSorting(object sender, EventArgs e)
{
view.GridControl.BeginInvoke(new MethodInvoker(SetColumnPanelHeight));
}
void OnGridControlResize(object sender, EventArgs e)
{
SetColumnPanelHeight();
}
void OnColumnWidthChanged(object sender, DevExpress.XtraGrid.Views.Base.ColumnEventArgs e)
{
SetColumnPanelHeight();
}
private void SetColumnPanelHeight()
{
GridViewInfo viewInfo = view.GetViewInfo() as GridViewInfo;
int height = 0;
for (int i = 0; i < view.VisibleColumns.Count; i++)
height = Math.Max(GetColumnBestHeight(viewInfo, view.VisibleColumns[i]), height);
view.ColumnPanelRowHeight = height;
}
private int GetColumnBestHeight(GridViewInfo viewInfo, GridColumn column)
{
GridColumnInfoArgs ex = viewInfo.ColumnsInfo[column];
GraphicsInfo grInfo = new GraphicsInfo();
grInfo.AddGraphics(null);
ex.Cache = grInfo.Cache;
bool canDrawMore = true;
Size captionSize = CalcCaptionTextSize(grInfo.Cache, ex as HeaderObjectInfoArgs, column.GetCaption());
Size res = ex.InnerElements.CalcMinSize(grInfo.Graphics, ref canDrawMore);
res.Height = Math.Max(res.Height, captionSize.Height);
res.Width += captionSize.Width;
res = viewInfo.Painter.ElementsPainter.Column.CalcBoundsByClientRectangle(ex, new Rectangle(Point.Empty, res)).Size;
grInfo.ReleaseGraphics();
return res.Height;
}
Size CalcCaptionTextSize(GraphicsCache cache, HeaderObjectInfoArgs ee, string caption)
{
Size captionSize = ee.Appearance.CalcTextSize(cache, caption, ee.CaptionRect.Width).ToSize();
captionSize.Height++; captionSize.Width++;
return captionSize;
}
public void DisableColumnPanelAutoHeight()
{
UnsubscribeFromEvents();
}
private void UnsubscribeFromEvents()
{
view.ColumnWidthChanged -= OnColumnWidthChanged;
view.GridControl.Resize -= OnGridControlResize;
view.EndSorting -= OnGridColumnEndSorting;
}
}
Then on your form you should make the helper class to Handle GridView's columns resize events by adding the following lines of code
AutoHeightHelper helper;
private void OnFormLoad(object sender, EventArgs e)
{
helper = new AutoHeightHelper(gridView1);
helper.EnableColumnPanelAutoHeight();
}
private void OnFormClosing(object sender, FormClosingEventArgs e)
{
helper.DisableColumnPanelAutoHeight();
}
Hope this helps...

Categories