Here is my custom control which is a kind of simple progress bar:
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace eg.BJM.Controls
{
public class HorizontalBarGauge : Canvas
{
public HorizontalBarGauge()
{
IsSigned = true;
MinValue = -100;
MaxValue = 100;
Value = 25;
MarkerValue = -1;
BorderColor = Colors.Black;
MarkerColor = Colors.Red;
BarColor = Colors.SkyBlue;
BaseColor = Colors.DodgerBlue;
ValueColor = Colors.White;
}
#region Dependecy Properties
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(HorizontalBarGauge));
public static readonly DependencyProperty MinValueProperty =
DependencyProperty.Register("MinValue", typeof(int), typeof(HorizontalBarGauge));
public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.Register("MaxValue", typeof(int), typeof(HorizontalBarGauge));
public static readonly DependencyProperty MarkerValueProperty =
DependencyProperty.Register("MarkerValue", typeof(int), typeof(HorizontalBarGauge));
public static readonly DependencyProperty MarkerColorProperty =
DependencyProperty.Register("MarkerColor", typeof(Color), typeof(HorizontalBarGauge));
public static readonly DependencyProperty BaseColorProperty =
DependencyProperty.Register("BaseColor", typeof(Color), typeof(HorizontalBarGauge));
public static readonly DependencyProperty BarColorProperty =
DependencyProperty.Register("BarColor", typeof(Color), typeof(HorizontalBarGauge));
public static readonly DependencyProperty BorderColorProperty =
DependencyProperty.Register("BorderColor", typeof(Color), typeof(HorizontalBarGauge));
public static readonly DependencyProperty BorderThicknessProperty =
DependencyProperty.Register("BorderThickness", typeof(double), typeof(HorizontalBarGauge));
public static readonly DependencyProperty IsSignedProperty =
DependencyProperty.Register("IsSigned", typeof(bool), typeof(HorizontalBarGauge));
public static readonly DependencyProperty ShowValueProperty =
DependencyProperty.Register("ShowValue", typeof(bool), typeof(HorizontalBarGauge));
public static readonly DependencyProperty ValueColorProperty =
DependencyProperty.Register("ValueColor", typeof(Color), typeof(HorizontalBarGauge));
#endregion
[Description("Determines whether the bar shows [min,max] or [-max,+max]")]
public bool IsSigned
{
get { return (bool)GetValue( IsSignedProperty ); }
set
{
SetValue( IsSignedProperty, value );
if (value)
{
MinValue = -MaxValue;
}
else
{
MinValue = 0;
}
reassertValues();
InvalidateVisual();
}
}
[Description("Get/set the current value to show.")]
public int Value
{
get { return (int)GetValue(ValueProperty); }
set
{
SetValue( ValueProperty, value );
InvalidateVisual();
}
}
[Description("Get/set the minimum value to show.")]
public int MinValue
{
get { return (int)GetValue(MinValueProperty); }
set
{
SetValue(MinValueProperty, value);
reassertValues();
InvalidateVisual();
}
}
[Description("Get/set the maximum value to show.")]
public int MaxValue
{
get { return (int)GetValue(MaxValueProperty); }
set
{
value = Math.Max(value, 1);
SetValue(MaxValueProperty, value);
reassertValues();
InvalidateVisual();
}
}
[Description("Get/set the marker value to show. Negative => no marker.")]
public int MarkerValue
{
get { return (int)GetValue(MarkerValueProperty); }
set
{
SetValue(MaxValueProperty, value);
reassertValues();
InvalidateVisual();
}
}
[Description("Get/set the color of the marker.")]
public Color MarkerColor
{
get { return (Color)GetValue(MarkerColorProperty); }
set
{
SetValue(MarkerColorProperty, value);
Brush penBrush = new SolidColorBrush( value );
_markerPen = new Pen( penBrush, 3 );
}
}
[Description("Get/set the value text color.")]
public Color ValueColor
{
get { return (Color)GetValue(ValueColorProperty); }
set
{
SetValue(ValueColorProperty, value);
_textBrush = new SolidColorBrush(value);
}
}
[Description("Get/set the base color.")]
public Color BaseColor
{
get { return (Color)GetValue(BaseColorProperty); }
set
{
SetValue(BaseColorProperty, value);
_bodyBrush = new SolidColorBrush( value );
}
}
[Description("Get/set the bar color.")]
public Color BarColor
{
get { return (Color)GetValue(BarColorProperty); }
set
{
SetValue(BarColorProperty, value);
_barBrush = new SolidColorBrush( value );
}
}
[Description("Get/set the border color.")]
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set
{
SetValue(BorderColorProperty, value);
var brush = new SolidColorBrush( value );
_borderPen = new Pen( brush, BorderThickness );
}
}
[Description("Get/set the border thickness.")]
public double BorderThickness
{
get { return (double)GetValue(BorderThicknessProperty); }
set
{
SetValue(BorderThicknessProperty, value);
var brush = new SolidColorBrush( BorderColor );
_borderPen = new Pen( brush, value );
}
}
[Description("Get/set whether values are displayed in the control.")]
public bool ShowValue
{
get { return (bool)GetValue( ShowValueProperty ); }
set { SetValue( ShowValueProperty, value );}
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
// Background.
drawingContext.DrawRectangle(_bodyBrush, null, _rectangle);
// Bar.
int value = Value;
if (value != 0)
{
Rect barRect;
if (IsSigned)
{
double frac = Convert.ToDouble(value)/Convert.ToDouble(MaxValue*2);
if (value < 0)
{
barRect = new Rect(_rectangle.Width/2-frac*_rectangle.Width, 0, _rectangle.Width/2, _rectangle.Height);
}
else
{
barRect = new Rect( _rectangle.Width/2, 0, frac*_rectangle.Width, _rectangle.Height );
}
drawingContext.DrawRectangle( _barBrush, null, barRect );
}
else
{
double x = (_rectangle.Width*value)/(MaxValue - MinValue);
if (x > 0)
{
barRect = new Rect( 0, 0, x, _rectangle.Height );
drawingContext.DrawRectangle( _barBrush, null, barRect );
}
}
}
// Marker.
if (MarkerValue >= 0)
{
if (IsSigned)
{
double frac = Convert.ToDouble( MarkerValue )/Convert.ToDouble( MaxValue );
double markerX1 = 0.5*_rectangle.Width*(1 + frac);
Point p0 = new Point( markerX1, 0 );
Point p1 = new Point( markerX1, _rectangle.Height );
drawingContext.DrawLine( _markerPen, p0, p1 );
double markerX2 = 0.5*_rectangle.Width*(1 - frac);
Point p2 = new Point( markerX2, 0 );
Point p3 = new Point( markerX2, _rectangle.Height );
drawingContext.DrawLine( _markerPen, p2, p3 );
}
else
{
double markerX = (_rectangle.Width*MarkerValue)/(MaxValue - MinValue);
Point p0 = new Point( markerX, 0 );
Point p1 = new Point( markerX, _rectangle.Height );
drawingContext.DrawLine( _markerPen, p0, p1 );
}
}
// Border.
drawingContext.DrawRectangle(null, _borderPen, _rectangle);
// Text.
if (ShowValue)
{
FormattedText text = new FormattedText( value.ToString(), CultureInfo.CurrentCulture,
FlowDirection.LeftToRight, s_typeface, 12, _textBrush );
Point pos = new Point( (_rectangle.Width - text.Width)/2, (_rectangle.Height - text.Height)/2 );
drawingContext.DrawText( text, pos );
}
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
_rectangle = new Rect( sizeInfo.NewSize );
InvalidateVisual();
}
private int clamp(int value)
{
return (value < MinValue ? MinValue : value > MaxValue ? MaxValue : value);
}
private void reassertValues()
{
// Make sure max and min are correct.
int oldMin = MinValue;
int newMin = oldMin;
int oldMax = MaxValue;
int newMax = oldMax;
if (oldMin > oldMax)
{
newMin = oldMax;
newMax = oldMin;
}
if (newMin != oldMin)
{
SetValue(MinValueProperty, newMin);
}
if (newMax != oldMax)
{
SetValue(MaxValueProperty, newMax);
}
// Make sure value is in [min,max].
int oldValue = Value;
int newValue = clamp(oldValue);
if (newValue != oldValue)
{
Value = newValue;
}
// Make sure marker is correctly placed.
int marker = MarkerValue;
if (marker >= 0)
{
int newMarker = clamp(marker);
if (newMarker != marker)
{
SetValue(MarkerValueProperty, newMarker);
}
}
}
private Pen _borderPen;
private Pen _markerPen;
private Brush _barBrush;
private Brush _bodyBrush;
private Rect _rectangle;
private Brush _textBrush;
private static readonly Typeface s_typeface = new Typeface( "Verdana" );
}
}
Here is the XAML which binds to it:
<controls:HorizontalBarGauge Grid.Row="1" Grid.Column="1"
Margin="8" Width="130" Height="18"
IsSigned="False"
MinValue="0"
MaxValue="100"
BorderThickness="2"
BorderColor="MidnightBlue"
ShowValue="True"
Value="{Binding StepperPosition}"
/>
When the controls initialize, the control shows the correct value. But when I change the "StepperPosition" property in later code, nothing changes. Breakpointing the code shows the ValueProperty setter does not get called even though the StepperPosition does and fires the OnPropertyChanged( "StepperPosition" ). The INPC is in a base class. So I think I've ruled out the usual causes of the binding not refreshing, and am now a bit stumped.
Monday. Right, moved on a bit. Can now refresh the visuals by hooking in to the DepedencyPropertyChanged callbacks. #HighCore this is what they look like:
The bar has two modes: speed (which needs to be +/-) and position (which is always +ve). The red line in the speed gauge shows a max speed set elsewhere. They are updated in near-real-time as they are indicators for stepper motor state.
Related
I noticed when I resize an element in Visual Studio the Splitter Line is painted in a solid transparent black like this:
However in my own Winforms application I get this resize line:
I am wondering how I can change the painting of this resize line?
If you take a look at Splitter source code, you will see drawing of the highlight is performed in the DrawSplitHelper private method.
Since splitter methods are tightly deppending on private members of the control, overriding members or handling events of the control which use this method doesn't provides such transparent highlight simply. Also if you decide to darw a solid highlight, you can not have a filcker-free drawing because of the halftone highlight which the control draws in private methods.
The idea of showing a transparent highlight is based on showing a top-most semi-transparent window which doesn't activate on showing. Also you can use PatBlt method to draw a reversible rectangle using a brush.
As an option I started by Splitter.cs and changed it to draw desired transparent highlight as you see in image below:
Code
As an option I started by Splitter.cs and changed it to draw desired transparent highlight. I used a Form to show transparent highlight. I showed the form without activating, as top-most and with a suitable opacity.
Probably you can create a splitter using less code, but I preferred to use source code of Splitter instead of writing my own splitter. Thanks to Microsoft for sharing source code.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Drawing.Drawing2D;
using System.Reflection;
[ComVisible(true)]
[DefaultEvent("SplitterMoved")]
[DefaultProperty("Dock")]
[Designer(typeof(MySplitterDesigner))]
public class MySplitter : Control
{
private Point anchor = Point.Empty;
private System.Windows.Forms.BorderStyle borderStyle;
private const int defaultWidth = 3;
private const int DRAW_END = 3;
private const int DRAW_MOVE = 2;
private const int DRAW_START = 1;
private static readonly object EVENT_MOVED = new object();
private static readonly object EVENT_MOVING = new object();
private int initTargetSize;
private int lastDrawSplit = -1;
private int maxSize;
private int minExtra = 25;
private int minSize = 25;
private int splitSize = -1;
private Control splitTarget;
private SplitterMessageFilter splitterMessageFilter;
private int splitterThickness = 3;
[Category("Behavior")]
[Description("Occurs when the splitter is done being moved.")]
public event SplitterEventHandler SplitterMoved
{
add
{
base.Events.AddHandler(EVENT_MOVED, value);
}
remove
{
base.Events.RemoveHandler(EVENT_MOVED, value);
}
}
[Category("Behavior")]
[Description("Occurs when the splitter is being moved.")]
public event SplitterEventHandler SplitterMoving
{
add
{
base.Events.AddHandler(EVENT_MOVING, value);
}
remove
{
base.Events.RemoveHandler(EVENT_MOVING, value);
}
}
HighLight highlight;
public MySplitter()
{
base.SetStyle(ControlStyles.Selectable, false);
this.TabStop = false;
this.minSize = 0x19;
this.minExtra = 0x19;
this.Dock = DockStyle.Left;
highlight = new HighLight();
}
private void ApplySplitPosition()
{
this.SplitPosition = this.splitSize;
}
private SplitData CalcSplitBounds()
{
SplitData data = new SplitData();
Control control = this.FindTarget();
data.target = control;
if (control != null)
{
switch (control.Dock)
{
case DockStyle.Top:
case DockStyle.Bottom:
this.initTargetSize = control.Bounds.Height;
break;
case DockStyle.Left:
case DockStyle.Right:
this.initTargetSize = control.Bounds.Width;
break;
}
Control parentInternal = this.Parent;
Control.ControlCollection controls = parentInternal.Controls;
int count = controls.Count;
int num2 = 0;
int num3 = 0;
for (int i = 0; i < count; i++)
{
Control control3 = controls[i];
if (control3 != control)
{
switch (control3.Dock)
{
case DockStyle.Top:
case DockStyle.Bottom:
num3 += control3.Height;
break;
case DockStyle.Left:
case DockStyle.Right:
num2 += control3.Width;
break;
}
}
}
Size clientSize = parentInternal.ClientSize;
if (this.Horizontal)
{
this.maxSize = (clientSize.Width - num2) - this.minExtra;
}
else
{
this.maxSize = (clientSize.Height - num3) - this.minExtra;
}
data.dockWidth = num2;
data.dockHeight = num3;
}
return data;
}
private Rectangle CalcSplitLine(int splitSize, int minWeight)
{
Rectangle bounds = base.Bounds;
Rectangle rectangle2 = this.splitTarget.Bounds;
switch (this.Dock)
{
case DockStyle.Top:
if (bounds.Height < minWeight)
{
bounds.Height = minWeight;
}
bounds.Y = rectangle2.Y + splitSize;
return bounds;
case DockStyle.Bottom:
if (bounds.Height < minWeight)
{
bounds.Height = minWeight;
}
bounds.Y = ((rectangle2.Y + rectangle2.Height) - splitSize) - bounds.Height;
return bounds;
case DockStyle.Left:
if (bounds.Width < minWeight)
{
bounds.Width = minWeight;
}
bounds.X = rectangle2.X + splitSize;
return bounds;
case DockStyle.Right:
if (bounds.Width < minWeight)
{
bounds.Width = minWeight;
}
bounds.X = ((rectangle2.X + rectangle2.Width) - splitSize) - bounds.Width;
return bounds;
}
return bounds;
}
private int CalcSplitSize()
{
Control control = this.FindTarget();
if (control != null)
{
Rectangle bounds = control.Bounds;
switch (this.Dock)
{
case DockStyle.Top:
case DockStyle.Bottom:
return bounds.Height;
case DockStyle.Left:
case DockStyle.Right:
return bounds.Width;
}
}
return -1;
}
private void DrawSplitBar(int mode)
{
if ((mode != 1) && (this.lastDrawSplit != -1))
{
this.DrawSplitHelper(this.lastDrawSplit);
this.lastDrawSplit = -1;
}
else if ((mode != 1) && (this.lastDrawSplit == -1))
{
return;
}
if (mode != 3)
{
this.DrawSplitHelper(this.splitSize);
this.lastDrawSplit = this.splitSize;
}
else
{
if (this.lastDrawSplit != -1)
{
this.DrawSplitHelper(this.lastDrawSplit);
}
this.lastDrawSplit = -1;
highlight.Hide();
}
Console.WriteLine(mode);
}
private void DrawSplitHelper(int splitSize)
{
if (this.splitTarget != null)
{
Rectangle rectangle = this.CalcSplitLine(splitSize, 3);
var r = this.Parent.RectangleToScreen(rectangle);
if (!highlight.Visible)
highlight.ShowInactiveTopmost();
highlight.Location = r.Location;
highlight.Size = r.Size;
}
}
private Control FindTarget()
{
Control parentInternal = this.Parent;
if (parentInternal != null)
{
Control.ControlCollection controls = parentInternal.Controls;
int count = controls.Count;
DockStyle dock = this.Dock;
for (int i = 0; i < count; i++)
{
Control control2 = controls[i];
if (control2 != this)
{
switch (dock)
{
case DockStyle.Top:
if (control2.Bottom != base.Top)
{
break;
}
return control2;
case DockStyle.Bottom:
if (control2.Top != base.Bottom)
{
break;
}
return control2;
case DockStyle.Left:
if (control2.Right != base.Left)
{
break;
}
return control2;
case DockStyle.Right:
if (control2.Left != base.Right)
{
break;
}
return control2;
}
}
}
}
return null;
}
private int GetSplitSize(int x, int y)
{
int num;
if (this.Horizontal)
num = x - this.anchor.X;
else
num = y - this.anchor.Y;
int num2 = 0;
switch (this.Dock)
{
case DockStyle.Top:
num2 = this.splitTarget.Height + num;
break;
case DockStyle.Bottom:
num2 = this.splitTarget.Height - num;
break;
case DockStyle.Left:
num2 = this.splitTarget.Width + num;
break;
case DockStyle.Right:
num2 = this.splitTarget.Width - num;
break;
}
return Math.Max(Math.Min(num2, this.maxSize), this.minSize);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if ((this.splitTarget != null) && (e.KeyCode == Keys.Escape))
this.SplitEnd(false);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if ((e.Button == MouseButtons.Left) && (e.Clicks == 1))
this.SplitBegin(e.X, e.Y);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (this.splitTarget != null)
{
int x = e.X + base.Left;
int y = e.Y + base.Top;
Rectangle rectangle = this.CalcSplitLine(this.GetSplitSize(e.X, e.Y), 0);
int splitX = rectangle.X;
int splitY = rectangle.Y;
this.OnSplitterMoving(new SplitterEventArgs(x, y, splitX, splitY));
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (this.splitTarget != null)
{
Rectangle rectangle = this.CalcSplitLine(this.GetSplitSize(e.X, e.Y), 0);
this.SplitEnd(true);
}
}
protected virtual void OnSplitterMoved(SplitterEventArgs sevent)
{
SplitterEventHandler handler = (SplitterEventHandler)base.Events[EVENT_MOVED];
if (handler != null)
handler(this, sevent);
if (this.splitTarget != null)
this.SplitMove(sevent.SplitX, sevent.SplitY);
}
protected virtual void OnSplitterMoving(SplitterEventArgs sevent)
{
SplitterEventHandler handler = (SplitterEventHandler)base.Events[EVENT_MOVING];
if (handler != null)
handler(this, sevent);
if (this.splitTarget != null)
this.SplitMove(sevent.SplitX, sevent.SplitY);
}
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
if (this.Horizontal)
{
if (width < 1)
width = 3;
this.splitterThickness = width;
}
else
{
if (height < 1)
height = 3;
this.splitterThickness = height;
}
base.SetBoundsCore(x, y, width, height, specified);
}
private void SplitBegin(int x, int y)
{
SplitData data = this.CalcSplitBounds();
if ((data.target != null) && (this.minSize < this.maxSize))
{
this.anchor = new Point(x, y);
this.splitTarget = data.target;
this.splitSize = this.GetSplitSize(x, y);
try
{
if (this.splitterMessageFilter != null)
this.splitterMessageFilter = new SplitterMessageFilter(this);
Application.AddMessageFilter(this.splitterMessageFilter);
}
finally { }
this.Capture = true;
this.DrawSplitBar(1);
}
}
private void SplitEnd(bool accept)
{
this.DrawSplitBar(3);
this.splitTarget = null;
this.Capture = false;
if (this.splitterMessageFilter != null)
{
Application.RemoveMessageFilter(this.splitterMessageFilter);
this.splitterMessageFilter = null;
}
if (accept)
this.ApplySplitPosition();
else if (this.splitSize != this.initTargetSize)
this.SplitPosition = this.initTargetSize;
this.anchor = Point.Empty;
}
private void SplitMove(int x, int y)
{
int splitSize = this.GetSplitSize((x - base.Left) + this.anchor.X, (y - base.Top) + this.anchor.Y);
if (this.splitSize != splitSize)
{
this.splitSize = splitSize;
this.DrawSplitBar(2);
}
}
public override string ToString()
{
string str = base.ToString();
string[] textArray1 = new string[] { str, ", MinExtra: ", this.MinExtra.ToString(CultureInfo.CurrentCulture), ", MinSize: ", this.MinSize.ToString(CultureInfo.CurrentCulture) };
return string.Concat(textArray1);
}
[DefaultValue(0)]
[Category("Appearance")]
[Description("The border type of the control.")]
public System.Windows.Forms.BorderStyle BorderStyle
{
get
{
return this.borderStyle;
}
set
{
if (!IsEnumValid(value, (int)value, 0, 2))
throw new InvalidEnumArgumentException("value", (int)value, typeof(System.Windows.Forms.BorderStyle));
if (this.borderStyle != value)
{
this.borderStyle = value;
base.UpdateStyles();
}
}
}
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams createParams = base.CreateParams;
createParams.ExStyle &= -513;
createParams.Style &= -8388609;
System.Windows.Forms.BorderStyle borderStyle = this.borderStyle;
if (borderStyle != System.Windows.Forms.BorderStyle.FixedSingle)
{
if (borderStyle == System.Windows.Forms.BorderStyle.Fixed3D)
{
createParams.ExStyle |= 0x200;
}
return createParams;
}
createParams.Style |= 0x800000;
return createParams;
}
}
protected override Cursor DefaultCursor
{
get
{
switch (this.Dock)
{
case DockStyle.Top:
case DockStyle.Bottom:
return Cursors.HSplit;
case DockStyle.Left:
case DockStyle.Right:
return Cursors.VSplit;
}
return base.DefaultCursor;
}
}
protected override System.Windows.Forms.ImeMode DefaultImeMode
{
get
{
return System.Windows.Forms.ImeMode.Disable;
}
}
protected override Size DefaultSize
{
get { return new Size(3, 3); }
}
[Localizable(true), DefaultValue(3)]
public override DockStyle Dock
{
get { return base.Dock; }
set
{
if (((value != DockStyle.Top) && (value != DockStyle.Bottom)) && ((value != DockStyle.Left) && (value != DockStyle.Right)))
throw new ArgumentException("Splitter control must be docked left, right, top, or bottom.");
int splitterThickness = this.splitterThickness;
base.Dock = value;
switch (this.Dock)
{
case DockStyle.Top:
case DockStyle.Bottom:
if (this.splitterThickness == -1)
break;
base.Height = splitterThickness;
return;
case DockStyle.Left:
case DockStyle.Right:
if (this.splitterThickness != -1)
base.Width = splitterThickness;
break;
default:
return;
}
}
}
private bool Horizontal
{
get
{
DockStyle dock = this.Dock;
if (dock != DockStyle.Left)
return (dock == DockStyle.Right);
return true;
}
}
[Category("Behavior")]
[Localizable(true)]
[DefaultValue(25)]
[Description("Specifies the minimum size of the undocked area.")]
public int MinExtra
{
get { return this.minExtra; }
set
{
if (value < 0)
value = 0;
this.minExtra = value;
}
}
[Category("Behavior")]
[Localizable(true)]
[DefaultValue(25)]
[Description("Specifies the minimum size of the control being resized.")]
public int MinSize
{
get { return this.minSize; }
set
{
if (value < 0)
value = 0;
this.minSize = value;
}
}
[Category("Layout")]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Description("The current position of the splitter, or -1 if it is not bound to a control.")]
public int SplitPosition
{
get
{
if (this.splitSize == -1)
{
this.splitSize = this.CalcSplitSize();
}
return this.splitSize;
}
set
{
SplitData data = this.CalcSplitBounds();
if (value > this.maxSize)
{
value = this.maxSize;
}
if (value < this.minSize)
{
value = this.minSize;
}
this.splitSize = value;
this.DrawSplitBar(3);
if (data.target == null)
{
this.splitSize = -1;
}
else
{
Rectangle bounds = data.target.Bounds;
switch (this.Dock)
{
case DockStyle.Top:
bounds.Height = value;
break;
case DockStyle.Bottom:
bounds.Y += bounds.Height - this.splitSize;
bounds.Height = value;
break;
case DockStyle.Left:
bounds.Width = value;
break;
case DockStyle.Right:
bounds.X += bounds.Width - this.splitSize;
bounds.Width = value;
break;
}
data.target.Bounds = bounds;
Application.DoEvents();
this.OnSplitterMoved(new SplitterEventArgs(base.Left, base.Top, base.Left + (bounds.Width / 2), base.Top + (bounds.Height / 2)));
}
}
}
private class SplitData
{
public int dockHeight = -1;
public int dockWidth = -1;
internal Control target;
}
private class SplitterMessageFilter : IMessageFilter
{
private MySplitter owner;
public SplitterMessageFilter(MySplitter splitter)
{
this.owner = splitter;
}
public bool PreFilterMessage(ref Message m)
{
if ((m.Msg < 0x100) || (m.Msg > 0x108))
{
return false;
}
if ((m.Msg == 0x100) && (((int)((long)m.WParam)) == 0x1b))
{
this.owner.SplitEnd(false);
}
return true;
}
}
private static bool IsEnumValid(Enum enumValue, int value, int minValue, int maxValue)
{
return ((value >= minValue) && (value <= maxValue));
}
}
public class MySplitterDesigner : ControlDesigner
{
public MySplitterDesigner() { base.AutoResizeHandles = true; }
private void DrawBorder(Graphics graphics)
{
Color white;
Control control = this.Control;
Rectangle clientRectangle = control.ClientRectangle;
if (control.BackColor.GetBrightness() < 0.5)
white = Color.White;
else
white = Color.Black;
using (Pen pen = new Pen(white))
{
pen.DashStyle = DashStyle.Dash;
clientRectangle.Width--;
clientRectangle.Height--;
graphics.DrawRectangle(pen, clientRectangle);
}
}
protected override void OnPaintAdornments(PaintEventArgs pe)
{
base.OnPaintAdornments(pe);
if (((MySplitter)base.Component).BorderStyle == BorderStyle.None)
this.DrawBorder(pe.Graphics);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x47)
this.Control.Invalidate();
base.WndProc(ref m);
}
}
Here is highlight form:
public class HighLight : Form
{
public HighLight()
{
FormBorderStyle = FormBorderStyle.None;
BackColor = Color.Black;
Opacity = 0;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
}
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
this.Hide();
}
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(int hWnd, int hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public void ShowInactiveTopmost()
{
ShowWindow(this.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(this.Handle.ToInt32(), HWND_TOPMOST,
this.Left, this.Top, this.Width, this.Height,
SWP_NOACTIVATE);
this.Opacity = 0.3;
}
}
The shape is not drawing correctly or not at all - that is, if I click towards the top left of the canvas, it will draw an ellipse but not anywhere else like this. Is the code logically correct? Everything seems good but perhaps I am missing something.
/* in mainwindow.xaml */
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (btnNode.IsChecked == true)
{
CreateNode(e.GetPosition(Canvas));
}
}
private void CreateNode(Point origin)
{
Node n = new Node("new_" + cmbStart.Items.Count, origin) { Fill = new SolidColorBrush(Colors.Tomato), Width = 60, Height = 60 };
Canvas.Children.Add(n);
Canvas.SetLeft(n, origin.X - n.Width/2);
Canvas.SetTop(n, origin.Y - n.Height/2);
}
/* in Node class */
private void Init(Point p)
{
X = p.X;
Y = p.Y;
}
public double Y
{
get { return (double)this.GetValue(YProperty); }
set { this.SetValue(YProperty, value); }
}
public double X
{
get { return (double) this.GetValue(XProperty); }
set { this.SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register("X", typeof(double), typeof(Node), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty YProperty = DependencyProperty.Register("Y", typeof(double), typeof(Node), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
protected override Geometry DefiningGeometry
{
get
{
Console.WriteLine("rendering at: " + this.X + ", " + this.Y + "---Scale: " + Width);
return new EllipseGeometry(new Point(this.X, this.Y), Width, Height);
}
}
The EllipseGeometry constructor has the radiusX and radiusY parameters to pass radii, not diameters.
If you want to draw a full ellipse into the bounds of your custom control, you should use Width/2 and Height/2 as parameter values:
protected override Geometry DefiningGeometry
{
get
{
return new EllipseGeometry(new Point(X, Y), Width / 2, Height / 2);
}
}
I am learning to make a small variant of chess game using windows forms C#, the game includes only the pawns of both sides, i have drew the board and organized the pieces on there places, but i honestly do not know how to start implementing the moves by clicking the mouse on the piece and then the location where i want to move it.
as references the black pawn is named piece, and white pawn is named pieceW
here is my code for the board
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AIchess
{
public partial class Form1 : Form
{
static System.Drawing.Bitmap piece = AIchess.Properties.Resources.piece;
ChessPiece Piece = new ChessPiece(piece, ChessColor.Black);
static System.Drawing.Bitmap pieceW = AIchess.Properties.Resources.pieceW;
ChessPiece PieceW = new ChessPiece(pieceW, ChessColor.White);
Square[,] square = new Square[8, 8];
public Form1()
{
InitializeComponent();
int i, j;
for (i = 0; i < 8; i++)
{
for (j = 0; j < 8; j++)
{
this.square[i, j] = new Square();
this.square[i, j].BackColor = System.Drawing.SystemColors.ActiveCaption;
this.square[i, j].BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.square[i, j].Location = new System.Drawing.Point(57 + i * 60, 109 + j * 60);
this.square[i, j].Name = i.ToString()+j.ToString();
this.square[i, j].Size = new System.Drawing.Size(60, 60);
this.square[i, j].TabIndex = 2;
this.square[i, j].TabStop = false;
this.Controls.Add(this.square[i, j]);
if (j == 1)
{
this.square[i, j].Image = piece;
this.square[i, j].AllocatedBy = "black";
}
if (j == 6)
{
this.square[i, j].Image = pieceW;
this.square[i, j].AllocatedBy = "white";
}
if (((i+j) % 2) ==0)
this.square[i, j].BackColor = Color.RoyalBlue;
else
this.square[i, j].BackColor = Color.LightBlue;
}
}
}
}
public enum ChessColor
{
White,
Black,
};
class ChessPiece
{
private Image DisplayedImage;
private ChessColor DisplayedColor;
private Point CurrentSquare;
public ChessPiece(Image image, ChessColor color)
{
DisplayedImage = image;
DisplayedColor = color;
}
}
class Square:PictureBox
{
private bool color;
public string AllocatedBy;
}
}
Here's a really simple implementation, I hope you won't mind that I did it from scratch.
Obviously it's very simple, there's no drag and drop and no animation but it fulfills your requirement.
I'll go through each part and explain them
InitializeGame
There you do set your images dimensions (they should be identical obviously)
You add in the dictionary the relationship between piece type/color and your bitmap
Note : the grid will be scaled so you can throw any size of bitmap you like
CreateBoard, DrawGame, DrawPieces
Nothing exceptional in there, note that for keeping things simple I do that every time a user clicks but it shouldn't be much of an issue, it's not Crysis after all :D
PickOrDropPiece
This is the logic where picking/dropping happens, it's really trivial and I'll let you take a look by yourself.
Differences between your code
I've created a Board type which holds the pieces and that you can easily update.
Note : do not remove the equality members in Piece they are here to help the dictionary.
Make sure to use 32-bit bitmaps with transparent borders
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += pictureBox1_MouseDown;
}
#region Properties
private Board Board { get; set; }
private Piece CurrentPiece { get; set; }
private Dictionary<Piece, Bitmap> PieceBitmaps { get; set; }
private int TileWidth { get; set; }
private int TileHeight { get; set; }
#endregion
#region Events
private void Form1_Load(object sender, EventArgs e)
{
InitializeGame();
DrawGame();
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
PickOrDropPiece(e);
DrawGame();
}
#endregion
#region Methods
private void InitializeGame()
{
TileWidth = 64;
TileHeight = 64;
Board = new Board();
PieceBitmaps = new Dictionary<Piece, Bitmap>();
PieceBitmaps.Add(new Piece(PieceType.Pawn, PieceColor.Black), new Bitmap("pawnblack.png"));
PieceBitmaps.Add(new Piece(PieceType.Pawn, PieceColor.White), new Bitmap("pawnwhite.png"));
}
private void DrawGame()
{
var tileSize = new Size(TileWidth, TileHeight);
Bitmap bitmap = CreateBoard(tileSize);
DrawPieces(bitmap);
pictureBox1.Image = bitmap;
}
private Bitmap CreateBoard(Size tileSize)
{
int tileWidth = tileSize.Width;
int tileHeight = tileSize.Height;
var bitmap = new Bitmap(tileWidth*8, tileHeight*8);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
Brush brush = (x%2 == 0 && y%2 == 0) || (x%2 != 0 && y%2 != 0) ? Brushes.Black : Brushes.White;
graphics.FillRectangle(brush, new Rectangle(x*tileWidth, y*tileHeight, tileWidth, tileHeight));
}
}
}
return bitmap;
}
private void DrawPieces(Bitmap bitmap)
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
Board board = Board;
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
Piece piece = board.GetPiece(x, y);
if (piece != null)
{
Bitmap bitmap1 = PieceBitmaps[piece];
graphics.DrawImageUnscaled(bitmap1, new Point(x*TileWidth, y*TileHeight));
}
}
}
}
}
private void PickOrDropPiece(MouseEventArgs e)
{
Point location = e.Location;
int x = location.X/TileWidth;
int y = location.Y/TileHeight;
bool pickOrDrop = CurrentPiece == null;
if (pickOrDrop)
{
// Pick a piece
Piece piece = Board.GetPiece(x, y);
Board.SetPiece(x, y, null);
if (piece != null)
{
label1.Text = string.Format("You picked a {0} {1} at location {2},{3}", piece.Color, piece.Type, x,
y);
}
else
{
label1.Text = "Nothing there !";
}
CurrentPiece = piece;
}
else
{
// Drop picked piece
Board.SetPiece(x, y, CurrentPiece);
label1.Text = string.Format("You dropped a {0} {1} at location {2},{3}", CurrentPiece.Color,
CurrentPiece.Type, x,
y);
CurrentPiece = null;
}
}
#endregion
}
public class Board
{
private readonly Piece[] _pieces;
public Board()
{
_pieces = new Piece[8*8];
PopulatePieces();
}
public Piece GetPiece(int x, int y)
{
int i = y*8 + x;
return _pieces[i];
}
public void SetPiece(int x, int y, Piece piece)
{
int i = y*8 + x;
_pieces[i] = piece;
}
private void PopulatePieces()
{
for (int i = 0; i < 8; i++)
{
SetPiece(i, 1, new Piece(PieceType.Pawn, PieceColor.Black));
SetPiece(i, 7, new Piece(PieceType.Pawn, PieceColor.White));
}
}
}
public class Piece
{
private readonly PieceColor _color;
private readonly PieceType _type;
public Piece(PieceType type, PieceColor color)
{
_type = type;
_color = color;
}
public PieceType Type
{
get { return _type; }
}
public PieceColor Color
{
get { return _color; }
}
protected bool Equals(Piece other)
{
return _color == other._color && _type == other._type;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((Piece) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((int) _color*397) ^ (int) _type;
}
}
public static bool operator ==(Piece left, Piece right)
{
return Equals(left, right);
}
public static bool operator !=(Piece left, Piece right)
{
return !Equals(left, right);
}
}
public enum PieceType
{
Pawn
}
public enum PieceColor
{
Black,
White
}
}
I am trying to create a custom wedge shape class in WPF, derived from the abstract Shape class, and be able to define it in XAML just like any other shape.
I've been searching on Google for a complete tutorial on how to do this but all I am finding is stuff on custom controls. What I want is to create a wedge class that allows me to specify inner radius, outer radius, how many sections out of 360 degrees this wedge will be one of (i.e., if I want to fit 24 of these wedges around the circle, this wedge will be the right size to be one of those 24), and its position (which one of those 24 spaces it will occupy). These are all dependency properties, and I've registered them.
The DefiningGeometry property calls a method that does all the logic for calculating points and drawing the shape.
The problem I'm running into is that VS2010 created a style automatically with a setter of property "Template". Then, when I compile, it gives me an error saying:
"Error 3 Cannot find the Style Property 'Template' on the type 'WpfApplication1.Wedge'. Line 8 Position 17. C:\Users\rflint\Desktop\WpfApplication1\WpfApplication1\Themes\Generic.xaml 8 17 WpfApplication1"
If I comment this out everything compiles but the wedge is not shown on the form. How do I implement this Template setter property? Do I need to?
XAML:
<my:Wedge CenterPoint="300,300" InnerRadius="100" OuterRadius="200" Sections="12" Position="0" Stroke="Transparent" Fill="#FFCC7329" />
C#:
protected override Geometry DefiningGeometry
{
get
{
using (StreamGeometryContext context = geometry.Open())
{
DrawWedgeGeometry(context);
}
return geometry;
}
}
private void DrawWedgeGeometry(StreamGeometryContext context)
{
double wedgeAngle = 360/Sections;
double angleA = (Position * wedgeAngle) + (wedgeAngle/2);
double angleB = (Position * wedgeAngle) - (wedgeAngle/2);
Point point1 = getPointOnCircle(CenterPoint, InnerRadius, angleA);
Point point2 = getPointOnCircle(CenterPoint, InnerRadius, angleB);
Point point3 = getPointOnCircle(CenterPoint, OuterRadius, angleB);
Point point4 = getPointOnCircle(CenterPoint, OuterRadius, angleA);
Size innerSize = new Size(InnerRadius, InnerRadius);
Size outerSize = new Size(OuterRadius, OuterRadius);
context.BeginFigure(point1, true, true);
context.ArcTo(point2, innerSize, 90, false, SweepDirection.Clockwise, true, true);
context.LineTo(point3, true, true);
context.ArcTo(point4, outerSize, 90, false, SweepDirection.Counterclockwise, true, true);
}
I've just tried it on VS2012 and it works fine, at least with a simple ellipse geometry:
public sealed class Wedge : Shape
{
public Double Radius
{
get { return (Double)this.GetValue(RadiusProperty); }
set { this.SetValue(RadiusProperty, value); }
}
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register(
"Radius", typeof(Double), typeof(Wedge), new PropertyMetadata(0.0));
protected override Geometry DefiningGeometry
{
get {return new EllipseGeometry(new Point(0, 0), this.Radius, this.Radius); }
}
}
And the XAML:
<local:Wedge Radius="50" Stroke="Black" Fill="Yellow" StrokeThickness="2" Canvas.Top="100" Canvas.Left="100" />
Let me give a simple solution to your problem:
public class Wedge : Shape
{
public Double StartAngle
{
get { return (Double)GetValue(StartAngleProperty); }
set { SetValue(StartAngleProperty, value); }
}
public static readonly DependencyProperty StartAngleProperty =
DependencyProperty.Register("StartAngle", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));
public Double EndAngle
{
get { return (Double)GetValue(EndAngleProperty); }
set { SetValue(EndAngleProperty, value); }
}
public static readonly DependencyProperty EndAngleProperty =
DependencyProperty.Register("EndAngle", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));
public Point Center
{
get { return (Point)GetValue(CenterProperty); }
set { SetValue(CenterProperty, value); }
}
public static readonly DependencyProperty CenterProperty =
DependencyProperty.Register("Center", typeof(Point), typeof(Wedge), new PropertyMetadata(new Point()));
public Double InnerRadius
{
get { return (Double)GetValue(InnerRadiusProperty); }
set { SetValue(InnerRadiusProperty, value); }
}
public static readonly DependencyProperty InnerRadiusProperty =
DependencyProperty.Register("InnerRadius", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));
public Double OuterRadius
{
get { return (Double)GetValue(OuterRadiusProperty); }
set { SetValue(OuterRadiusProperty, value); }
}
public static readonly DependencyProperty OuterRadiusProperty =
DependencyProperty.Register("OuterRadius", typeof(Double), typeof(Wedge), new PropertyMetadata(0d));
protected override Geometry DefiningGeometry
{
get
{
StreamGeometry geometry = new StreamGeometry();
using (StreamGeometryContext context = geometry.Open())
{
Draw(context);
}
return geometry;
}
}
private void Draw(StreamGeometryContext context)
{
var isStroked = Stroke != null & Stroke != Brushes.Transparent & StrokeThickness > 0;
var isFilled = Fill != null & Fill != Brushes.Transparent;
context.BeginFigure(
GetPointOnCircle(Center, OuterRadius, StartAngle),
isFilled,
true);
context.ArcTo(
GetPointOnCircle(Center, OuterRadius, EndAngle),
new Size(OuterRadius, OuterRadius),
0,
EndAngle - StartAngle > 180,
SweepDirection.Clockwise,
isStroked,
true);
context.LineTo(GetPointOnCircle(Center, InnerRadius, EndAngle), isStroked, true);
context.ArcTo(
GetPointOnCircle(Center, InnerRadius, StartAngle),
new Size(InnerRadius, InnerRadius),
0,
EndAngle - StartAngle > 180,
SweepDirection.Counterclockwise,
isStroked,
true);
context.LineTo(GetPointOnCircle(Center, OuterRadius, StartAngle), isStroked, true);
}
private Point GetPointOnCircle(Point center, double radius, double angle)
{
var px = center.X + radius * Math.Cos(ToRadians(angle));
var py = center.Y + radius * Math.Sin(ToRadians(angle));
return new Point(px, py);
}
public double ToRadians(double angle)
{
return angle * Math.PI / 180;
}
}
So I created this class Sprite.cs:
class Sprite : INotifyPropertyChanged
{
double _Speed;
RectangleGeometry _SpriteRectangleGeometry;
Path _SpritePath;
public Sprite()
{
_SpriteRectangleGeometry = new RectangleGeometry();
_SpriteRectangleGeometry.Rect = new Rect(0, 0, 50, 50);
Speed = 50;
_SpritePath = new Path();
Color = Brushes.Black;
_SpritePath.Data = _SpriteRectangleGeometry;
}
public Sprite(double xpos, double ypos, double height, double width, double speed, SolidColorBrush color)
{
_SpriteRectangleGeometry = new RectangleGeometry();
_SpriteRectangleGeometry.Rect = new Rect(xpos, ypos, width, height);
this.Speed = speed;
_SpritePath = new Path();
this.Color = color;
_SpritePath.Data = _SpriteRectangleGeometry;
}
public double XPos
{
get { return _SpriteRectangleGeometry.Rect.X; }
set
{
_SpriteRectangleGeometry.Rect = new Rect(value, YPos, Width, Height);
//Notify the binding that the value has changed.
this.OnPropertyChanged("XPos");
}
}
public double YPos
{
get { return _SpriteRectangleGeometry.Rect.Y; }
set
{
_SpriteRectangleGeometry.Rect = new Rect(XPos, value, Width, Height);
//Notify the binding that the value has changed.
this.OnPropertyChanged("YPos");
}
}
public double Speed
{
get { return _Speed; }
set { _Speed = value; }
}
public double Width
{
get { return _SpriteRectangleGeometry.Rect.Width; }
set { _SpriteRectangleGeometry.Rect = new Rect(XPos, YPos, value, Height); }
}
public double Height
{
get { return _SpriteRectangleGeometry.Rect.Height; }
set { _SpriteRectangleGeometry.Rect = new Rect(XPos, YPos, Width, value); }
}
public SolidColorBrush Color
{
get { return (SolidColorBrush)_SpritePath.Fill; }
set { _SpritePath.Fill = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string strPropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
}
}
What I want to do now is add an instance of Sprite to the Xaml, but when i do i get this error:
The value of type 'Sprite" cannot be added to collection or dictionary of type UIElementCollection
Any advice?
The Sprite should derive from the UIElement class to be added to UIElementCollection. Also you could wrap it with ContentControl and provide a DataTemplate which would create some UIElement for your sprite object.
You have to add it to the resources section rather than just inline (and make sure it has a key)
<src:Sprite x:Key="data"/>
You also need to have declared your namespace at the top of the file