c# transparent spinning wheel (or gif) is just half transparent - c#

Well, first try was with a readily made spinning wheel (a gif). After that, I found a code that dynamically generates a wheel, but no way in both cases to have full transparency.
Depending on what I set as parent (form or panel) and the position on my wheel on form, the spin is just half transparent.
I use C# express (VS 2008).
All I want is a nice wheel in center of form, activated when BG is doing something, but full transparent so I can resize form.
Thanks,
Update:
I worked on some code that is draw a spinning wheel just fine. Almost working except OnPantBackground() which needs to be empty to paint behind, but in this case, a black rectangle is drawn because of ControlStyles.OptimizedDoubleBuffer enabled.
Any suggestions? Thank you.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Drawing2D;
namespace WinFormsControls
{
/// <summary>
/// A label that can be transparent.
/// </summary>
public class TransparentLabel : Control
{
// Constants =========================================================
private const double NumberOfDegreesInCircle = 360;
private const double NumberOfDegreesInHalfCircle = NumberOfDegreesInCircle / 2;
private const int DefaultInnerCircleRadius = 8;
private const int DefaultOuterCircleRadius = 10;
private const int DefaultNumberOfSpoke = 10;
private const int DefaultSpokeThickness = 4;
private readonly Color DefaultColor = Color.DarkGray;
private const int MacOSXInnerCircleRadius = 5;
private const int MacOSXOuterCircleRadius = 11;
private const int MacOSXNumberOfSpoke = 12;
private const int MacOSXSpokeThickness = 2;
private const int FireFoxInnerCircleRadius = 6;
private const int FireFoxOuterCircleRadius = 7;
private const int FireFoxNumberOfSpoke = 9;
private const int FireFoxSpokeThickness = 4;
private const int IE7InnerCircleRadius = 8;
private const int IE7OuterCircleRadius = 9;
private const int IE7NumberOfSpoke = 24;
private const int IE7SpokeThickness = 4;
// Enumeration =======================================================
public enum StylePresets
{
MacOSX,
Firefox,
IE7,
Custom
}
// Attributes ========================================================
private Timer m_Timer;
private bool m_IsTimerActive;
private int m_NumberOfSpoke;
private int m_SpokeThickness;
private int m_ProgressValue;
private int m_OuterCircleRadius;
private int m_InnerCircleRadius;
private PointF m_CenterPoint;
private Color m_Color;
private Color[] m_Colors;
private double[] m_Angles;
private StylePresets m_StylePreset;
// Properties ========================================================
/// <summary>
/// Gets or sets the lightest color of the circle.
/// </summary>
/// <value>The lightest color of the circle.</value>
[TypeConverter("System.Drawing.ColorConverter"),
Category("LoadingCircle"),
Description("Sets the color of spoke.")]
public Color Color
{
get
{
return m_Color;
}
set
{
m_Color = value;
GenerateColorsPallet();
Invalidate();
}
}
/// <summary>
/// Gets or sets the outer circle radius.
/// </summary>
/// <value>The outer circle radius.</value>
[System.ComponentModel.Description("Gets or sets the radius of outer circle."),
System.ComponentModel.Category("LoadingCircle")]
public int OuterCircleRadius
{
get
{
if (m_OuterCircleRadius == 0)
m_OuterCircleRadius = DefaultOuterCircleRadius;
return m_OuterCircleRadius;
}
set
{
m_OuterCircleRadius = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the inner circle radius.
/// </summary>
/// <value>The inner circle radius.</value>
[System.ComponentModel.Description("Gets or sets the radius of inner circle."),
System.ComponentModel.Category("LoadingCircle")]
public int InnerCircleRadius
{
get
{
if (m_InnerCircleRadius == 0)
m_InnerCircleRadius = DefaultInnerCircleRadius;
return m_InnerCircleRadius;
}
set
{
m_InnerCircleRadius = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the number of spoke.
/// </summary>
/// <value>The number of spoke.</value>
[System.ComponentModel.Description("Gets or sets the number of spoke."),
System.ComponentModel.Category("LoadingCircle")]
public int NumberSpoke
{
get
{
if (m_NumberOfSpoke == 0)
m_NumberOfSpoke = DefaultNumberOfSpoke;
return m_NumberOfSpoke;
}
set
{
if (m_NumberOfSpoke != value && m_NumberOfSpoke > 0)
{
m_NumberOfSpoke = value;
GenerateColorsPallet();
GetSpokesAngles();
Invalidate();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:LoadingCircle"/> is active.
/// </summary>
/// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
[System.ComponentModel.Description("Gets or sets the number of spoke."),
System.ComponentModel.Category("LoadingCircle")]
public bool Active
{
get
{
return m_IsTimerActive;
}
set
{
m_IsTimerActive = value;
ActiveTimer();
}
}
/// <summary>
/// Gets or sets the spoke thickness.
/// </summary>
/// <value>The spoke thickness.</value>
[System.ComponentModel.Description("Gets or sets the thickness of a spoke."),
System.ComponentModel.Category("LoadingCircle")]
public int SpokeThickness
{
get
{
if (m_SpokeThickness <= 0)
m_SpokeThickness = DefaultSpokeThickness;
return m_SpokeThickness;
}
set
{
m_SpokeThickness = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the rotation speed.
/// </summary>
/// <value>The rotation speed.</value>
[System.ComponentModel.Description("Gets or sets the rotation speed. Higher the slower."),
System.ComponentModel.Category("LoadingCircle")]
public int RotationSpeed
{
get
{
return m_Timer.Interval;
}
set
{
if (value > 0)
m_Timer.Interval = value;
}
}
/// <summary>
/// Quickly sets the style to one of these presets, or a custom style if desired
/// </summary>
/// <value>The style preset.</value>
[Category("LoadingCircle"),
Description("Quickly sets the style to one of these presets, or a custom style if desired"),
DefaultValue(typeof(StylePresets), "Custom")]
public StylePresets StylePreset
{
get { return m_StylePreset; }
set
{
m_StylePreset = value;
switch (m_StylePreset)
{
case StylePresets.MacOSX:
SetCircleAppearance(MacOSXNumberOfSpoke,
MacOSXSpokeThickness, MacOSXInnerCircleRadius,
MacOSXOuterCircleRadius);
break;
case StylePresets.Firefox:
SetCircleAppearance(FireFoxNumberOfSpoke,
FireFoxSpokeThickness, FireFoxInnerCircleRadius,
FireFoxOuterCircleRadius);
break;
case StylePresets.IE7:
SetCircleAppearance(IE7NumberOfSpoke,
IE7SpokeThickness, IE7InnerCircleRadius,
IE7OuterCircleRadius);
break;
case StylePresets.Custom:
SetCircleAppearance(DefaultNumberOfSpoke,
DefaultSpokeThickness,
DefaultInnerCircleRadius,
DefaultOuterCircleRadius);
break;
}
}
}
/// <summary>
/// Creates a new <see cref="TransparentLabel"/> instance.
/// </summary>
public TransparentLabel()
{
TabStop = false;
InitializeComponent();
SetStyle(ControlStyles.UserPaint, true);
//SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
m_Color = DefaultColor;
GenerateColorsPallet();
GetSpokesAngles();
GetControlCenterPoint();
m_Timer = new Timer();
m_Timer.Tick += new EventHandler(aTimer_Tick);
ActiveTimer();
this.Resize += new EventHandler(LoadingCircle_Resize);
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.ContainerControl |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.SupportsTransparentBackColor
, true);
}
// Events ============================================================
/// <summary>
/// Handles the Resize event of the LoadingCircle control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
void LoadingCircle_Resize(object sender, EventArgs e)
{
GetControlCenterPoint();
}
/// <summary>
/// Handles the Tick event of the aTimer control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
void aTimer_Tick(object sender, EventArgs e)
{
m_ProgressValue = ++m_ProgressValue % m_NumberOfSpoke;
Invalidate();
}
/// <summary>
/// Gets the creation parameters.
/// </summary>
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
// Overridden Methods ================================================
/// <summary>
/// Retrieves the size of a rectangular area into which a control can be fitted.
/// </summary>
/// <param name="proposedSize">The custom-sized area for a control.</param>
/// <returns>
/// An ordered pair of type <see cref="T:System.Drawing.Size"></see> representing the width and height of a rectangle.
/// </returns>
public override Size GetPreferredSize(Size proposedSize)
{
proposedSize.Width =
(m_OuterCircleRadius + m_SpokeThickness) * 2;
return proposedSize;
}
/// <summary>
/// Paints the background.
/// </summary>
/// <param name="e">E.</param>
///
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing
}
/// <summary>
/// Paints the control.
/// </summary>
/// <param name="e">E.</param>
protected override void OnPaint(PaintEventArgs e)
{
//DrawText();
if (m_NumberOfSpoke > 0)
{
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
int intPosition = m_ProgressValue;
for (int intCounter = 0; intCounter < m_NumberOfSpoke; intCounter++)
{
intPosition = intPosition % m_NumberOfSpoke;
DrawLine(e.Graphics,
GetCoordinate(m_CenterPoint, m_InnerCircleRadius, m_Angles[intPosition]),
GetCoordinate(m_CenterPoint, m_OuterCircleRadius, m_Angles[intPosition]),
m_Colors[intCounter], m_SpokeThickness);
intPosition++;
}
}
base.OnPaint(e);
}
/*
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x000F)
{
DrawText();
}
}
private void DrawText()
{
using (Graphics graphics = CreateGraphics())
using (SolidBrush brush = new SolidBrush(ForeColor))
{
SizeF size = graphics.MeasureString(Text, Font);
// first figure out the top
float top = 0;
switch (textAlign)
{
case ContentAlignment.MiddleLeft:
case ContentAlignment.MiddleCenter:
case ContentAlignment.MiddleRight:
top = (Height - size.Height) / 2;
break;
case ContentAlignment.BottomLeft:
case ContentAlignment.BottomCenter:
case ContentAlignment.BottomRight:
top = Height - size.Height;
break;
}
float left = -1;
switch (textAlign)
{
case ContentAlignment.TopLeft:
case ContentAlignment.MiddleLeft:
case ContentAlignment.BottomLeft:
if (RightToLeft == RightToLeft.Yes)
left = Width - size.Width;
else
left = -1;
break;
case ContentAlignment.TopCenter:
case ContentAlignment.MiddleCenter:
case ContentAlignment.BottomCenter:
left = (Width - size.Width) / 2;
break;
case ContentAlignment.TopRight:
case ContentAlignment.MiddleRight:
case ContentAlignment.BottomRight:
if (RightToLeft == RightToLeft.Yes)
left = -1;
else
left = Width - size.Width;
break;
}
graphics.DrawString(Text, Font, brush, left, top);
}
}
*/
/*
/// <summary>
/// Gets or sets the text associated with this control.
/// </summary>
/// <returns>
/// The text associated with this control.
/// </returns>
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
RecreateHandle();
}
}
/// <summary>
/// Gets or sets a value indicating whether control's elements are aligned to support locales using right-to-left fonts.
/// </summary>
/// <value></value>
/// <returns>
/// One of the <see cref="T:System.Windows.Forms.RightToLeft"/> values. The default is <see cref="F:System.Windows.Forms.RightToLeft.Inherit"/>.
/// </returns>
/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">
/// The assigned value is not one of the <see cref="T:System.Windows.Forms.RightToLeft"/> values.
/// </exception>
public override RightToLeft RightToLeft
{
get
{
return base.RightToLeft;
}
set
{
base.RightToLeft = value;
RecreateHandle();
}
}
/// <summary>
/// Gets or sets the font of the text displayed by the control.
/// </summary>
/// <value></value>
/// <returns>
/// The <see cref="T:System.Drawing.Font"/> to apply to the text displayed by the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultFont"/> property.
/// </returns>
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
RecreateHandle();
}
}
private ContentAlignment textAlign = ContentAlignment.TopLeft;
/// <summary>
/// Gets or sets the text alignment.
/// </summary>
public ContentAlignment TextAlign
{
get { return textAlign; }
set
{
textAlign = value;
RecreateHandle();
}
}
*/
private void InitializeComponent()
{
this.SuspendLayout();
this.ResumeLayout(false);
}
// Methods ===========================================================
/// <summary>
/// Darkens a specified color.
/// </summary>
/// <param name="_objColor">Color to darken.</param>
/// <param name="_intPercent">The percent of darken.</param>
/// <returns>The new color generated.</returns>
private Color Darken(Color _objColor, int _intPercent)
{
int intRed = _objColor.R;
int intGreen = _objColor.G;
int intBlue = _objColor.B;
return Color.FromArgb(_intPercent, Math.Min(intRed, byte.MaxValue), Math.Min(intGreen, byte.MaxValue), Math.Min(intBlue, byte.MaxValue));
}
/// <summary>
/// Generates the colors pallet.
/// </summary>
private void GenerateColorsPallet()
{
m_Colors = GenerateColorsPallet(m_Color, Active, m_NumberOfSpoke);
}
/// <summary>
/// Generates the colors pallet.
/// </summary>
/// <param name="_objColor">Color of the lightest spoke.</param>
/// <param name="_blnShadeColor">if set to <c>true</c> the color will be shaded on X spoke.</param>
/// <returns>An array of color used to draw the circle.</returns>
private Color[] GenerateColorsPallet(Color _objColor, bool _blnShadeColor, int _intNbSpoke)
{
Color[] objColors = new Color[NumberSpoke];
// Value is used to simulate a gradient feel... For each spoke, the
// color will be darken by value in intIncrement.
byte bytIncrement = (byte)(byte.MaxValue / NumberSpoke);
//Reset variable in case of multiple passes
byte PERCENTAGE_OF_DARKEN = 0;
for (int intCursor = 0; intCursor < NumberSpoke; intCursor++)
{
if (_blnShadeColor)
{
if (intCursor == 0 || intCursor < NumberSpoke - _intNbSpoke)
objColors[intCursor] = _objColor;
else
{
// Increment alpha channel color
PERCENTAGE_OF_DARKEN += bytIncrement;
// Ensure that we don't exceed the maximum alpha
// channel value (255)
if (PERCENTAGE_OF_DARKEN > byte.MaxValue)
PERCENTAGE_OF_DARKEN = byte.MaxValue;
// Determine the spoke forecolor
objColors[intCursor] = Darken(_objColor, PERCENTAGE_OF_DARKEN);
}
}
else
objColors[intCursor] = _objColor;
}
return objColors;
}
/// <summary>
/// Gets the control center point.
/// </summary>
private void GetControlCenterPoint()
{
m_CenterPoint = GetControlCenterPoint(this);
}
/// <summary>
/// Gets the control center point.
/// </summary>
/// <returns>PointF object</returns>
private PointF GetControlCenterPoint(Control _objControl)
{
return new PointF(_objControl.Width / 2, _objControl.Height / 2 - 1);
}
/// <summary>
/// Draws the line with GDI+.
/// </summary>
/// <param name="_objGraphics">The Graphics object.</param>
/// <param name="_objPointOne">The point one.</param>
/// <param name="_objPointTwo">The point two.</param>
/// <param name="_objColor">Color of the spoke.</param>
/// <param name="_intLineThickness">The thickness of spoke.</param>
private void DrawLine(Graphics _objGraphics, PointF _objPointOne, PointF _objPointTwo,
Color _objColor, int _intLineThickness)
{
using (Pen objPen = new Pen(new SolidBrush(_objColor), _intLineThickness))
{
objPen.StartCap = LineCap.Round;
objPen.EndCap = LineCap.Round;
_objGraphics.DrawLine(objPen, _objPointOne, _objPointTwo);
}
}
/// <summary>
/// Gets the coordinate.
/// </summary>
/// <param name="_objCircleCenter">The Circle center.</param>
/// <param name="_intRadius">The radius.</param>
/// <param name="_dblAngle">The angle.</param>
/// <returns></returns>
private PointF GetCoordinate(PointF _objCircleCenter, int _intRadius, double _dblAngle)
{
double dblAngle = Math.PI * _dblAngle / NumberOfDegreesInHalfCircle;
return new PointF(_objCircleCenter.X + _intRadius * (float)Math.Cos(dblAngle),
_objCircleCenter.Y + _intRadius * (float)Math.Sin(dblAngle));
}
/// <summary>
/// Gets the spokes angles.
/// </summary>
private void GetSpokesAngles()
{
m_Angles = GetSpokesAngles(NumberSpoke);
}
/// <summary>
/// Gets the spoke angles.
/// </summary>
/// <param name="_shtNumberSpoke">The number spoke.</param>
/// <returns>An array of angle.</returns>
private double[] GetSpokesAngles(int _intNumberSpoke)
{
double[] Angles = new double[_intNumberSpoke];
double dblAngle = (double)NumberOfDegreesInCircle / _intNumberSpoke;
for (int shtCounter = 0; shtCounter < _intNumberSpoke; shtCounter++)
Angles[shtCounter] = (shtCounter == 0 ? dblAngle : Angles[shtCounter - 1] + dblAngle);
return Angles;
}
/// <summary>
/// Actives the timer.
/// </summary>
private void ActiveTimer()
{
if (m_IsTimerActive)
{
m_Timer.Start();
}
else
{
m_Timer.Stop();
m_ProgressValue = 0;
}
GenerateColorsPallet();
Invalidate();
}
/// <summary>
/// Sets the circle appearance.
/// </summary>
/// <param name="numberSpoke">The number spoke.</param>
/// <param name="spokeThickness">The spoke thickness.</param>
/// <param name="innerCircleRadius">The inner circle radius.</param>
/// <param name="outerCircleRadius">The outer circle radius.</param>
public void SetCircleAppearance(int numberSpoke, int spokeThickness,
int innerCircleRadius, int outerCircleRadius)
{
NumberSpoke = numberSpoke;
SpokeThickness = spokeThickness;
InnerCircleRadius = innerCircleRadius;
OuterCircleRadius = outerCircleRadius;
Invalidate();
}
}
}

Sorry for answering my own post, but after testing a lot of projects, modify without success, I finally found one that is almost full transparent
https://www.codeproject.com/Articles/27185/WYSIWYG-Progress-Circle-for-NET-Framework-C
That guy made a very well job almost 10 years ago, at least inner part of spin is copied and re-drawn in OnPaint so quite nice transparent over RichTextBox control where all other projects failed :)
The only small problem is that the circle trail, don't copy the bmp behind so there will always be a comet effect, but I can live with that! Much pleasant effect than other solutions, including few on stackoverflow.
In case anyone will need!

Wow that seems incredibly complicated. I don't know much about wpf, but on iOS what we usually do is load an image that has the exact properties we need (here, transparent background), and just rotate the image to make the spinning effect.
You only need very little logic to make it appear/disappear and rotate. For a very basic spinner you get to create and use that class in a matter of seconds.
At least that's the route I'd go for, instead of writing 300 lines of code to manually handle the spinner :p

Related

touch scrolling for winforms in C#

Im trying to implement a panel containing a multitude of buttons and panels with a touch UI in C#. In particular I'm interested in the scrolling functionality. The application is supposed to run on a windows 10 tablet which offers this functionality partially (I.e. if you slide your fingers over the scroll panel's background the scrolling is performed. However, if the gesture starts on a child element of the panel it has no effect. Performing the same gestures with the mouse, no matter where, has no effect.) Unfortunately I don't have the possibility to switch from winforms to WPF application.
Right now, I have implemented the functionality using a transparent panel (overwriting its OnPaintBackground() method) which I put on top of the actual scroll panel. It takes the mouse down/move/up events and transforms them into scrolling actions and forwards the clicks to the child controls of the scroll panel.
This solution works ok but is very slow (lagging). I wonder if there is a fix to it or some other, better solution.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ETNA
{
public class TransparentPanel : Panel
{
bool mouseDown = false;
bool isScrolling = false;
FlowLayoutPanel flap;
Point mouseDownPos = new Point();
int curserYPosBeforeScroll;
Point initScrollPos = new Point();
public TransparentPanel(FlowLayoutPanel flap)
{
this.flap = flap;
Location = flap.Location;
Size = flap.Size;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT
return cp;
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
mouseDown = true;
mouseDownPos.X = e.X;
mouseDownPos.Y = e.Y;
curserYPosBeforeScroll = Cursor.Position.Y;
initScrollPos = new Point(0, -flap.AutoScrollPosition.Y); // XXX unclear why negtion is necessary
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
isScrolling = false;
mouseDown = false;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (mouseDown)
{
isScrolling = true;
int initMousePos = curserYPosBeforeScroll;
int currMousePos = Cursor.Position.Y;
int autoScrollPos = initScrollPos.Y + initMousePos - currMousePos;
autoScrollPos = clamp(autoScrollPos, 0, flap.VerticalScroll.Maximum);
flap.AutoScrollPosition = new Point(initScrollPos.X, autoScrollPos);
flap.Refresh();
Refresh();
}
}
private static int clamp(int value, int min, int max)
{
return value < min ? min : value > max ? max : value;
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
Point clickPos = new Point(e.X, e.Y);
if (!isScrolling && mouseDownPos.Equals(clickPos))
{
foreach (Control c in flap.Controls)
{
if (c.Bounds.Contains(mouseDownPos))
{
if (c.GetType().Equals(typeof(UserButton)))
{
((Button)c).PerformClick();
}
else if (c.GetType().Equals(typeof(ProductPanel)))
{
((ProductPanel)c).virtualClick(this, e);
}
}
}
}
}
// skipping the paint Background method makes the panel transparent
protected override void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e);
}
}
}
This is not really an answer, but it's too long for a comment.
I was faced with a similar problem and managed to convince my boss to go to wpf after a lot of attempts to manage panning in a winforms application.
But - in my application the scrolling container is also transparent, so the whole thing looked quite terrible.
However, My solution to handle panning was a different solution then yours, so you might be able to benefit from my experience.
I've found this gem online, that enabled me to process panning gestures in my c# code, it might also help you.
I actually have the same issue
i know i'm pretty late but you could have solved your first problem by bubbling the event
foreach(var ctrl in panel.controls){ ctrl.MouseDown += Panel_mouseDown;...etc}
then they will all have the same scroll gesture functionality
the problem with this tho is that it causes some stuttering and glitching that i have no idea where it comes from 😂
Since this question apeared on top, I'll post my solution. I hope it'll help to somebody.
First a MouseFilter that filters input messages from hardware (works same for mouse and touch gestures):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
namespace MediTab
{
class MouseFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_MOUSEMOVE = 0x0200;
/// <summary>
/// Filtert eine Meldung, bevor sie gesendet wird.
/// </summary>
/// <param name="m">Die zu sendende Meldung. Diese Meldung kann nicht geändert werden.</param>
/// <returns>
/// true, um die Meldung zu filtern und das Senden zu verhindern. false, um das Senden der Meldung bis zum nächsten Filter oder Steuerelement zu ermöglichen.
/// </returns>
public bool PreFilterMessage(ref Message m)
{
Point mousePosition = Control.MousePosition;
var args = new MouseFilterEventArgs(MouseButtons.Left, 0, mousePosition.X, mousePosition.Y, 0);
switch (m.Msg)
{
case WM_MOUSEMOVE:
if (MouseFilterMove != null)
{
MouseFilterMove(Control.FromHandle(m.HWnd), args);
}
break;
case WM_LBUTTONDOWN:
if (MouseFilterDown != null)
{
MouseFilterDown(Control.FromHandle(m.HWnd), args);
}
break;
case WM_LBUTTONUP:
if (MouseFilterUp != null)
{
MouseFilterUp(Control.FromHandle(m.HWnd), args);
}
break;
}
// Always allow message to continue to the next filter control
return args.Handled;
}
/// <summary>
/// Occurs when [mouse filter up].
/// </summary>
public event MouseFilterEventHandler MouseFilterUp;
/// <summary>
/// Occurs when [mouse filter down].
/// </summary>
public event MouseFilterEventHandler MouseFilterDown;
/// <summary>
/// Occurs when [mouse filter move].
/// </summary>
public event MouseFilterMoveEventHandler MouseFilterMove;
}
internal delegate void MouseFilterEventHandler(object sender, MouseFilterEventArgs args);
internal delegate void MouseFilterMoveEventHandler(object sender, MouseFilterEventArgs args);
internal class MouseFilterEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="MouseFilterEventArgs" /> class.
/// </summary>
/// <param name="mouseButton">The mouse button.</param>
/// <param name="clicks">The clicks.</param>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="delta">The delta.</param>
public MouseFilterEventArgs(MouseButtons mouseButton, int clicks, int x, int y, int delta)
{
Button = mouseButton;
Clicks = clicks;
X = x;
Y = y;
Delta = delta;
Handled = false;
}
/// <summary>
/// Gets or sets the button.
/// </summary>
/// <value>
/// The button.
/// </value>
public MouseButtons Button { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MouseFilterEventArgs" /> is handled.
/// </summary>
/// <value>
/// <c>true</c> if handled; otherwise, <c>false</c>.
/// </value>
public bool Handled { get; set; }
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>
/// The X.
/// </value>
public int X { get; set; }
/// <summary>
/// Gets or sets the Y.
/// </summary>
/// <value>
/// The Y.
/// </value>
public int Y { get; set; }
/// <summary>
/// Gets or sets the clicks.
/// </summary>
/// <value>
/// The clicks.
/// </value>
public int Clicks { get; set; }
/// <summary>
/// Gets or sets the delta.
/// </summary>
/// <value>
/// The delta.
/// </value>
public int Delta { get; set; }
}
}
In your control add change of scroll position to the MouseFilter events.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
namespace MediTab
{
public partial class TouchableFlowLayoutPanel : FlowLayoutPanel
{
private bool _doTouchScroll;
private Point _mouseStartPoint = Point.Empty;
private Point _PanelStartPoint = Point.Empty;
public TouchableFlowLayoutPanel()
{
InitializeComponent();
Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
}
public TouchableFlowLayoutPanel(IContainer container)
{
container.Add(this);
InitializeComponent();
Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
}
/// <summary>
/// Handles the MouseFilterDown event of the mudmFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterDown(object sender, MouseFilterEventArgs e)
{
if (!_doTouchScroll && e.Button == MouseButtons.Left)
{
_mouseStartPoint = new Point(e.X, e.Y);
_PanelStartPoint = new Point(-this.AutoScrollPosition.X,
-this.AutoScrollPosition.Y);
}
}
/// <summary>
/// Handles the MouseFilterMove event of the mudmFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (!_mouseStartPoint.Equals(Point.Empty))
{
int dx = (e.X - _mouseStartPoint.X);
int dy = (e.Y - _mouseStartPoint.Y);
if (_doTouchScroll)
{
this.AutoScrollPosition = new Point(_PanelStartPoint.X - dx,
_PanelStartPoint.Y - dy);
}
else if (Math.Abs(dx) > 10 || Math.Abs(dy) > 10)
{
_doTouchScroll = true;
}
}
}
}
/// <summary>
/// Handles the MouseFilterUp event of the mudmFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterUp(object sender, MouseFilterEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (_doTouchScroll && !this.AutoScrollPosition.Equals(_PanelStartPoint) &&
!_PanelStartPoint.Equals(Point.Empty))
{
// dont fire Click-Event
e.Handled = true;
}
_doTouchScroll = false;
_mouseStartPoint = Point.Empty;
_PanelStartPoint = Point.Empty;
}
}
}
}
However, if you have a choice, go for WPF. All controls for touch devices are already there and you'll save yourself a lot of problems.

Hub Tiles (In App Tiles) Windows Phone 8.1 (Universal Apps)

Basic Requirement - Displaying In App tiles in Universal Apps.
I have gone through samples of Hub Tiles from the following link Hub Tiles which applies to Windows Phone 7 and 8, 8.1 (Silverlight), but there is no mentioning of windows Phone 8.1 (Universal Applications). Even if i include the assembly separately it throws an error that Microsoft.Phone cannot be resolved.
As a work around I have even downloaded the source of hub Tiles from Windows phone Toolkit Source and resolved the assemblies, modified the templates and edited it accordingly. But I might be missing on some points, Although It is showing as a control in the tool box but it is not even visible when I add it to XAML. I am adding the edited code for all the three files available from the source of the toolkit.
HubTiles.cs
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System.Windows;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace Microsoft.Phone.Controls
{
/// <summary>
/// Represents an animated tile that supports an image and a title.
/// Furthermore, it can also be associated with a message or a notification.
/// </summary>
/// <QualityBand>Preview</QualityBand>
[TemplateVisualState(Name = Expanded, GroupName = ImageStates)]
[TemplateVisualState(Name = Semiexpanded, GroupName = ImageStates)]
[TemplateVisualState(Name = Collapsed, GroupName = ImageStates)]
[TemplateVisualState(Name = Flipped, GroupName = ImageStates)]
[TemplatePart(Name = NotificationBlock, Type = typeof(TextBlock))]
[TemplatePart(Name = MessageBlock, Type = typeof(TextBlock))]
[TemplatePart(Name = BackTitleBlock, Type = typeof(TextBlock))]
[TemplatePart(Name = TitlePanel, Type = typeof(Panel))]
public class HubTile : Control
{
/// <summary>
/// Common visual states.
/// </summary>
private const string ImageStates = "ImageState";
/// <summary>
/// Expanded visual state.
/// </summary>
private const string Expanded = "Expanded";
/// <summary>
/// Semiexpanded visual state.
/// </summary>
private const string Semiexpanded = "Semiexpanded";
/// <summary>
/// Collapsed visual state.
/// </summary>
private const string Collapsed = "Collapsed";
/// <summary>
/// Flipped visual state.
/// </summary>
private const string Flipped = "Flipped";
/// <summary>
/// Nofitication Block template part name.
/// </summary>
private const string NotificationBlock = "NotificationBlock";
/// <summary>
/// Message Block template part name.
/// </summary>
private const string MessageBlock = "MessageBlock";
/// <summary>
/// Back Title Block template part name.
/// </summary>
private const string BackTitleBlock = "BackTitleBlock";
/// <summary>
/// Title Panel template part name.
/// </summary>
private const string TitlePanel = "TitlePanel";
/// <summary>
/// Notification Block template part.
/// </summary>
private TextBlock _notificationBlock;
/// <summary>
/// Message Block template part.
/// </summary>
private TextBlock _messageBlock;
/// <summary>
/// Title Panel template part.
/// </summary>
private Panel _titlePanel;
/// <summary>
/// Back Title Block template part.
/// </summary>
private TextBlock _backTitleBlock;
/// <summary>
/// Represents the number of steps inside the pipeline of stalled images
/// </summary>
internal int _stallingCounter;
/// <summary>
/// Flag that determines if the hub tile has a primary text string associated to it.
/// If it does not, the hub tile will not drop.
/// </summary>
internal bool _canDrop;
/// <summary>
/// Flag that determines if the hub tile has a secondary text string associated to it.
/// If it does not, the hub tile will not flip.
/// </summary>
internal bool _canFlip;
#region Source DependencyProperty
/// <summary>
/// Gets or sets the image source.
/// </summary>
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
/// <summary>
/// Identifies the Source dependency property.
/// </summary>
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ImageSource), typeof(HubTile), new PropertyMetadata(null));
#endregion
#region Title DependencyProperty
/// <summary>
/// Gets or sets the title.
/// </summary>
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// Identifies the Title dependency property.
/// </summary>
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(HubTile), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnTitleChanged)));
/// <summary>
/// Prevents the hub tile from transitioning into a Semiexpanded or Collapsed visual state if the title is not set.
/// </summary>
/// <param name="obj">The dependency object.</param>
/// <param name="e">The event information.</param>
private static void OnTitleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
HubTile tile = (HubTile)obj;
if (string.IsNullOrEmpty((string)e.NewValue))
{
tile._canDrop = false;
tile.State = ImageState.Expanded;
}
else
{
tile._canDrop = true;
}
}
#endregion
#region Notification DependencyProperty
/// <summary>
/// Gets or sets the notification alert.
/// </summary>
public string Notification
{
get { return (string)GetValue(NotificationProperty); }
set { SetValue(NotificationProperty, value); }
}
/// <summary>
/// Identifies the Notification dependency property.
/// </summary>
public static readonly DependencyProperty NotificationProperty =
DependencyProperty.Register("Notification", typeof(string), typeof(HubTile), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnBackContentChanged)));
/// <summary>
/// Prevents the hub tile from transitioning into a Flipped visual state if neither the notification alert nor the message are set.
/// </summary>
/// <param name="obj">The dependency object.</param>
/// <param name="e">The event information.</param>
private static void OnBackContentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
HubTile tile = (HubTile)obj;
// If there is a new notification or a message, the hub tile can flip.
if ((!(string.IsNullOrEmpty(tile.Notification)) && tile.DisplayNotification)
|| (!(string.IsNullOrEmpty(tile.Message)) && !tile.DisplayNotification))
{
tile._canFlip = true;
}
else
{
tile._canFlip = false;
tile.State = ImageState.Expanded;
}
}
#endregion
#region Message DependencyProperty
/// <summary>
/// Gets or sets the message.
/// </summary>
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
/// <summary>
/// Identifies the Message dependency property.
/// </summary>
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(HubTile), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnBackContentChanged)));
#endregion
#region DisplayNotification DependencyProperty
/// <summary>
/// Gets or sets the flag for new notifications.
/// </summary>
public bool DisplayNotification
{
get { return (bool)GetValue(DisplayNotificationProperty); }
set { SetValue(DisplayNotificationProperty, value); }
}
/// <summary>
/// Identifies the DisplayNotification dependency property.
/// </summary>
public static readonly DependencyProperty DisplayNotificationProperty =
DependencyProperty.Register("DisplayNotification", typeof(bool), typeof(HubTile), new PropertyMetadata(false, new PropertyChangedCallback(OnBackContentChanged)));
#endregion
#region IsFrozen DependencyProperty
/// <summary>
/// Gets or sets the flag for images that do not animate.
/// </summary>
public bool IsFrozen
{
get { return (bool)GetValue(IsFrozenProperty); }
set { SetValue(IsFrozenProperty, value); }
}
/// <summary>
/// Identifies the IsFrozen dependency property.
/// </summary>
public static readonly DependencyProperty IsFrozenProperty =
DependencyProperty.Register("IsFrozen", typeof(bool), typeof(HubTile), new PropertyMetadata(false, new PropertyChangedCallback(OnIsFrozenChanged)));
/// <summary>
/// Removes the frozen image from the enabled image pool or the stalled image pipeline.
/// Adds the non-frozen image to the enabled image pool.
/// </summary>
/// <param name="obj">The dependency object.</param>
/// <param name="e">The event information.</param>
private static void OnIsFrozenChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
HubTile tile = (HubTile)obj;
if ((bool)e.NewValue)
{
HubTileService.FreezeHubTile(tile);
}
else
{
HubTileService.UnfreezeHubTile(tile);
}
}
#endregion
#region GroupTag DependencyProperty
/// <summary>
/// Gets or sets the group tag.
/// </summary>
public string GroupTag
{
get { return (string)GetValue(GroupTagProperty); }
set { SetValue(GroupTagProperty, value); }
}
/// <summary>
/// Identifies the GroupTag dependency property.
/// </summary>
public static readonly DependencyProperty GroupTagProperty =
DependencyProperty.Register("GroupTag", typeof(string), typeof(HubTile), new PropertyMetadata(string.Empty));
#endregion
#region State DependencyProperty
/// <summary>
/// Gets or sets the visual state.
/// </summary>
internal ImageState State
{
get { return (ImageState)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
/// <summary>
/// Identifies the State dependency property.
/// </summary>
private static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(ImageState), typeof(HubTile), new PropertyMetadata(ImageState.Expanded, OnImageStateChanged));
/// <summary>
/// Triggers the transition between visual states.
/// </summary>
/// <param name="obj">The dependency object.</param>
/// <param name="e">The event information.</param>
private static void OnImageStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((HubTile)obj).UpdateVisualState();
}
#endregion
#region Size DependencyProperty
/// <summary>
/// Gets or sets the visual state.
/// </summary>
public TileSize Size
{
get { return (TileSize)GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
/// <summary>
/// Identifies the State dependency property.
/// </summary>
public static readonly DependencyProperty SizeProperty =
DependencyProperty.Register("Size", typeof(TileSize), typeof(HubTile), new PropertyMetadata(TileSize.Default, OnSizeChanged));
/// <summary>
/// Triggers the transition between visual states.
/// </summary>
/// <param name="obj">The dependency object.</param>
/// <param name="e">The event information.</param>
private static void OnSizeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
HubTile hubTile = (HubTile)obj;
// And now we'll update the width and height to match the new size.
switch (hubTile.Size)
{
case TileSize.Default:
hubTile.Width = 173;
hubTile.Height = 173;
break;
case TileSize.Small:
hubTile.Width = 99;
hubTile.Height = 99;
break;
case TileSize.Medium:
hubTile.Width = 210;
hubTile.Height = 210;
break;
case TileSize.Large:
hubTile.Width = 432;
hubTile.Height = 210;
break;
}
hubTile.SizeChanged += OnHubTileSizeChanged;
HubTileService.FinalizeReference(hubTile);
}
static void OnHubTileSizeChanged(object sender, SizeChangedEventArgs e)
{
HubTile hubTile = (HubTile)sender;
hubTile.SizeChanged -= OnHubTileSizeChanged;
// In order to avoid getting into a bad state, we'll shift the HubTile
// back to the Expanded state. If we were already in the Expanded state,
// then we'll manually shift the title panel to the right location,
// since the visual state manager won't do it for us in that case.
if (hubTile.State != ImageState.Expanded)
{
hubTile.State = ImageState.Expanded;
VisualStateManager.GoToState(hubTile, Expanded, false);
}
else if (hubTile._titlePanel != null)
{
CompositeTransform titlePanelTransform = hubTile._titlePanel.RenderTransform as CompositeTransform;
if (titlePanelTransform != null)
{
titlePanelTransform.TranslateY = -hubTile.Height;
}
}
HubTileService.InitializeReference(hubTile);
}
#endregion
/// <summary>
/// Updates the visual state.
/// </summary>
private void UpdateVisualState()
{
string state;
// If we're in the Small size, then we should just display the image
// instead of having animations.
if (Size != TileSize.Small)
{
switch (State)
{
case ImageState.Expanded:
state = Expanded;
break;
case ImageState.Semiexpanded:
state = Semiexpanded;
break;
case ImageState.Collapsed:
state = Collapsed;
break;
case ImageState.Flipped:
state = Flipped;
break;
default:
state = Expanded;
break;
}
}
else
{
state = Expanded;
}
VisualStateManager.GoToState(this, state, true);
}
/// <summary>
/// Gets the template parts and sets binding.
/// </summary>
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_notificationBlock = base.GetTemplateChild(NotificationBlock) as TextBlock;
_messageBlock = base.GetTemplateChild(MessageBlock) as TextBlock;
_backTitleBlock = base.GetTemplateChild(BackTitleBlock) as TextBlock;
_titlePanel = base.GetTemplateChild(TitlePanel) as Panel;
//Do binding in code to avoid exposing unnecessary value converters.
if (_notificationBlock != null)
{
Binding bindVisible = new Binding();
bindVisible.Source = this;
bindVisible.Path = new PropertyPath("DisplayNotification");
bindVisible.Converter = new VisibilityConverter();
bindVisible.ConverterParameter = false;
_notificationBlock.SetBinding(TextBlock.VisibilityProperty, bindVisible);
}
if(_messageBlock != null)
{
Binding bindCollapsed = new Binding();
bindCollapsed.Source = this;
bindCollapsed.Path = new PropertyPath("DisplayNotification");
bindCollapsed.Converter = new VisibilityConverter();
bindCollapsed.ConverterParameter = true;
_messageBlock.SetBinding(TextBlock.VisibilityProperty, bindCollapsed);
}
if(_backTitleBlock != null)
{
Binding bindTitle = new Binding();
bindTitle.Source = this;
bindTitle.Path = new PropertyPath("Title");
bindTitle.Converter = new MultipleToSingleLineStringConverter();
_backTitleBlock.SetBinding(TextBlock.TextProperty, bindTitle);
}
UpdateVisualState();
}
/// <summary>
/// Initializes a new instance of the HubTile class.
/// </summary>
public HubTile()
{
DefaultStyleKey = typeof(HubTile);
Loaded += HubTile_Loaded;
Unloaded += HubTile_Unloaded;
}
/// <summary>
/// This event handler gets called as soon as a hub tile is added to the visual tree.
/// A reference of this hub tile is passed on to the service singleton.
/// </summary>
/// <param name="sender">The hub tile.</param>
/// <param name="e">The event information.</param>
void HubTile_Loaded(object sender, RoutedEventArgs e)
{
HubTileService.InitializeReference(this);
}
/// <summary>
/// This event handler gets called as soon as a hub tile is removed from the visual tree.
/// Any existing reference of this hub tile is eliminated from the service singleton.
/// </summary>
/// <param name="sender">The hub tile.</param>
/// <param name="e">The event information.</param>
void HubTile_Unloaded(object sender, RoutedEventArgs e)
{
HubTileService.FinalizeReference(this);
}
}
/// <summary>
/// Represents the visual states of a Hub tile.
/// </summary>
internal enum ImageState
{
/// <summary>
/// Expanded visual state value.
/// </summary>
Expanded = 0,
/// <summary>
/// Semiexpanded visual state value.
/// </summary>
Semiexpanded = 1,
/// <summary>
/// Collapsed visual state value.
/// </summary>
Collapsed = 2,
/// <summary>
/// Flipped visual state value.
/// </summary>
Flipped = 3,
};
/// <summary>
/// Represents the size of a Hub tile.
/// </summary>
public enum TileSize
{
/// <summary>
/// Default size (173 px x 173 px).
/// </summary>
Default,
/// <summary>
/// Small size (99 px x 99 px).
/// </summary>
Small,
/// <summary>
/// Medium size (210 px x 210 px).
/// </summary>
Medium,
/// <summary>
/// Large size (432 px x 210 px).
/// </summary>
Large,
};
}
Note: We can edit the same for HubTileConverters and HubTileService but still it doesnt work.
If any one has a solution or has edited the same for Universal Application Or if there is any Toolkit from Microsoft for the same then please let me know.
TIA.
I have ported the toolkit HubTile stuff. Done with a sample for your refrence please find in the following link.
HubTile sample for universal app
Hope this will help you.
Maybe you should check the Generic.xaml file in toolkit source code, it is placed in Themes folder. All toolkit control default styles are stored in this file. It seems your problem is lack of this style file.
Because HubTile is a custom control, it will read the \Themes\Generic.xaml in solution by using DefaultStyleKey = typeof(HubTile) in its constructor. You should port this file to your solution.

how to get smartphone like scrolling for a winforms touchscreen app ( scrolling panel )

After scouring the articles online I have come up with this design for a winforms based touchscreen app that needs smartphone like scrolling. The app itself will run on a tablet laptop or touchscreen desktop.
I put everything I want to scroll on a panel.
Set autoscroll to true (which will show scrollbars)
Now put this whole panel inside a groupbox
Shrink the groupbox until the scrollbars are hidden (visually hidden, not visible = false)
Now the fun part I am stuck at. I think I have to handle the mousedown, mouseup & mousemove on the panel to set the autoscrollposition so that when someone touches the panel and drags, it does it's scroll magic. Please help fill in the few lines of code in below method stubs. The msdn doc on autoscrollposition is very confusing since it returns negative numbers but needs to be set to positive with abs and what not.
Point mouseDownPoint;
Point mouseUpPoint;
Point mouseDragPoint;
private void myPanel_MouseDown(object sender, MouseEventArgs e)
{
this.mouseDownPoint = e.Location;
Console.WriteLine("Mouse down at {0}", e.location);
}
private void myPanel_MouseUp(object sender, MouseEventArgs e)
{
this.mouseUpPoint = e.Location;
this.mouseDownPoint = new Point(); //will set for IsEmpty check
Console.WriteLine("Mouse Up at {0}", e.location);
}
private void myPanel_MouseMove(object sender, MouseEventArgs e)
{
Console.WriteLine("Mouse at {0}", e.location);
if (mouseDownPoint.IsEmpty()) //finger is off the touchscreen
return;
myPanel.Autocrollposition = ??
}
thank you
//UPDATE - Below I have with trial and error working & tested code. (not refactored). If someone has a more elegant solution please post.
Point mouseDownPoint;
private void innerpanel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
this.mouseDownPoint = e.Location;
}
private void innerpanel_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
if ((mouseDownPoint.X == e.Location.X) && (mouseDownPoint.Y == e.Location.Y))
return;
Point currAutoS = innerpanel.AutoScrollPosition;
if (mouseDownPoint.Y > e.Location.Y)
{
//finger slide UP
if (currAutoS.Y != 0)
currAutoS.Y = Math.Abs(currAutoS.Y) - 5;
}
else if (mouseDownPoint.Y < e.Location.Y)
{
//finger slide down
currAutoS.Y = Math.Abs(currAutoS.Y) + 5;
}
else
{
currAutoS.Y = Math.Abs(currAutoS.Y);
}
if (mouseDownPoint.X > e.Location.X)
{
//finger slide left
if (currAutoS.X != 0)
currAutoS.X = Math.Abs(currAutoS.X) - 5;
}
else if (mouseDownPoint.X < e.Location.X)
{
//finger slide right
currAutoS.X = Math.Abs(currAutoS.X) + 5;
}
else
{
currAutoS.X = Math.Abs(currAutoS.X);
}
innerpanel.AutoScrollPosition = currAutoS;
mouseDownPoint = e.Location; //IMPORTANT
}
And as a Component:
public partial class TouchableFlowLayoutPanel : FlowLayoutPanel
{
private bool _doTouchScroll;
private Point _mouseStartPoint = Point.Empty;
private Point _panelStartPoint = Point.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="TouchableFlowLayoutPanel" /> class.
/// </summary>
public TouchableFlowLayoutPanel()
{
InitializeComponent();
Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
}
/// <summary>
/// Initializes a new instance of the <see cref="TouchableFlowLayoutPanel" /> class.
/// </summary>
/// <param name="container">The container.</param>
public TouchableFlowLayoutPanel(IContainer container)
{
container.Add(this);
InitializeComponent();
Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
}
/// <summary>
/// Handles the MouseFilterDown event of the mouseFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterDown(object sender, MouseFilterEventArgs e)
{
if (!_doTouchScroll && e.Button == MouseButtons.Left)
{
_mouseStartPoint = new Point(e.X, e.Y);
_panelStartPoint = new Point(-AutoScrollPosition.X,
-AutoScrollPosition.Y);
}
}
/// <summary>
/// Handles the MouseFilterMove event of the mouseFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (!_mouseStartPoint.Equals(Point.Empty))
{
int dx = (e.X - _mouseStartPoint.X);
int dy = (e.Y - _mouseStartPoint.Y);
if (_doTouchScroll)
{
AutoScrollPosition = new Point(_panelStartPoint.X - dx,
_panelStartPoint.Y - dy);
}
else if (Math.Abs(dx) > 10 || Math.Abs(dy) > 10)
{
_doTouchScroll = true;
}
}
}
}
/// <summary>
/// Handles the MouseFilterUp event of the mouseFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterUp(object sender, MouseFilterEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (_doTouchScroll && !AutoScrollPosition.Equals(_panelStartPoint) &&
!_panelStartPoint.Equals(Point.Empty))
{
// don't fire Click-Event
e.Handled = true;
}
_doTouchScroll = false;
_mouseStartPoint = Point.Empty;
_panelStartPoint = Point.Empty;
}
}
}
internal class MouseFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_MOUSEMOVE = 0x0200;
/// <summary>
/// Filters a message before sending it
/// </summary>
/// <param name="m">The message to be sent.This message can not be changed.</param>
/// <returns>
/// true to filter the message and prevent it from being sent. false to allow the message to be sent to the next filter or control.
/// </returns>
public bool PreFilterMessage(ref Message m)
{
Point mousePosition = Control.MousePosition;
var args = new MouseFilterEventArgs(MouseButtons.Left, 0, mousePosition.X, mousePosition.Y, 0);
switch (m.Msg)
{
case WM_MOUSEMOVE:
if (MouseFilterMove != null)
{
MouseFilterMove(Control.FromHandle(m.HWnd), args);
}
break;
case WM_LBUTTONDOWN:
if (MouseFilterDown != null)
{
MouseFilterDown(Control.FromHandle(m.HWnd), args);
}
break;
case WM_LBUTTONUP:
if (MouseFilterUp != null)
{
MouseFilterUp(Control.FromHandle(m.HWnd), args);
}
break;
}
// Always allow message to continue to the next filter control
return args.Handled;
}
/// <summary>
/// Occurs when [mouse filter up].
/// </summary>
public event MouseFilterEventHandler MouseFilterUp;
/// <summary>
/// Occurs when [mouse filter down].
/// </summary>
public event MouseFilterEventHandler MouseFilterDown;
/// <summary>
/// Occurs when [mouse filter move].
/// </summary>
public event MouseFilterMoveEventHandler MouseFilterMove;
}
internal delegate void MouseFilterEventHandler(object sender, MouseFilterEventArgs args);
internal delegate void MouseFilterMoveEventHandler(object sender, MouseFilterEventArgs args);
internal class MouseFilterEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="MouseFilterEventArgs" /> class.
/// </summary>
/// <param name="mouseButton">The mouse button.</param>
/// <param name="clicks">The clicks.</param>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="delta">The delta.</param>
public MouseFilterEventArgs(MouseButtons mouseButton, int clicks, int x, int y, int delta)
{
Button = mouseButton;
Clicks = clicks;
X = x;
Y = y;
Delta = delta;
Handled = false;
}
/// <summary>
/// Gets or sets the button.
/// </summary>
/// <value>
/// The button.
/// </value>
public MouseButtons Button { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MouseFilterEventArgs" /> is handled.
/// </summary>
/// <value>
/// <c>true</c> if handled; otherwise, <c>false</c>.
/// </value>
public bool Handled { get; set; }
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>
/// The X.
/// </value>
public int X { get; set; }
/// <summary>
/// Gets or sets the Y.
/// </summary>
/// <value>
/// The Y.
/// </value>
public int Y { get; set; }
/// <summary>
/// Gets or sets the clicks.
/// </summary>
/// <value>
/// The clicks.
/// </value>
public int Clicks { get; set; }
/// <summary>
/// Gets or sets the delta.
/// </summary>
/// <value>
/// The delta.
/// </value>
public int Delta { get; set; }
}
static class Program
{
public static MouseFilter mouseFilter = new MouseFilter();
/// <summary>
/// Der Haupteinstiegspunkt für die Anwendung.
/// </summary>
[STAThread]
static void Main()
{
Application.AddMessageFilter(mouseFilter);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
7 Years later but for anyone that is looking for a neat and tidy winforms solution:
using System;
using System.Drawing;
using System.Windows.Forms;
/// <summary>
/// Pass the panel into constructor and the control will be turned into a touch scrollable control.
/// </summary>
public class TouchScroll
{
private Point mouseDownPoint;
private Panel parentPanel;
/// <summary>
/// pass in the panel you would like to be touch scrollable and it will be handled here.
/// </summary>
/// <param name="panel">The root panel you need to scroll with</param>
public TouchScroll(Panel panel)
{
parentPanel = panel;
AssignEvents(panel);
}
private void AssignEvents(Control control)
{
control.MouseDown += MouseDown;
control.MouseMove += MouseMove;
foreach (Control child in control.Controls)
AssignEvents(child);
}
private void MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
this.mouseDownPoint = Cursor.Position;
}
private void MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
Point pointDifference = new Point(Cursor.Position.X - mouseDownPoint.X, Cursor.Position.Y - mouseDownPoint.Y);
if ((mouseDownPoint.X == Cursor.Position.X) && (mouseDownPoint.Y == Cursor.Position.Y))
return;
Point currAutoS = parentPanel.AutoScrollPosition;
parentPanel.AutoScrollPosition = new Point(Math.Abs(currAutoS.X) - pointDifference.X, Math.Abs(currAutoS.Y) - pointDifference.Y);
mouseDownPoint = Cursor.Position; //IMPORTANT
}
}
}
This´s my way using IMessageFilter. For everyone who´s looking for a solutiion.
First you have to implement a Filter that will listen for all Application Events:
internal class MouseFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_MOUSEMOVE = 0x0200;
/// <summary>
/// Filtert eine Meldung, bevor sie gesendet wird.
/// </summary>
/// <param name="m">Die zu sendende Meldung. Diese Meldung kann nicht geändert werden.</param>
/// <returns>
/// true, um die Meldung zu filtern und das Senden zu verhindern. false, um das Senden der Meldung bis zum nächsten Filter oder Steuerelement zu ermöglichen.
/// </returns>
public bool PreFilterMessage(ref Message m)
{
Point mousePosition = Control.MousePosition;
var args = new MouseFilterEventArgs(MouseButtons.Left, 0, mousePosition.X, mousePosition.Y, 0);
switch (m.Msg)
{
case WM_MOUSEMOVE:
if (MouseFilterMove != null)
{
MouseFilterMove(Control.FromHandle(m.HWnd), args);
}
break;
case WM_LBUTTONDOWN:
if (MouseFilterDown != null)
{
MouseFilterDown(Control.FromHandle(m.HWnd), args);
}
break;
case WM_LBUTTONUP:
if (MouseFilterUp != null)
{
MouseFilterUp(Control.FromHandle(m.HWnd), args);
}
break;
}
// Always allow message to continue to the next filter control
return args.Handled;
}
/// <summary>
/// Occurs when [mouse filter up].
/// </summary>
public event MouseFilterEventHandler MouseFilterUp;
/// <summary>
/// Occurs when [mouse filter down].
/// </summary>
public event MouseFilterEventHandler MouseFilterDown;
/// <summary>
/// Occurs when [mouse filter move].
/// </summary>
public event MouseFilterMoveEventHandler MouseFilterMove;
}
internal delegate void MouseFilterEventHandler(object sender, MouseFilterEventArgs args);
internal delegate void MouseFilterMoveEventHandler(object sender, MouseFilterEventArgs args);
internal class MouseFilterEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="MouseFilterEventArgs" /> class.
/// </summary>
/// <param name="mouseButton">The mouse button.</param>
/// <param name="clicks">The clicks.</param>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="delta">The delta.</param>
public MouseFilterEventArgs(MouseButtons mouseButton, int clicks, int x, int y, int delta)
{
Button = mouseButton;
Clicks = clicks;
X = x;
Y = y;
Delta = delta;
Handled = false;
}
/// <summary>
/// Gets or sets the button.
/// </summary>
/// <value>
/// The button.
/// </value>
public MouseButtons Button { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MouseFilterEventArgs" /> is handled.
/// </summary>
/// <value>
/// <c>true</c> if handled; otherwise, <c>false</c>.
/// </value>
public bool Handled { get; set; }
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>
/// The X.
/// </value>
public int X { get; set; }
/// <summary>
/// Gets or sets the Y.
/// </summary>
/// <value>
/// The Y.
/// </value>
public int Y { get; set; }
/// <summary>
/// Gets or sets the clicks.
/// </summary>
/// <value>
/// The clicks.
/// </value>
public int Clicks { get; set; }
/// <summary>
/// Gets or sets the delta.
/// </summary>
/// <value>
/// The delta.
/// </value>
public int Delta { get; set; }
}
Then you have to register this Filter to you Program:
static class Program
{
public static MouseFilter mouseFilter = new MouseFilter();
/// <summary>
/// Der Haupteinstiegspunkt für die Anwendung.
/// </summary>
[STAThread]
static void Main()
{
Application.AddMessageFilter(mouseFilter);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
And now you can listen for global Mouse Events and scroll your scrollable Panel like this:
public partial class MainForm : Form
{
private bool _doTouchScroll;
private Point _mouseStartPoint = Point.Empty;
private Point _yourScrollablePanelStartPoint = Point.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="MainForm" /> class.
/// </summary>
public MainForm()
{
InitializeComponent();
Program.mouseFilter.MouseFilterDown += mouseFilter_MouseFilterDown;
Program.mouseFilter.MouseFilterMove += mouseFilter_MouseFilterMove;
Program.mouseFilter.MouseFilterUp += mouseFilter_MouseFilterUp;
}
/// <summary>
/// Handles the MouseFilterDown event of the mudmFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterDown(object sender, MouseFilterEventArgs e)
{
if (!_doTouchScroll && e.Button == MouseButtons.Left)
{
_mouseStartPoint = new Point(e.X, e.Y);
_yourScrollablePanelStartPoint = new Point(-yourScrollablePanel.AutoScrollPosition.X,
-yourScrollablePanel.AutoScrollPosition.Y);
}
}
/// <summary>
/// Handles the MouseFilterMove event of the mudmFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (!_mouseStartPoint.Equals(Point.Empty))
{
int dx = (e.X - _mouseStartPoint.X);
int dy = (e.Y - _mouseStartPoint.Y);
if (_doTouchScroll)
{
yourScrollablePanel.AutoScrollPosition = new Point(_yourScrollablePanelStartPoint.X - dx,
_yourScrollablePanelStartPoint.Y - dy);
}
else if (Math.Abs(dx) > 10 || Math.Abs(dy) > 10)
{
_doTouchScroll = true;
}
}
}
}
/// <summary>
/// Handles the MouseFilterUp event of the mudmFilter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">
/// The <see cref="MouseFilterEventArgs" /> instance containing the event data.
/// </param>
private void mouseFilter_MouseFilterUp(object sender, MouseFilterEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (_doTouchScroll && !yourScrollablePanel.AutoScrollPosition.Equals(_yourScrollablePanelStartPoint) &&
!_yourScrollablePanelStartPoint.Equals(Point.Empty))
{
// dont fire Click-Event
e.Handled = true;
}
_doTouchScroll = false;
_mouseStartPoint = Point.Empty;
_yourScrollablePanelStartPoint = Point.Empty;
}
}
}
I used the code the OP posted but found it doesnt work if there are things with in the panel like labels. To make this work more smoothly I changed .
e.Location
to
PointToClient(Cursor.Position)
and then had all objects inside the panel also call the mousedown and mousemove events. This way not matter where you click it should move.
To the original post, considering Cheap Funeral answer.
This version is more simple and more fluid to the touch (considers the finger move speed but not considering the effect of still scrolling after removing the finger, like moving but speeding down(maybe i'll do that whem i have time))
Works for a panel or FlowLayoutPanel
Create a form and insert a FlowLayoutPanel on it.
Form code:
Public Class Form1
Private Function GenerateButton(pName As String) As Button
Dim mResult As New Button
With mResult
.Name = pName
.Text = pName
.Width = FlowPanel.Width
.Height = 100
.Margin = New Padding(0)
.Padding = New Padding(0)
.BackColor = Color.CornflowerBlue
AddHandler .MouseDown, AddressOf Button_MouseDown
AddHandler .MouseMove, AddressOf Button_MouseMove
End With
Return mResult
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FlowPanel.Padding = New Padding(0)
FlowPanel.Margin = New Padding(0)
Dim i As Integer
For i = 1 To 100
FlowPanel.Controls.Add(GenerateButton("btn" & i.ToString))
Next
End Sub
Dim myMouseDownPoint As Point
Dim myCurrAutoSMouseDown As Point
Private Sub Button_MouseDown(sender As Object, e As MouseEventArgs) Handles FlowPanel.MouseDown
myMouseDownPoint = PointToClient(Cursor.Position)
myCurrAutoSMouseDown = FlowPanel.AutoScrollPosition
End Sub
Private Sub Button_MouseMove(sender As Object, e As MouseEventArgs) Handles FlowPanel.MouseMove
If e.Button = Windows.Forms.MouseButtons.Left Then
Dim mLocation As Point = PointToClient(Cursor.Position)
If myMouseDownPoint <> mLocation Then
Dim mCurrAutoS As Point
Dim mDeslocation As Point = myMouseDownPoint - mLocation
mCurrAutoS.X = Math.Abs(myCurrAutoSMouseDown.X) + mDeslocation.X
mCurrAutoS.Y = Math.Abs(myCurrAutoSMouseDown.Y) + mDeslocation.Y
FlowPanel.AutoScrollPosition = mCurrAutoS
End If
End If
End Sub
TIP:
To hide the scrolls of the flowlayoutpanel(or panel), create a usercontrol that inherits from flowlayoutpanel(or panel) and:
Imports System.Runtime.InteropServices
Public Class FlowLayoutPanelExt
Inherits FlowLayoutPanel
<DllImport("user32.dll")> _
Private Shared Function ShowScrollBar(hWnd As IntPtr, wBar As Integer, bShow As Boolean) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Enum ScrollBarDirection
SB_HORZ = 0
SB_VERT = 1
SB_CTL = 2
SB_BOTH = 3
End Enum
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If Me.Visible Then
ShowScrollBar(Me.Handle, CInt(ScrollBarDirection.SB_BOTH), False)
MyBase.WndProc(m)
End If
End Sub
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.AutoScroll = True
End Sub
End Class
This solution seems to be the best one out there and the most commonly accepted one - however, if you scroll to the bottom and touch a the actual flowcontrol behind the buttons (I tried to make this so that there would be empty space), you then have to double tap-and-hold the button for the scrolling to resume. Restarting the application restores the phone-like scrolling functionality. I am wondering if anyone else has seen this or figured it out - try it with your apps and see if it is the case as well. I modified the snippet above so that you can start a new project, copy and paste this into form1's code, and hit run.
Public Class Form1
Dim FlowPanel As New FlowLayoutPanel
Private Function GenerateButton(ByVal pName As String) As Button
Dim mResult As New Button
With mResult
.Name = pName
.Text = pName
.Width = 128
.Height = 128
.Margin = New Padding(0)
.Padding = New Padding(0)
.BackColor = Color.CornflowerBlue
AddHandler .MouseDown, AddressOf Button_MouseDown
AddHandler .MouseMove, AddressOf Button_MouseMove
End With
Return mResult
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
Me.Width = 806
Me.Height = 480
FlowPanel.Padding = New Padding(0)
FlowPanel.Margin = New Padding(0)
' FlowPanel.ColumnCount = Me.Width / (128 + 6)
FlowPanel.Dock = DockStyle.Fill
FlowPanel.AutoScroll = True
Me.Controls.Add(FlowPanel)
Dim i As Integer
For i = 1 To 98
FlowPanel.Controls.Add(GenerateButton("btn" & i.ToString))
Next
End Sub
Dim myMouseDownPoint As Point
Dim myCurrAutoSMouseDown As Point
Private Sub Button_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
myMouseDownPoint = PointToClient(Cursor.Position)
myCurrAutoSMouseDown = FlowPanel.AutoScrollPosition
End Sub
Private Sub Button_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
If e.Button = Windows.Forms.MouseButtons.Left Then
Dim mLocation As Point = PointToClient(Cursor.Position)
If myMouseDownPoint <> mLocation Then
Dim mCurrAutoS As Point
Dim mDeslocation As Point = myMouseDownPoint - mLocation
mCurrAutoS.X = Math.Abs(myCurrAutoSMouseDown.X) + mDeslocation.X
mCurrAutoS.Y = Math.Abs(myCurrAutoSMouseDown.Y) + mDeslocation.Y
FlowPanel.AutoScrollPosition = mCurrAutoS
End If
End If
End Sub
End Class

Optimizing Bitmap Loading in .Net Compact Framework

I'm in need to load an bunch of bitmaps in my application. The problem is that the loading make the form very slow. My actual class is as follows:
public class ImagensDisponiveis
{
/// <summary>
/// List of ImagemSygic struct
/// </summary>
private List<ImagemSygic> _poolImagens;
/// <summary>
/// index of next avaiable image
/// </summary>
private int indiceProximoDisponivel;
/// <summary>
/// Path to image folder
/// </summary>
private string caminhoPasta;
/// <summary>
/// Number of found images that conforms to patterm
/// </summary>
private int MAXCOUNT;
public ImagensDisponiveis(string caminhoPastaRecursos)
{
indiceProximoDisponivel = 0;
caminhoPasta = caminhoPastaRecursos;
PreencherPool(out _poolImagens, caminhoPasta);
MAXCOUNT = _poolImagens.Count;
}
/// <summary>
/// Preenche a lista de imagens com uma estrutura que contém a imagem e o caminho dessa imagem para o Sygic
/// </summary>
/// <param name="_poolImagens">The _pool imagens.</param>
/// <param name="filepath">The filepath.</param>
private void PreencherPool(out List<ImagemSygic> _poolImagens, string filepath)
{
DateTime momentoInicio = DateTime.Now;
_poolImagens = new List<ImagemSygic>();
string[] imagens = Directory.GetFiles(filepath);
#if DEBUG
//int counter = 0;
//int numFiles = imagens.Length;
#endif
foreach (string caminhoImagem in imagens)
{
try
{
string filename = Path.GetFileName(caminhoImagem);
//original image to show on .net [POI]anything.bmp
//image that sygic tries to use on drive ?[POI]anything.bmp, where ? is an number between 1 to 6
bool valido = filename.StartsWith("[POI]", StringComparison.InvariantCulture);
//Log.writeToLog(caminhoImagem + " " + valido.ToString());
if (valido)
{
var streamImagem = File.Open(caminhoImagem, FileMode.Open, FileAccess.Read);
Bitmap temImagem = new Bitmap(streamImagem);
ImagemSygic tempImgSygic = new ImagemSygic();
tempImgSygic.CaminhoImagemSygic = caminhoImagem;
tempImgSygic.ImagemWindows = temImagem;
tempImgSygic.SygicImageID = -1;
_poolImagens.Add(tempImgSygic);
#if DEBUG
//counter++;
#endif
}
}
catch (ArgumentException aec)
{
Log.writeToLog("[EXCEPCAO ImagensDisp]: ArgumentException - " + aec.Message);
}
catch (UnauthorizedAccessException uae)
{
Log.writeToLog("[EXCEPCAO ImagensDisp]: UnauthorizedAccessException - " + uae.Message);
}
catch (Exception exc)
{
Log.writeToLog("[EXCEPCAO ImagensDisp]: Exception - " + exc.Message);
}
}
DateTime tempoFim = DateTime.Now;
TimeSpan duracao = tempoFim.Subtract(momentoInicio);
Log.writeToLog("[Criacao da pool] Demorou " + duracao.TotalSeconds.ToString());
}
/// <summary>
/// OObtains the next avaianle ImagemSygic if there is an avaiable
/// </summary>
/// <returns>ImagemSygic if possible, else null</returns>
public ImagemSygic ObterProximoDisponivel()
{
if (indiceProximoDisponivel > MAXCOUNT)
return null;
else
{
ImagemSygic imagemRetornar = _poolImagens[indiceProximoDisponivel];
indiceProximoDisponivel++;
return imagemRetornar;
}
}
public void ResetCounter()
{
indiceProximoDisponivel = 0;
}
}
/// <summary>
/// Class that contains the Bitmap preview and the original path to that image
/// </summary>
public class ImagemSygic
{
private volatile int _imageID;
/// <summary>
/// Gets or sets the imagem windows.
/// </summary>
/// <value>The imagem windows.</value>
public Bitmap ImagemWindows { get; set; }
/// <summary>
/// Gets or sets the caminho imagem sygic.
/// </summary>
/// <value>The caminho imagem sygic.</value>
public string CaminhoImagemSygic { get; set; }
/// <summary>
/// Gets or sets the sygic image ID.
/// </summary>
/// <value>The sygic image ID.</value>
public int SygicImageID
{
get
{
return this._imageID;
}
set
{
this._imageID = value;
}
}
}
/// <summary>
///
/// </summary>
public class POISygic
{
private volatile int _latitude;
private volatile int _longitude;
/// <summary>
/// Gets or sets the imagem.
/// </summary>
/// <value>The imagem.</value>
public ImagemSygic Imagem { get; set; }
/// <summary>
/// Gets or sets the latitude.
/// </summary>
/// <value>The latitude.</value>
public int Latitude { get { return this._latitude; } set { this._latitude = value; } }
/// <summary>
/// Gets or sets the longitude.
/// </summary>
/// <value>The longitude.</value>
public int Longitude { get { return this._longitude; } set { this._longitude = value; } }
/// <summary>
/// Gets or sets the descricao.
/// </summary>
/// <value>The descricao.</value>
public string Descricao { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is displayed now.
/// </summary>
/// <value>
/// <c>true</c> if this instance is displayed now; otherwise, <c>false</c>.
/// </value>
public bool isDisplayedNow { get; set; }
/// <summary>
/// Gets or sets the elem ID.
/// </summary>
/// <value>The elem ID.</value>
public int elemID { get; set; }
}
The objective of this class is to read an serie of bitmaps that have the [POI] prefix to allow .net to display an legend in an form control to the poi image appearing in an Sygic Drive window.
The question is how i can optimize this code to load the images faster. At the moment it loads 26 315k bitmaps images in 10s. But probably in the final solution we could have as many as +260 images, so that is the reason of the need of optimization.
The biggest potential improvement I see is that PreencherPool loads all images in a folder. Are you showing all images at once? If not, you could change that to lazy-load the images on demand, or at least load them in a background thread so the caller doesn't block while they all get loaded.
I'd also suggest that 300k seems a bit large for a "thumbnail" (which the code suggests they are). Are you sizing it to what you actually need for displ15?

Get aspect ratio of a monitor

I want to get aspect ratio of a monitor as two digits : width and height. For example 4 and 3, 5 and 4, 16 and 9.
I wrote some code for that task. Maybe it is any easier way to do that ? For example, some library function =\
/// <summary>
/// Aspect ratio.
/// </summary>
public struct AspectRatio
{
int _height;
/// <summary>
/// Height.
/// </summary>
public int Height
{
get
{
return _height;
}
}
int _width;
/// <summary>
/// Width.
/// </summary>
public int Width
{
get
{
return _width;
}
}
/// <summary>
/// Ctor.
/// </summary>
/// <param name="height">Height of aspect ratio.</param>
/// <param name="width">Width of aspect ratio.</param>
public AspectRatio(int height, int width)
{
_height = height;
_width = width;
}
}
public sealed class Aux
{
/// <summary>
/// Get aspect ratio.
/// </summary>
/// <returns>Aspect ratio.</returns>
public static AspectRatio GetAspectRatio()
{
int deskHeight = Screen.PrimaryScreen.Bounds.Height;
int deskWidth = Screen.PrimaryScreen.Bounds.Width;
int gcd = GCD(deskWidth, deskHeight);
return new AspectRatio(deskHeight / gcd, deskWidth / gcd);
}
/// <summary>
/// Greatest Common Denominator (GCD). Euclidean algorithm.
/// </summary>
/// <param name="a">Width.</param>
/// <param name="b">Height.</param>
/// <returns>GCD.</returns>
static int GCD(int a, int b)
{
return b == 0 ? a : GCD(b, a % b);
}
}
Use Screen class to get the height/width.
Divide to get the GCD
Calculate the ratio.
See following code:
private void button1_Click(object sender, EventArgs e)
{
int nGCD = GetGreatestCommonDivisor(Screen.PrimaryScreen.Bounds.Height, Screen.PrimaryScreen.Bounds.Width);
string str = string.Format("{0}:{1}", Screen.PrimaryScreen.Bounds.Height / nGCD, Screen.PrimaryScreen.Bounds.Width / nGCD);
MessageBox.Show(str);
}
static int GetGreatestCommonDivisor(int a, int b)
{
return b == 0 ? a : GetGreatestCommonDivisor(b, a % b);
}
I don't think there's a library function to do it, but that code looks good. Very similar to the answer in this related post of doing the same thing in Javascript: Javascript Aspect Ratio

Categories