Xamarin Slider OnDraw is called only once - c#

I am inheriting from Xamarin..Android.SliderRenderer to create a circular slider. I created a View class and assembled them already:
[assembly: ExportRendererAttribute(typeof(CircleSliderView), typeof(CircleSliderViewRenderer))]
public class CircleSliderViewRenderer : SliderRenderer
I carefully avoid the SetWillNotDraw trap in constructor:
this.SetWillNotDraw(false);
Finally, override the OnDraw method to draw something else than the original one:
protected override void OnDraw(Canvas canvas)
{
...
DrawTrack(canvas, view.MaximumTrackColor.ToAndroid(), maxSweep);
DrawTrack(canvas, view.MinimumTrackColor.ToAndroid(), minSweep);
// no call to super.OnDraw()
}
As a result, I have an arc drawn:
However, I have 2 issues here:
Old slider drawing is still there.
My OnDraw is called once and never again (even after value change).
Apparently, there is another method than OnDraw that is being called continually (susceptibly when the value changes). But the API docs do not help at all on finding this method. Does anyone know which methods I should override to solve above issues?
Thank you in advance.

You could custom a circle slider which Inherited the View,then use ViewRenderer instead of SliderRenderer.
Custom CircularSlider in Android Project:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Android.Content;
using Android.Graphics;
using Android.Util;
using Android.Views;
using Java.Lang;
using Math = Java.Lang.Math;
namespace EntryCa.Droid
{
public class CircularSlider : View, INotifyPropertyChanged
{
int _arcRadius;
float _progressSweep;
RectF _arcRect = new RectF();
Paint _arcPaint;
Paint _progressPaint;
int _translateX;
int _translateY;
int _thumbXPos;
int _thumbYPos;
float _touchInsideIgnoreRadius;
float _touchOutsideIgnoreRadius;
int _touchCorrection = 40;
/// <summary>
/// This indicates how many points a touch event can go inside or outside the circle before the slider stops updating.
/// This may never exceed half of the controls size (this can create unwanted behaviour).
/// The value must be greater or equal to 0.
/// </summary>
public int TouchCorrection
{
get
{
return _touchCorrection;
}
set
{
if (_touchCorrection < 0)
throw new ArgumentOutOfRangeException(nameof(TouchCorrection), "The value must be at least 0");
_touchCorrection = value;
OnPropertyChanged();
}
}
int _sweepAngle = 180;
/// <summary>
/// This indicates how many degrees the circle is used.
/// The value must be between 0 and 360
/// </summary>
public int SweepAngle
{
get { return _sweepAngle; }
set
{
if (value < 0 || value > 360)
throw new ArgumentOutOfRangeException(nameof(SweepAngle), "The value must be between 0 and 360");
_sweepAngle = value;
if (Width > 0 && Height > 0)
{
CalculateArcRect(Width, Height);
UpdateProgress();
}
Invalidate();
OnPropertyChanged();
}
}
int _startAngle = 180;
/// <summary>
/// This indicates at how many degrees in the circle the indicator will start.
/// The value must be between 0 and 360.
/// </summary>
public int StartAngle
{
get { return _startAngle; }
set
{
if (value < 0 || value > 360)
throw new ArgumentOutOfRangeException(nameof(StartAngle), "The value must be between 0 and 360");
_startAngle = value;
if (Width > 0 && Height > 0)
{
CalculateArcRect(Width, Height);
UpdateProgress();
}
Invalidate();
OnPropertyChanged();
}
}
/// <summary>
/// The color of the uncompleted progress indicator.
/// </summary>
public Color Color
{
get
{
return _arcPaint.Color;
}
set
{
_arcPaint.Color = value;
Invalidate();
OnPropertyChanged();
}
}
/// <summary>
/// The color of the completed progress indicator.
/// </summary>
public Color ProgressColor
{
get
{
return _progressPaint.Color;
}
set
{
_progressPaint.Color = value;
Invalidate();
OnPropertyChanged();
}
}
Bitmap _thumb;
/// <summary>
/// The thumb image to indicate the current progress.
/// The bitmap must have the same width and height.
/// </summary>
public Bitmap Thumb
{
get
{
return _thumb;
}
set
{
if (value != null && value.Width != value.Height)
throw new ArgumentException("The image must be a square (same width and height)", nameof(Thumb));
_thumb = value;
if (Width > 0 && Height > 0)
{
CalculateArcRect(Width, Height);
UpdateProgress();
}
OnPropertyChanged();
}
}
/// <summary>
/// The line width in pixels of the circle.
/// </summary>
public int LineWidth
{
get
{
return (int)_arcPaint.StrokeWidth;
}
set
{
_arcPaint.StrokeWidth = value;
_progressPaint.StrokeWidth = value;
if (Width > 0 && Height > 0)
{
CalculateArcRect(Width, Height);
UpdateThumbPosition();
}
Invalidate();
OnPropertyChanged();
}
}
/// <summary>
/// This indicates if the circle line has rounded corners at the end of the line.
/// </summary>
public bool RoundEdges
{
get
{
return _arcPaint.StrokeCap == Paint.Cap.Round;
}
set
{
_arcPaint.StrokeCap = value ? Paint.Cap.Round : Paint.Cap.Square;
_progressPaint.StrokeCap = value ? Paint.Cap.Round : Paint.Cap.Square;
Invalidate();
OnPropertyChanged();
}
}
int _maximum;
/// <summary>
/// The maximum value of the progress.
/// </summary>
public int Maximum
{
get
{
return _maximum;
}
set
{
if (value < 0)
throw new IllegalStateException("Maximum can not be less than 0");
if (value < Progress)
throw new IllegalStateException("Maximum can not be less than Progress value" + Progress);
if (value != _maximum)
{
_maximum = value;
UpdateProgress();
OnPropertyChanged();
}
}
}
int _progress;
/// <summary>
/// The current progress.
/// </summary>
public int Progress
{
get
{
return _progress;
}
set
{
if (value < 0)
throw new IllegalStateException("Progress can not be less than 0");
if (value > Maximum)
throw new IllegalStateException("Progress can not be more than Maximum value " + Maximum);
if (value != _progress)
{
_progress = value;
UpdateProgress();
ProgressChanged?.Invoke(this, value);
OnPropertyChanged();
}
}
}
bool _clockwise = true;
/// <summary>
/// This indicates if the slider should work clockwise or counter clockwise.
/// </summary>
public bool Clockwise
{
get
{
return _clockwise;
}
set
{
_clockwise = value;
Invalidate();
OnPropertyChanged();
}
}
/// <summary>
/// This indicates if the user can interact with the control or not
/// </summary>
public override bool Enabled
{
get
{
return base.Enabled;
}
set
{
base.Enabled = value;
OnPropertyChanged();
}
}
/// <summary>
/// Triggered when one of the custom properties is changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Triggered when the progress value has changed.
/// </summary>
public event EventHandler<int> ProgressChanged;
public CircularSlider(Context context)
: base(context)
{
Init();
}
public CircularSlider(Context context, IAttributeSet attrs)
: base(context, attrs)
{
Init();
}
public CircularSlider(Context context, IAttributeSet attrs, int defStyle)
: base(context, attrs, defStyle)
{
Init();
}
void Init()
{
_arcPaint = new Paint
{
AntiAlias = true,
StrokeCap = Paint.Cap.Round
};
_arcPaint.SetStyle(Paint.Style.Stroke);
_progressPaint = new Paint
{
AntiAlias = true,
StrokeCap = Paint.Cap.Round
};
_progressPaint.SetStyle(Paint.Style.Stroke);
LineWidth = (int)(4 * Context.Resources.DisplayMetrics.Density);
UpdateProgress();
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
var width = GetDefaultSize(SuggestedMinimumWidth, widthMeasureSpec);
var height = GetDefaultSize(SuggestedMinimumHeight, heightMeasureSpec);
CalculateArcRect(width, height);
UpdateThumbPosition();
// Don't use the exact radius but include TouchCorrection or else this makes interaction too tricky
if (Thumb != null)
{
_touchInsideIgnoreRadius = _arcRadius - (Math.Min(Thumb.Width, Thumb.Height) + TouchCorrection);
_touchOutsideIgnoreRadius = _arcRadius + (Math.Min(Thumb.Width, Thumb.Height) + TouchCorrection);
}
else
{
_touchInsideIgnoreRadius = _arcRadius - TouchCorrection;
_touchOutsideIgnoreRadius = _arcRadius + TouchCorrection;
}
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
if (!Clockwise)
canvas.Scale(-1, 1, _arcRect.CenterX(), _arcRect.CenterY());
canvas.DrawArc(_arcRect, StartAngle, SweepAngle, false, _arcPaint);
canvas.DrawArc(_arcRect, StartAngle, _progressSweep, false, _progressPaint);
if (Thumb != null)
{
var left = (_translateX - _thumbXPos) - (Thumb.Width / 2);
var top = (_translateY - _thumbYPos) - (Thumb.Height / 2);
canvas.DrawBitmap(Thumb, left, top, null);
}
}
void CalculateArcRect(int width, int height)
{
_translateX = (int)(width * 0.5f);
_translateY = (int)(height * 0.5f);
var min = Math.Min(width, height);
if (Thumb != null)
{
var arcDiameter = min - (LineWidth + Thumb.Width);
_arcRadius = arcDiameter / 2;
var top = height / 2 - (arcDiameter / 2);
var left = width / 2 - (arcDiameter / 2);
_arcRect.Set(left, top, left + arcDiameter, top + arcDiameter);
}
else
{
var arcDiameter = min - LineWidth;
_arcRadius = arcDiameter / 2;
var top = height / 2 - (arcDiameter / 2);
var left = width / 2 - (arcDiameter / 2);
_arcRect.Set(left, top, left + arcDiameter, top + arcDiameter);
}
}
public override bool OnTouchEvent(MotionEvent e)
{
if (Enabled)
{
Parent?.RequestDisallowInterceptTouchEvent(true);
switch (e.Action)
{
case MotionEventActions.Down:
case MotionEventActions.Move:
UpdateOnTouch(e);
break;
case MotionEventActions.Up:
case MotionEventActions.Cancel:
Pressed = false;
Parent?.RequestDisallowInterceptTouchEvent(true);
break;
}
return true;
}
return false;
}
void UpdateOnTouch(MotionEvent e)
{
if (IgnoreTouch(e.GetX(), e.GetY()))
return;
Pressed = true;
var touchAngle = GetTouchDegrees(e.GetX(), e.GetY());
var progress = GetProgressForAngle(touchAngle);
if (progress >= 0)
{
Progress = progress;
}
}
bool IgnoreTouch(float xPos, float yPos)
{
var x = xPos - _translateX;
var y = yPos - _translateY;
return PointIsInsideCircle(_touchInsideIgnoreRadius, x, y) || !PointIsInsideCircle(_touchOutsideIgnoreRadius, x, y);
}
bool PointIsInsideCircle(double circleRadius, double x, double y)
{
return (Math.Pow(x, 2) + Math.Pow(y, 2)) < (Math.Pow(circleRadius, 2));
}
double GetTouchDegrees(float xPos, float yPos)
{
float x = xPos - _translateX;
float y = yPos - _translateY;
if (!Clockwise)
x = -x;
// Convert to arc Angle
var angle = Math.ToDegrees(Math.Atan2(y, x) + (Math.Pi / 2));
angle -= 90;
if (angle < 0)
{
angle = 360 + angle;
}
angle -= StartAngle;
return angle;
}
int GetProgressForAngle(double angle)
{
var valuePerDegree = (float)Maximum / SweepAngle;
var progress = (int)Math.Round(valuePerDegree * angle);
if (progress < 0 || progress > Maximum)
return -1;
return progress;
}
void UpdateProgress()
{
_progressSweep = (float)Progress / Maximum * SweepAngle;
UpdateThumbPosition();
Invalidate();
}
void UpdateThumbPosition()
{
var thumbAngle = StartAngle + _progressSweep + 180;
_thumbXPos = (int)(_arcRadius * Math.Cos(Math.ToRadians(thumbAngle)));
_thumbYPos = (int)(_arcRadius * Math.Sin(Math.ToRadians(thumbAngle)));
}
/// <summary>
/// Sets the thumb resource identifier.
/// This will automatically be converted to a bitmap and set to the Thumb property.
/// </summary>
public void SetThumbResourceId(int resId)
{
Thumb = BitmapFactory.DecodeResource(Resources, resId);
}
public void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
the in your CircleSliderViewRenderer :
class CircleSliderViewRenderer: ViewRenderer
{
Context mContext;
CircularSlider _slider;
public CircleSliderViewRenderer(Context context) : base(context)
{
mContext = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
_slider = new CircularSlider(mContext);
_slider.Color = Android.Graphics.Color.Red;
_slider.ProgressColor = Android.Graphics.Color.Yellow;
_slider.Maximum = 50;
_slider.Progress = 20;
_slider.ProgressChanged += _slider_ProgressChanged;
SetNativeControl(_slider);
}
private void _slider_ProgressChanged(object sender, int e)
{
//when the progress change,you could do something here
}
}
the effect like this:

Related

Image Gallery Wrap Pannel not wrapping properly

I seem to have a problem with my Images, they are not displaying properly and seems to be some problem with the WrapPanel.
There are a lot of White spaces on the sides and only one Image seems to be shown at the time. The images are loading and everything.
Below is part of my XAML
<ScrollView Grid.Column="0" Grid.Row="1" x:Name="ssView" Orientation="Vertical" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*">
</RowDefinition>
<RowDefinition Height="128">
</RowDefinition>
<RowDefinition Height="Auto">
</RowDefinition>
</Grid.RowDefinitions>
<controls:WrapPanelFixed x:Name="wpImages" Orientation="Horizontal" ItemHeightRequest="{Binding RequestedTileSize}" ItemWidthRequest="{Binding RequestedTileSize}" HeightScaling="1">
<controls:WrapPanelFixed.ItemTemplate>
<DataTemplate>
<RelativeLayout BackgroundColor="White">
<Image
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1.0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1.0}"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
Source="{Binding LocalImagePath}" HorizontalOptions="Center" VerticalOptions="Center" Aspect="AspectFit">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnImageTapped" NumberOfTapsRequired="1"/>
</Image.GestureRecognizers>
</Image>
</RelativeLayout>
</DataTemplate>
</controls:WrapPanelFixed.ItemTemplate>
</controls:WrapPanelFixed>
</Grid>
</ScrollView>
c# Part of the code
public void Prepare()
{
RefreshImageList(Issue.Id);
}
protected void OnItemHeightChanged(object sender, ItemHeightChangedArg e)
{
int width = e.newHeigth;
e.newWidth = width;
}
protected void OnItemWidthChanged(object sender, ItemWidthChangedArg e)
{
int width = e.newWidth;
e.newHeigth = width;
}
protected void OnSizeChanged(object sender, EventArgs e)
{
wpImages.NeedLayoutOfChildren = true;
wpImages.ForceLayout();
} public void Prepare()
{
RefreshImageList(Issue.Id);
}
protected void OnItemHeightChanged(object sender, ItemHeightChangedArg e)
{
int width = e.newHeigth;
e.newWidth = width;
}
protected void OnItemWidthChanged(object sender, ItemWidthChangedArg e)
{
int width = e.newWidth;
e.newHeigth = width;
}
protected void OnSizeChanged(object sender, EventArgs e)
{
wpImages.NeedLayoutOfChildren = true;
wpImages.ForceLayout();
}
WrapPanel
namespace WrapPanelFixed.Controls
{
public class ItemHeightChangedArg : EventArgs
{
public int newWidth { get; set; }
public int newHeigth { get; private set; }
public ItemHeightChangedArg(int h)
{
newHeigth = h;
}
}
public class ItemWidthChangedArg : EventArgs
{
public int newHeigth { get; set; }
public int newWidth{ get; private set; }
public ItemWidthChangedArg(int h)
{
newWidth = h;
}
}
// Fork of the Wrappanel at https://gist.github.com/NicoVermeir/7ffb34ebd639ed958382
// This panel only allow fixed size items. and it will also strech/shrink item size to makesure it fills the orientation size
public class WrapPanelFixed : Layout<View>
{
private event EventHandler<NotifyCollectionChangedEventArgs> _collectionChanged;
public event EventHandler<ItemHeightChangedArg> ItemHeightChanged;
public event EventHandler<ItemWidthChangedArg> ItemWidthChanged;
public bool NeedLayoutOfChildren { get; set; }
public int ItemHeight { get; private set; }
public int ItemWidth { get; private set; }
public int UnusedWidth { get; private set; }
public int UnusedHeight { get; private set; }
public double HeightScaling { get; set; }
// Minimum number of items per constraint. (width or height)
public int MinimumItems {get; set;}
protected virtual void OnItemHeightChanged(int newHeight, out int newWidth)
{
newWidth = -1;
EventHandler<ItemHeightChangedArg> handler = ItemHeightChanged;
if(handler != null)
{
ItemHeightChangedArg e = new ItemHeightChangedArg(newHeight);
handler(this, e);
newWidth = e.newWidth;
}
}
protected virtual void OnItemWidthChanged(int newHeight, out int newWidth)
{
newWidth = -1;
EventHandler<ItemWidthChangedArg> handler = ItemWidthChanged;
if (handler != null)
{
ItemWidthChangedArg e = new ItemWidthChangedArg(newHeight);
handler(this, e);
newWidth = e.newWidth;
}
}
/// <summary>
/// Backing Storage for the Orientation property
/// </summary>
public static readonly BindableProperty OrientationProperty =
BindableProperty.Create<WrapPanelFixed, StackOrientation>(w => w.Orientation, StackOrientation.Vertical,
propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());
/// <summary>
/// Orientation (Horizontal or Vertical)
/// </summary>
public StackOrientation Orientation
{
get { return (StackOrientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
/// <summary>
/// Backing Storage for the Spacing property
/// </summary>
public static readonly BindableProperty SpacingProperty =
BindableProperty.Create<WrapPanelFixed, double>(w => w.Spacing, 6,
propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());
/// <summary>
/// Spacing added between elements (both directions)
/// </summary>
/// <value>The spacing.</value>
public double Spacing
{
get { return (double)GetValue(SpacingProperty); }
set { SetValue(SpacingProperty, value); }
}
public static readonly BindableProperty ItemHeightRequestProperty =
BindableProperty.Create<WrapPanelFixed, int>(w => w.ItemHeightRequest, 100,
propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());
public int ItemHeightRequest
{
get { return (int)GetValue(ItemHeightRequestProperty); }
set { SetValue(ItemHeightRequestProperty, value); }
}
public static readonly BindableProperty ItemWidthRequestProperty =
BindableProperty.Create<WrapPanelFixed, int>(w => w.ItemWidthRequest, 100,
propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());
public int ItemWidthRequest
{
get { return (int)GetValue(ItemWidthRequestProperty); }
set { SetValue(ItemWidthRequestProperty, value); }
}
/// <summary>
/// Backing Storage for the Spacing property
/// </summary>
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create<WrapPanelFixed, DataTemplate>(w => w.ItemTemplate, null,
propertyChanged: (bindable, oldvalue, newvalue) => ((WrapPanelFixed)bindable).OnSizeChanged());
/// <summary>
/// Spacing added between elements (both directions)
/// </summary>
/// <value>The spacing.</value>
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
/// <summary>
/// Backing Storage for the Spacing property
/// </summary>
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create<WrapPanelFixed, IEnumerable>(w => w.ItemsSource, null,
propertyChanged: ItemsSource_OnPropertyChanged);
/// <summary>
/// Spacing added between elements (both directions)
/// </summary>
/// <value>The spacing.</value>
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
private static void ItemsSource_OnPropertyChanged(BindableObject bindable, IEnumerable oldvalue, IEnumerable newvalue)
{
WrapPanelFixed wp = bindable as WrapPanelFixed;
if (oldvalue != null)
{
var coll = (INotifyCollectionChanged)oldvalue;
// Unsubscribe from CollectionChanged on the old collection
coll.CollectionChanged -= wp.ItemsSource_OnItemChanged;
}
if (newvalue != null)
{
var coll = (INotifyCollectionChanged)newvalue;
// Subscribe to CollectionChanged on the new collection
coll.CollectionChanged += wp.ItemsSource_OnItemChanged;
}
}
public WrapPanelFixed()
{
MinimumItems = 1;
HeightScaling = 1.0;
_collectionChanged += OnCollectionChanged;
NeedLayoutOfChildren = true;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if(args.Action == NotifyCollectionChangedAction.Reset)
{
Children.Clear();
NeedLayoutOfChildren = true;
}
else
{
foreach (object item in args.NewItems)
{
var child = ItemTemplate.CreateContent() as View;
if (child == null)
return;
child.BindingContext = item;
Children.Add(child);
}
NeedLayoutOfChildren = true;
}
}
public void OnCollectionRebuild(IList Items)
{
foreach (object item in Items)
{
var child = ItemTemplate.CreateContent() as View;
if (child == null)
return;
child.BindingContext = item;
Children.Add(child);
}
NeedLayoutOfChildren = true;
}
public void ItemsSource_OnItemChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_collectionChanged != null)
_collectionChanged(sender, e);
}
/// <summary>
/// This is called when the spacing or orientation properties are changed - it forces
/// the control to go back through a layout pass.
/// </summary>
private void OnSizeChanged()
{
ForceLayout();
}
/// <summary>
/// This method is called during the measure pass of a layout cycle to get the desired size of an element.
/// </summary>
/// <param name="widthConstraint">The available width for the element to use.</param>
/// <param name="heightConstraint">The available height for the element to use.</param>
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (WidthRequest > 0)
widthConstraint = Math.Min(widthConstraint, WidthRequest);
if (HeightRequest > 0)
heightConstraint = Math.Min(heightConstraint, HeightRequest);
double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);
return Orientation == StackOrientation.Vertical ? DoVerticalMeasure(internalWidth, internalHeight) : DoHorizontalMeasure(internalWidth, internalHeight);
}
public static int CalculateItemSize(double sizeConstraint, int requestItemSize, double itemSpacing, int MinimumItems, out int nItemsPerConstraint)
{
requestItemSize += (int)(itemSpacing / 2);
// Minimum size for MinimumItems
int MinSize = (requestItemSize * MinimumItems) + (int)(itemSpacing * (MinimumItems - 1));
if (MinimumItems > 0 && MinSize > sizeConstraint)
{
// Shrink item size so we requestItemSize is minimum of wanted items
requestItemSize = (int)((sizeConstraint - (itemSpacing * (MinimumItems - 1))) / MinimumItems); // Atleast 2 columns
}
int newItemSize = requestItemSize;
// How many items with the requested size fit in the sizeConstraint
nItemsPerConstraint = (int)(sizeConstraint / (requestItemSize + itemSpacing));
// How much space do we got left
// (There is 1 less spacing then number of item. Only spacing between items)
double spaceLeft = sizeConstraint - ((nItemsPerConstraint * (requestItemSize + itemSpacing)) - itemSpacing);
// If spaceLeft is > then 50% of requestItemSize and less then 1 full item extra, then make items bigger so they fill the sizeContraint
// else make them small so one more item fits.
if (spaceLeft > (requestItemSize * 0.5) && spaceLeft < (requestItemSize + itemSpacing))
{
int extraWidth = (int)(spaceLeft / nItemsPerConstraint);
newItemSize = (int)(requestItemSize + extraWidth);
}
else
{
nItemsPerConstraint++;
newItemSize = (int)((sizeConstraint - (itemSpacing * (nItemsPerConstraint - 1))) / nItemsPerConstraint);
}
return newItemSize;
}
/// <summary>
/// Does the vertical measure.
/// </summary>
/// <returns>The vertical measure.</returns>
/// <param name="widthConstraint">Width constraint.</param>
/// <param name="heightConstraint">Height constraint.</param>
private SizeRequest DoVerticalMeasure(double widthConstraint, double heightConstraint)
{
int nItemsPerCol = 0;
int newItemHeight = CalculateItemSize(heightConstraint, ItemHeightRequest, Spacing, MinimumItems, out nItemsPerCol);
UnusedHeight = (int)(heightConstraint - ((nItemsPerCol * (newItemHeight + Spacing)) - Spacing));
if(ItemHeight != newItemHeight)
{
int newItemWidth;
OnItemHeightChanged(newItemHeight, out newItemWidth);
if (newItemWidth == -1)
newItemWidth = newItemHeight;
ItemHeight = newItemWidth;
ItemWidth = newItemWidth;
NeedLayoutOfChildren = true;
}
// Correct for rounding error
int colCount = (int)((Children.Count / (double)nItemsPerCol) + 0.9);
double height = (ItemWidth * nItemsPerCol) + ((nItemsPerCol - 1) * Spacing);
double width = ((ItemHeight + Spacing) * colCount) - Spacing;
return new SizeRequest(new Size(width, height), new Size(width, height));
}
/// <summary>
/// Does the horizontal measure.
/// </summary>
/// <returns>The horizontal measure.</returns>
/// <param name="widthConstraint">Width constraint.</param>
/// <param name="heightConstraint">Height constraint.</param>
private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
{
int nItemsPerRow = 0;
int newItemWidth = CalculateItemSize(widthConstraint, ItemWidthRequest, Spacing, MinimumItems, out nItemsPerRow);
UnusedWidth = (int)(widthConstraint - ((nItemsPerRow * (newItemWidth + Spacing)) - Spacing));
if(ItemWidth != newItemWidth)
{
int newItemHeight = 0;
OnItemWidthChanged(newItemWidth, out newItemHeight);
if(newItemHeight == -1)
newItemHeight = newItemWidth;
ItemWidth = newItemWidth;
ItemHeight = newItemWidth;
NeedLayoutOfChildren = true;
}
ItemHeight = (int)(ItemHeight * HeightScaling);
// Correct for rounding error
int rowCount = (int)((Children.Count / (double)nItemsPerRow)+0.9); // So if we have 12.1 rows we get 13
double width = (ItemWidth * nItemsPerRow) + ((nItemsPerRow - 1) * Spacing);
double height = ((ItemHeight + Spacing) * rowCount) - Spacing;
return new SizeRequest(new Size(width, height), new Size(width, height));
}
/// <summary>
/// Positions and sizes the children of a Layout.
/// </summary>
/// <param name="x">A value representing the x coordinate of the child region bounding box.</param>
/// <param name="y">A value representing the y coordinate of the child region bounding box.</param>
/// <param name="width">A value representing the width of the child region bounding box.</param>
/// <param name="height">A value representing the height of the child region bounding box.</param>
protected override void LayoutChildren(double x, double y, double width, double height)
{
// Prevent unnessecary layouting.(since all items are fixed in size, we only need to relayout them if itemsource is changed)
if (NeedLayoutOfChildren == false)
return;
if (Orientation == StackOrientation.Vertical)
{
double colWidth = 0;
double yPos = y, xPos = x;
foreach (var child in Children.Where(c => c.IsVisible))
{
//var request = child.GetSizeRequest(width, height);
double childWidth = ItemWidth;
double childHeight = ItemHeight;
colWidth = Math.Max(colWidth, childWidth);
if (yPos + childHeight > height)
{
yPos = y;
xPos += colWidth + Spacing;
colWidth = 0;
}
var region = new Rectangle(xPos, yPos, childWidth, childHeight);
LayoutChildIntoBoundingRegion(child, region);
yPos += region.Height + Spacing;
}
}
else
{
//double rowHeight = 0;
x += UnusedWidth / 2;
double yPos = y, xPos = x;
foreach (var child in Children.Where(c => c.IsVisible))
{
double childWidth = ItemWidth;
double childHeight = ItemHeight;
//rowHeight = Math.Max(rowHeight, childHeight);
if (xPos + childWidth > width)
{
xPos = x;
yPos += childHeight + Spacing;
//rowHeight = 0;
}
var region = new Rectangle(xPos, yPos, childWidth, childHeight);
LayoutChildIntoBoundingRegion(child, region);
xPos += region.Width + Spacing;
}
}
NeedLayoutOfChildren = false;
}
}
}
Try this:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="PhotoTemplate">
<Grid Width="50"
Height="50"
Margin="0">
<Image HorizontalAlignment="Center"
Stretch="UniformToFill">
<Image.Source>
<BitmapImage DecodePixelHeight="200"
UriSource="{Binding LocalImagePath}" />
</Image.Source>
</Image>
</Grid>
</DataTemplate>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</Page.Resources>
<Grid Padding="48">
<ListView Name="WrapPanelContainer"
Margin="2"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource PhotoTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:WrapPanel Background="LightGray"
VerticalSpacing="5"
HorizontalSpacing="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListView>
</Grid>
</Page>

How to re-size image without effecting the quality of original image in C#? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
How to create a multiple image thumbnails of a image in C#.
Condition is:
1. Image quality should not effect.
2. Thumbnails size should be not same.
If you are using asp.net mvc then you could try the build-in WebImage class. More info can be found here: http://msdn.microsoft.com/en-us/library/system.web.helpers.webimage(v=vs.111).aspx
For more professional use, where you want to control everything, you could use: http://imageresizing.net/ this suite is very advanced with lots of features on image resizing.
There are also plenty of options available for writing your own code, an example can be found here: http://www.codeproject.com/Articles/191424/Resizing-an-Image-On-The-Fly-using-NET
My suggestion would be: "Don't try to invent the wheel again", there are a lot of good solutions out there.
Hope this can help you out.
using System.ComponentModel;
using System.Web.UI;
/// <summary>
/// An abstract ImageFilter class.
/// </summary>
public abstract class ImageFilter
{
/// <summary>
/// Processes the image with current filter.
/// </summary>
/// <param name="image">The image to process.</param>
/// <returns>Returns a Image after applying current filter.</returns>
public abstract System.Drawing.Image Process(System.Drawing.Image image);
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return "Image Filter";
}
}
public class ResizeFilter : ImageFilter
{
private int x = -1;
private int y = -1;
private int height;
private int width;
private InterpolationMode interpolationMode = InterpolationMode.Bicubic;
private ResizeMode resizeMode = ResizeMode.Fit;
[DefaultValue(-1)]
[Category("Behavior")]
public int X
{
get
{
return this.x;
}
set
{
if (value < 0)
{
value = -1;
}
x = value;
}
}
[DefaultValue(-1)]
[Category("Behavior")]
public int Y
{
get
{
return this.y;
}
set
{
if (value < -1)
{
value = -1;
}
y = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether disable transparency.
/// </summary>
/// <value>
/// <see langword="true"/> if disable transparency; otherwise, <see langword="false"/>.
/// </value>
/// <author>Anwar</author>
/// <datetime>3/4/2011 5:37 PM</datetime>
[DefaultValue(false), Category("Behavior")]
public bool DisableTransparency { get; set; }
/// <summary>
/// Gets or sets the maximum height of the resulting image.
/// </summary>
/// <value>The height of the resulting image.</value>
[DefaultValue(0)]
[Category("Behavior")]
public int Height
{
get
{
return this.height;
}
set
{
CheckValue(value);
this.height = value;
}
}
/// <summary>
/// Gets or sets the interpolation mode used for resizing images. The default is HighQualityBicubic.
/// </summary>
/// <value>The interpolation mode.</value>
[DefaultValue(InterpolationMode.HighQualityBicubic)]
[Category("Behavior")]
public InterpolationMode InterpolationMode
{
get { return this.interpolationMode; }
set { this.interpolationMode = value; }
}
/// <summary>
/// Gets or sets the resize mode. The default value is Fit.
/// </summary>
/// <value>The image resize mode.</value>
[DefaultValue(ResizeMode.Fit)]
[Category("Behavior")]
public ResizeMode ResizeMode
{
get { return this.resizeMode; }
set { this.resizeMode = value; }
}
/// <summary>
/// Gets or sets the maximum width of the resulting image.
/// </summary>
/// <value>The width of the image..</value>
[DefaultValue(0)]
[Category("Behavior")]
public int Width
{
get
{
return this.width;
}
set
{
CheckValue(value);
this.width = value;
}
}
/// <summary>
/// Processes the image with current filter.
/// </summary>
/// <param name="image">The image to process.</param>
/// <returns>Returns a Image after applying current filter.</returns>
public override Image Process(Image image)
{
int scaledHeight = (int)(image.Height * (this.Width / (float)image.Width));
int scaledWidth = (int)(image.Width * (this.Height / (float)image.Height));
switch (this.ResizeMode)
{
case ResizeMode.Fit:
return this.FitImage(image, scaledHeight, scaledWidth);
case ResizeMode.Crop:
return this.CropImage(image, scaledHeight, scaledWidth);
default:
Debug.Fail("Should not reach this");
break;
}
return null;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return "Resize Filter";
}
private static void CheckValue(int value)
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value");
}
}
private Image CropImage(Image img, int scaledHeight, int scaledWidth)
{
int resizeWidth;
int resizeHeight;
if (this.Width != 0 && this.Height != 0)
{
resizeWidth = this.Width;
resizeHeight = this.Height;
}
else if (this.Height == 0)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else if (this.Width == 0)
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
else
{
if (this.Width / (float)img.Width > this.Height / (float)img.Height)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
}
Bitmap newImage = new Bitmap(this.Width, this.Height);
using (Graphics graphics = Graphics.FromImage(newImage))
{
this.SetupGraphics(graphics);
if (this.DisableTransparency)
{
graphics.FillRectangle(new SolidBrush(Color.White), 0, 0, resizeWidth, resizeHeight);
}
int srcX = this.X;
int srcY = this.Y;
if (srcX == -1)
{
srcX = (this.Width - resizeWidth) / 2;
}
if (srcY == -1)
{
srcY = (this.Height - resizeHeight) / 2;
}
graphics.DrawImage(img, new Rectangle(0, 0, resizeWidth, resizeHeight), srcX, srcY, resizeWidth, resizeHeight, GraphicsUnit.Pixel);
}
return newImage;
}
private Image FitImage(Image img, int scaledHeight, int scaledWidth)
{
int resizeWidth;
int resizeHeight;
if (this.Width != 0 && this.Height != 0)
{
resizeWidth = this.Width;
resizeHeight = this.Height;
}
else if (this.Height == 0)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else if (this.Width == 0)
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
else
{
if (this.Width / (float)img.Width < this.Height / (float)img.Height)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
}
Bitmap newimage = new Bitmap(resizeWidth, resizeHeight);
using (Graphics gra = Graphics.FromImage(newimage))
{
if (DisableTransparency)
{
gra.FillRectangle(new SolidBrush(Color.White), 0, 0, resizeWidth, resizeHeight);
}
this.SetupGraphics(gra);
gra.DrawImage(img, 0, 0, resizeWidth, resizeHeight);
}
return newimage;
}
private void SetupGraphics(Graphics graphics)
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = this.InterpolationMode;
}
}
////Seprate this enum in your different class ResizeMode.cs
/// <summary>
/// Image Resize Modes.
/// </summary>
public enum ResizeMode
{
/// <summary>
/// Fit mode maintains the aspect ratio of the original image while ensuring that the dimensions of the result
/// do not exceed the maximum values for the resize transformation.
/// </summary>
Fit,
/// <summary>
/// Crop resizes the image and removes parts of it to ensure that the dimensions of the result are exactly
/// as specified by the transformation.
/// </summary>
Crop
}
///Your Controller Code
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult ImportImage()
{
var postedFile = Request.Files["FileUpload1"];
if (postedFile != null && postedFile.ContentLength > 0 )
{
MemoryStream ms=new MemoryStream();
postedFile.InputStream.CopyTo(ms);
Image image=new Bitmap(ms);
Guid newImageName=Guid.NewGuid();
string newNameToImage = newImageName.ToString() + Path.GetExtension(postedFile.FileName);
ResizeFilter resizeFilter=new ResizeFilter {Height = 0, Width = 75};
using (Image thumbnailImage = resizeFilter.Process(image))
{
var imagePath = Path.Combine(Server.MapPath("~/Content/Images"), newNameToImage);
thumbnailImage.Save(imagePath);
}
resizeFilter.Width = 350;
resizeFilter.Height = 0;
newImageName = Guid.NewGuid();
newNameToImage = newImageName.ToString() + Path.GetExtension(postedFile.FileName);
using (Image middleImage = resizeFilter.Process(image))
{
var imagePath = Path.Combine(Server.MapPath("~/Content/Images"), newNameToImage);
middleImage.Save(imagePath);
}
}
return RedirectToAction("Index");
}
}
///View Coding
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("ImportImage", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="FileUpload1" id="FileUpload1" />
<input type="submit" id="submit" />
}

How to make simple XNA buttons?

I was wondering if anyone could show me a simple way of creating buttons in XNA.
Preferably a way to add a function when its clicked, and a way to add and remove them easily.
I recommend using the NeoForce Controls Library for GUI related problems - it has buttons, among the other useful GUI controls including popup windows, list views, combo boxes, and so on.
If you are writing a button class for the learning experience... well, try learning more about it yourself via Google before asking for help.
ADDENDUM
This is some code that I've written for buttons. Maybe it can serve as a starting point. I use it in my 2D game engine, so it has been debugged and tested.
/// <summary>
/// A control that changes appearance when it is hovered over and triggers some effect when it is clicked
/// </summary>
public class EnigmaButton
{
/// <summary>
/// The method signature for notification when a button is clicked
/// </summary>
/// <param name="sender">EnigmaButton that was clicked</param>
public delegate void OnClickEvent(EnigmaButton sender);
/// <summary>
/// Types of textures used for Enigma Buttons
/// </summary>
public enum TextureType { Normal, Over, Press }
#region Variables
protected IVisualExposer m_ui;
protected Rectangle m_bounds;
IInputExposer m_input;
bool m_over = false, m_press = false, m_wasPressed = false;
Dictionary<TextureType, EnigmaResource<Texture2D>> m_textures;
string m_text, m_name;
EnigmaResource<SpriteFont> m_font;
int m_minTextShadow, m_maxTextShadow;
Color m_textTint;
public event OnClickEvent OnClick;
#endregion
/// <summary>
/// A control that changes appearance when it is hovered over and triggers some effect when it is clicked
/// </summary>
/// <param name="ui">Graphical assets</param>
/// <param name="input">Input exposer for mouse input and XBox controller input</param>
/// <param name="reader">XMLReader for the definition of the controller</param>
/// <param name="pos">Bounds of the controller</param>
public EnigmaButton(IVisualExposer ui, IInputExposer input, XmlReader reader, Rectangle pos)
{
m_ui = ui;
m_bounds = pos;
m_textures = new Dictionary<TextureType, EnigmaResource<Texture2D>>();
m_input = input;
Enabled = true;
#region Reading
string name;
bool started = false, insideText = false;
while (reader.Read())
{
if (reader.MoveToContent() == XmlNodeType.Element)
{
name = reader.Name.ToLower();
if (name == "button")
{
if (started)
throw new Exception("Already started.");
started = true;
m_name = reader.GetAttribute("name") ?? string.Empty;
}
else if (!started)
throw new Exception("Not started");
else if (name == "text")
{
m_font = new EnigmaResource<SpriteFont>();
m_font.Filepath = reader.GetAttribute("font");
string minShadow = reader.GetAttribute("minShadow"), maxShadow = reader.GetAttribute("maxShadow");
m_minTextShadow = minShadow != null ? int.Parse(minShadow) : 0;
m_maxTextShadow = maxShadow != null ? int.Parse(maxShadow) : 2;
m_text = reader.GetAttribute("text") ?? string.Empty;
insideText = true;
m_textTint = Color.White;
}
else if (name == "bounds")
{
insideText = false;
m_bounds = new Rectangle(int.Parse(reader.GetAttribute("x")), int.Parse(reader.GetAttribute("y")),
int.Parse(reader.GetAttribute("width")), int.Parse(reader.GetAttribute("height")));
}
else if (name == "texture")
{
insideText = false;
TextureType texType = (TextureType)Enum.Parse(typeof(TextureType), reader.GetAttribute("type"));
if (m_textures.ContainsKey(texType))
throw new Exception("A texture of type '" + texType.ToString() + "' cannot be registered twice");
EnigmaResource<Texture2D> res = new EnigmaResource<Texture2D>();
res.Filepath = reader.ReadString();
m_textures.Add(texType, res);
}
else if (name == "tint")
{
if (!insideText)
throw new Exception("Tints can only be for text");
float a, r, g, b;
string[] split = reader.ReadString().Split(',');
if (split.Length != 4)
throw new Exception("Colors must be RGBA");
r = float.Parse(split[0].Trim());
g = float.Parse(split[1].Trim());
b = float.Parse(split[2].Trim());
a = float.Parse(split[3].Trim());
m_textTint = new Color(r, g, b, a);
}
}
}
#endregion
if (!m_textures.ContainsKey(TextureType.Normal))
throw new Exception("A button must have at least a '" + TextureType.Normal.ToString() + "' texture");
}
#region Methods
public void Initialize()
{
}
public void LoadContent()
{
EnigmaResource<Texture2D> res;
for (int i = 0; i < m_textures.Count; i++)
{
res = m_textures[m_textures.ElementAt(i).Key];
res.Resource = m_ui.Content.Load<Texture2D>(res.Filepath);
m_textures[m_textures.ElementAt(i).Key] = res;
}
if (m_font.Filepath != null)
m_font.Resource = m_ui.Content.Load<SpriteFont>(m_font.Filepath);
}
public void Update(GameTime gameTime)
{
m_wasPressed = m_press;
m_over = m_bounds.Contains(m_input.MouseX, m_input.MouseY);
m_press = m_over ? m_wasPressed ? m_input.IsMouseLeftPressed || m_input.IsButtonPressed(Buttons.A) : m_input.IsMouseLeftTriggered || m_input.IsButtonTriggered(Buttons.A) : false;
if (!m_wasPressed && m_press && OnClick != null)
OnClick(this);
}
public void Draw(GameTime gameTime)
{
Texture2D toDraw = m_textures[TextureType.Normal].Resource;
if (Enabled)
{
if (m_press && m_textures.ContainsKey(TextureType.Press))
toDraw = m_textures[TextureType.Press].Resource;
else if (m_over && m_textures.ContainsKey(TextureType.Over))
toDraw = m_textures[TextureType.Over].Resource;
}
m_ui.SpriteBatch.Draw(toDraw, m_bounds, Enabled ? Color.White : Color.Gray);
if (m_font.Resource != null)
{
Vector2 pos = new Vector2(m_bounds.X, m_bounds.Y);
Vector2 size = m_font.Resource.MeasureString(m_text);
pos.X += (m_bounds.Width - size.X) / 2;
pos.Y += (m_bounds.Height - size.Y) / 2;
UIHelper.DrawShadowedString(m_ui, m_font.Resource, m_text, pos, m_textTint, m_minTextShadow, m_maxTextShadow);
}
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the name of the button
/// </summary>
public string Name
{
get { return m_name; }
set { m_name = value ?? m_name; }
}
/// <summary>
/// Gets or sets the text drawn in the button.
/// WARNING: Will overflow if the text does not normally fit.
/// </summary>
public string Text
{
get { return m_text; }
set { m_text = value ?? string.Empty; }
}
/// <summary>
/// Gets or sets the bounds of the button
/// </summary>
public Rectangle Bounds
{
get { return m_bounds; }
set { m_bounds = value; }
}
/// <summary>
/// Whether or not the control is enabled
/// </summary>
public bool Enabled { get; set; }
#endregion
}

How do I put text on ProgressBar?

I have used ProgressBar Control in my c# desktop application.I have used it in a thread other then the thread in which control has been declared.Its working Fine.
Now I am wondering how i can show some text inside progress bar control like "Initiating Registration" etc.Also I want to use it as Marquee progress bar.Please help me.
You will have to override the OnPaint method, call the base implementation and the paint your own text.
You will need to create your own CustomProgressBar and then override OnPaint to draw what ever text you want.
Custom Progress Bar Class
namespace ProgressBarSample
{
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class CustomProgressBar: ProgressBar
{
//Property to set to decide whether to print a % or Text
public ProgressBarDisplayText DisplayStyle { get; set; }
//Property to hold the custom text
public String CustomText { get; set; }
public CustomProgressBar()
{
// Modify the ControlStyles flags
//http://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = ClientRectangle;
Graphics g = e.Graphics;
ProgressBarRenderer.DrawHorizontalBar(g, rect);
rect.Inflate(-3, -3);
if (Value > 0)
{
// As we doing this ourselves we need to draw the chunks on the progress bar
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
}
// Set the Display text (Either a % amount or our custom text
int percent = (int)(((double)this.Value / (double)this.Maximum) * 100);
string text = DisplayStyle == ProgressBarDisplayText.Percentage ? percent.ToString() + '%' : CustomText;
using (Font f = new Font(FontFamily.GenericSerif, 10))
{
SizeF len = g.MeasureString(text, f);
// Calculate the location of the text (the middle of progress bar)
// Point location = new Point(Convert.ToInt32((rect.Width / 2) - (len.Width / 2)), Convert.ToInt32((rect.Height / 2) - (len.Height / 2)));
Point location = new Point(Convert.ToInt32((Width / 2) - len.Width / 2), Convert.ToInt32((Height / 2) - len.Height / 2));
// The commented-out code will centre the text into the highlighted area only. This will centre the text regardless of the highlighted area.
// Draw the custom text
g.DrawString(text, f, Brushes.Red, location);
}
}
}
}
Sample WinForms Application
using System;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
namespace ProgressBarSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set our custom Style (% or text)
customProgressBar1.DisplayStyle = ProgressBarDisplayText.CustomText;
customProgressBar1.CustomText = "Initialising";
}
private void btnReset_Click(object sender, EventArgs e)
{
customProgressBar1.Value = 0;
btnStart.Enabled = true;
}
private void btnStart_Click(object sender, EventArgs e)
{
btnReset.Enabled = false;
btnStart.Enabled = false;
for (int i = 0; i < 101; i++)
{
customProgressBar1.Value = i;
// Demo purposes only
System.Threading.Thread.Sleep(100);
// Set the custom text at different intervals for demo purposes
if (i > 30 && i < 50)
{
customProgressBar1.CustomText = "Registering Account";
}
if (i > 80)
{
customProgressBar1.CustomText = "Processing almost complete!";
}
if (i >= 99)
{
customProgressBar1.CustomText = "Complete";
}
}
btnReset.Enabled = true;
}
}
}
I have written a no blinking/flickering TextProgressBar
You can find the source code here: https://github.com/ukushu/TextProgressBar
WARNING: It's a little bit buggy! But still, I think it's better than another answers here. As I have no time for fixes, if you will do sth with them, please send me update by some way:) Thanks.
Samples:
AVOID FLICKERING TEXT
The solution provided by Barry above is excellent, but there's is the "flicker-problem".
As soon as the Value is above zero the OnPaint will be envoked repeatedly and the text will flicker.
There is a solution to this. We do not need VisualStyles for the object since we will be drawing it with our own code.
Add the following code to the custom object Barry wrote and you will avoid the flicker:
[DllImportAttribute("uxtheme.dll")]
private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);
protected override void OnHandleCreated(EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
base.OnHandleCreated(e);
}
I did not write this myself. It found it here: https://stackoverflow.com/a/299983/1163954
I've testet it and it works.
I wold create a control named for example InfoProgresBar, that provide this functionality with a label or two (Main Job, Current Job) and ProgressBar and use it instead of that ProgressBar.
I have used this simple code, and it works!
for (int i = 0; i < N * N; i++)
{
Thread.Sleep(50);
progressBar1.BeginInvoke(new Action(() => progressBar1.Value = i));
progressBar1.CreateGraphics().DrawString(i.ToString() + "%", new Font("Arial",
(float)10.25, FontStyle.Bold),
Brushes.Red, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
}
It just has one simple problem and this is it: when progress bar start to rising, percentage some times hide, and then appear again.
I did't write it myself.I found it here:
text on progressbar in c#
I used this code, and it does work.
I tried placing a label with transparent background over a progress bar but never got it to work properly. So I found Barry's solution here very useful, although I missed the beautiful Vista style progress bar. So I merged Barry's solution with http://www.dreamincode.net/forums/topic/243621-percent-into-progress-bar/ and managed to keep the native progress bar, while displaying text percentage or custom text over it. I don't see any flickering in this solution either. Sorry to dig up and old thread but I needed this today and so others may need it too.
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class ProgressBarWithCaption : ProgressBar
{
//Property to set to decide whether to print a % or Text
private ProgressBarDisplayText m_DisplayStyle;
public ProgressBarDisplayText DisplayStyle {
get { return m_DisplayStyle; }
set { m_DisplayStyle = value; }
}
//Property to hold the custom text
private string m_CustomText;
public string CustomText {
get { return m_CustomText; }
set {
m_CustomText = value;
this.Invalidate();
}
}
private const int WM_PAINT = 0x000F;
protected override void WndProc(ref Message m)
{
base.WndProc(m);
switch (m.Msg) {
case WM_PAINT:
int m_Percent = Convert.ToInt32((Convert.ToDouble(Value) / Convert.ToDouble(Maximum)) * 100);
dynamic flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
using (Graphics g = Graphics.FromHwnd(Handle)) {
using (Brush textBrush = new SolidBrush(ForeColor)) {
switch (DisplayStyle) {
case ProgressBarDisplayText.CustomText:
TextRenderer.DrawText(g, CustomText, new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
case ProgressBarDisplayText.Percentage:
TextRenderer.DrawText(g, string.Format("{0}%", m_Percent), new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
}
}
}
break;
}
}
}
Just want to point out something on #codingbadger answer. When using "ProgressBarRenderer" you should always check for "ProgressBarRenderer.IsSupported" before using the class. For me, this has been a nightmare with Visual Styles errors in Win7 that I couldn't fix. So, a better approach and workaround for the solution would be:
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
if (ProgressBarRenderer.IsSupported)
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
else
g.FillRectangle(new SolidBrush(this.ForeColor), clip);
Notice that the fill will be a simple rectangle and not chunks. Chunks will be used only if ProgressBarRenderer is supported
I have created a InfoProgressBar control which uses a TransparentLabel control. Testing on a form with a Timer, I get some slight glitches displaying the text every 30-40 value changes if using a timer interval of less than 250 milliseconds (probably because of the time required to update the screen is greater than the timer interval).
It would be possible to modify UpdateText method to insert all the calculated values into CustomText but it isn't something that I have needed yet. This would remove the need for the DisplayType property and enumerate.
The TransparentLabel class was created by adding a new UserControl and changing it to inheriting from Label with the following implementation:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Utils.GUI
{
public partial class TransparentLabel : Label
{
// hide the BackColor attribute as much as possible.
// setting the base value has no effect as drawing the
// background is disabled
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public override ContentAlignment TextAlign
{
get
{
return base.TextAlign;
}
set
{
base.TextAlign = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public TransparentLabel()
{
InitializeComponent();
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
base.BackColor = Color.Transparent;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
RecreateHandle();
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// do nothing
}
}
}
I did not make any changes to the related designer code but here it is for completeness.
namespace Utils.GUI
{
partial class TransparentLabel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
I then created another new UserControl and changed it to derive from ProgressBar with the following implementation:
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
namespace Utils.GUI
{
[Designer(typeof(InfoProgressBarDesigner))]
public partial class InfoProgressBar : ProgressBar
{
// designer class to add font baseline snapline by copying it from the label
private class InfoProgressBarDesigner : ControlDesigner
{
public override IList SnapLines
{
get
{
IList snapLines = base.SnapLines;
InfoProgressBar control = Control as InfoProgressBar;
if(control != null)
{
using(IDesigner designer = TypeDescriptor.CreateDesigner(control.lblText, typeof(IDesigner)))
{
if(designer != null)
{
designer.Initialize(control.lblText);
ControlDesigner boxDesigner = designer as ControlDesigner;
if(boxDesigner != null)
{
foreach(SnapLine line in boxDesigner.SnapLines)
{
if(line.SnapLineType == SnapLineType.Baseline)
{
snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset, line.Filter, line.Priority));
break;
}
}
}
}
}
}
return snapLines;
}
}
}
// enum to select the type of displayed value
public enum ProgressBarDisplayType
{
Custom = 0,
Percent = 1,
Progress = 2,
Remain = 3,
Value = 4,
}
private string _customText;
private ProgressBarDisplayType _displayType;
private int _range;
[Bindable(false)]
[Browsable(true)]
[DefaultValue("{0}")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// {0} is replaced with the result of the selected calculation
public string CustomText
{
get
{
return _customText;
}
set
{
_customText = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ProgressBarDisplayType.Percent)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ProgressBarDisplayType DisplayType
{
get
{
return _displayType;
}
set
{
_displayType = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// don't use the lblText font as if it is null, it checks the parent font (i.e. this property) and gives an infinite loop
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(100)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Maximum
{
get
{
return base.Maximum;
}
set
{
base.Maximum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Minimum
{
get
{
return base.Minimum;
}
set
{
base.Minimum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ContentAlignment.MiddleLeft)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ContentAlignment TextAlign
{
get
{
return lblText.TextAlign;
}
set
{
lblText.TextAlign = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(typeof(Color), "0x000000")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public Color TextColor
{
get
{
return lblText.ForeColor;
}
set
{
lblText.ForeColor = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Value
{
get
{
return base.Value;
}
set
{
base.Value = value;
UpdateText();
}
}
public InfoProgressBar()
{
InitializeComponent();
CustomText = "{0}";
DisplayType = ProgressBarDisplayType.Percent;
Maximum = 100;
Minimum = 0;
TextAlign = ContentAlignment.MiddleLeft;
TextColor = Color.Black;
Value = 0;
// means the label gets drawn in front of the progress bar
lblText.Parent = this;
_range = base.Maximum - base.Minimum;
}
protected void UpdateText()
{
switch(DisplayType)
{
case ProgressBarDisplayType.Custom:
{
lblText.Text = _customText;
break;
}
case ProgressBarDisplayType.Percent:
{
if(_range > 0)
{
lblText.Text = string.Format(_customText, string.Format("{0}%", (int)((Value * 100) / _range)));
}
else
{
lblText.Text = "100%";
}
break;
}
case ProgressBarDisplayType.Progress:
{
lblText.Text = string.Format(_customText, (Value - Minimum));
break;
}
case ProgressBarDisplayType.Remain:
{
lblText.Text = string.Format(_customText, (Maximum - Value));
break;
}
case ProgressBarDisplayType.Value:
{
lblText.Text = string.Format(_customText, Value);
break;
}
}
}
public new void Increment(int value)
{
base.Increment(value);
UpdateText();
}
public new void PerformStep()
{
base.PerformStep();
UpdateText();
}
}
}
And the designer code:
namespace Utils.GUI
{
partial class InfoProgressBar
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lblText = new Utils.GUI.TransparentLabel();
this.SuspendLayout();
//
// lblText
//
this.lblText.BackColor = System.Drawing.Color.Transparent;
this.lblText.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblText.Location = new System.Drawing.Point(0, 0);
this.lblText.Name = "lblText";
this.lblText.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.lblText.Size = new System.Drawing.Size(100, 23);
this.lblText.TabIndex = 0;
this.lblText.Text = "transparentLabel1";
this.ResumeLayout(false);
}
#endregion
private TransparentLabel lblText;
}
}
Alliteratively you can try placing a Label control and placing it on top of the progress bar control. Then you can set whatever the text you want to the label. I haven't done this my self. If it works it should be a simpler solution than overriding onpaint.

Round shaped buttons

How do I make a button in a round shape rather than the conventional rectangle.
I am using winforms(2.0)
First make a class. Give it name: "RoundButton".
Then write the code directly as this:
using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Linq;
using System.Text;
namespace WindowsFormsApplication1
{
public class RoundButton : Button
{
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
GraphicsPath grPath = new GraphicsPath();
grPath.AddEllipse(0, 0, ClientSize.Width, ClientSize.Height);
this.Region = new System.Drawing.Region(grPath);
base.OnPaint(e);
}
}
}
Then, build your application and close this.
Now go to the toolbox and you will see a control named RoundButton.
Then drag and drop this on your Windows form and test it.
GraphicsPath p = new GraphicsPath();
p.AddEllipse(1, 1, button1.Width - 4, button1.Height - 4);
button1.Region = new Region(p);
Code project has many articles about these kinds of things, especially the article RoundButton Windows Control - Ever Decreasing Circles might be of interest since it shows you have to do different kinds of round buttons.
This or this could help if you need to implement your own Button class based on the default Windows Forms button. You can also search online for more examples.
What about 'GDI'?
Implementation Example:
#region <Round Corners> : (Properties)
// [Use Round Corners]
private bool useRoundCorners = false;
[Category("Control Corners"), DisplayName("Round Corners")]
[Description("Set Round Corners.")]
[Browsable(true)]
public bool UseRoundBorders
{
get { return useRoundCorners; }
set { if (useRoundCorners != value) { useRoundCorners = value; Invalidate(); } }
}
// [Ellipse Radius]
private int ellipseRadius = 20;
[Category("Control Corners"), DisplayName("Radius")]
[Description("Set Corner (Ellipse) Radius")]
[Browsable(true)]
public int EllipseRadius
{
get { return ellipseRadius.FixedValue(0, 90); }
set { if (ellipseRadius != value.FixedValue(0, 90)) { ellipseRadius = value; Invalidate(); } }
}
#endregion
#region <Round Corners> : (Draw)
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn
(
int nLeftRect, // x-coordinate of upper-left corner
int nTopRect, // y-coordinate of upper-left corner
int nRightRect, // x-coordinate of lower-right corner-
int nBottomRect, // y-coordinate of lower-right corner
int nWidthEllipse, // width of ellipse
int nHeightEllipse // height of ellipse
);
/// <summary> Draw Corners (Round or Square). </summary>
/// <param name="e"></param>
private void DrawCorners()
{
if (useRoundCorners) { this.Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, ellipseRadius, ellipseRadius)); }
else { this.Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 0, 0)); }
}
#endregion
/// <summary> Redraw (Update) the Control. </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawCorners();
}
Extension to Limit Round Corner Radius (*Range: 0-90)
Author: Salvador
// Extension to Set Min. & Max. Values to Properties
public static class Extensions
{
public static int FixedValue(this int value, int min, int max)
{
if (value >= min && value <= max) { return value; }
else if (value > max) { return max; }
else if (value < min) { return min; }
else { return 1; }
}
}
This is what you want
public class RoundButton : Control
{
private readonly Label lbl;
public RoundButton() : base()
{
lbl = new Label
{
Text = Text,
ForeColor = ForeColor,
BackColor = BackColor,
Font = Font
};
CenterInParent();
}
private void CenterInParent()
{
lbl.Left = (Width - lbl.Width) / 2;
lbl.Top = (Height - lbl.Height) / 2;
}
protected override void OnPaint(PaintEventArgs e)
{
GraphicsPath grPath = new GraphicsPath();
grPath.AddEllipse(0, 0, ClientSize.Width, ClientSize.Height);
Region = new Region(grPath);
base.OnPaint(e);
}
protected override void OnMove(EventArgs e)
{
CenterInParent();
base.OnMove(e);
}
protected override void OnTextChanged(EventArgs e)
{
lbl.Text = Text;
base.OnTextChanged(e);
}
protected override void OnForeColorChanged(EventArgs e)
{
lbl.ForeColor = ForeColor;
base.OnForeColorChanged(e);
}
protected override void OnBackColorChanged(EventArgs e)
{
lbl.BackColor = BackColor;
base.OnBackColorChanged(e);
}
protected override void OnFontChanged(EventArgs e)
{
lbl.Font = Font;
base.OnFontChanged(e);
}
}
Pros:
No cuts
Round
Cons:
Wrong designer loader for round button
public class OptionsMenu : Button
{
public OptionsMenu(int NoOfOptions, Point Location, ControlCollection controls,
Size ButtonSize, int DistanceBetweenOptions)
{
Button[] buttons = new Button[NoOfOptions];
for (int i = 0; i < NoOfOptions; i++)
{
buttons[i] = new Button()
{
Size = ButtonSize,
};
GraphicsPath p = new GraphicsPath();
p.AddEllipse(1, 1, buttons[i].Width - 4, buttons[i].Height - 4);
buttons[i].Region = new Region(p);
buttons[i].Location = new Point(Location.X, Location.Y + DistanceBetweenOptions * i);
controls.Add(buttons[i]);
}
}
}
You can call it like this:
OptionsMenu menu = new OptionsMenu(4, new Point(50, 50), Controls, new Size(20, 20), 40);
Controls.AddRange(new Control[]
{
menu
});

Categories