I want to draw the Property value in my PropertyGrid in a similar fashion as owner-drawing items in a ListView (Details), and other controls.
If a Property is declared of type Color, its value is drawn with a swatch of the color next to a string description. If a Property is a type of Image, a thumbnail of the image is drawn next to a string description.
I have a property that is a class that contains three Properties of type Color. I want to draw all three colors in the PropertyGrid next to the Property name. The class has an ExpandableObjectConverter as the TypeConverter, where the colors are edited, but the only option I know of for changing how the Property's value is displayed is to use a TypeConverter to return a String.
You need to create a editor for your type (which has 3 color properties) by deriving from UITypeEditor and overriding its GetPaintValueSupported and PaintValue. Than register the editor for your class using an Editor attribute:
Exapmle
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
public class MyComponent : Component
{
public SampleClass SampleProperty { get; set; } = new SampleClass();
}
[TypeConverter(typeof(ExpandableObjectConverter))]
[Editor(typeof(SampleClassEditor), typeof(UITypeEditor))]
public class SampleClass
{
public Color Color1 { get; set; } = Color.Red;
public Color Color2 { get; set; } = Color.Green;
public Color Color3 { get; set; } = Color.Blue;
}
public class SampleClassEditor : UITypeEditor
{
public override bool GetPaintValueSupported(ITypeDescriptorContext context)
{
return true;
}
public override void PaintValue(PaintValueEventArgs e)
{
var sample = e.Value as SampleClass;
if (sample != null)
{
int x = e.Bounds.X, y = e.Bounds.Y;
int w = e.Bounds.Width, h = e.Bounds.Height;
using (var b = new SolidBrush(sample.Color1))
e.Graphics.FillRectangle(b, x, y, w / 3, h);
using (var b = new SolidBrush(sample.Color2))
e.Graphics.FillRectangle(b, x += w / 3, y, w / 3, h);
using (var b = new SolidBrush(sample.Color3))
e.Graphics.FillRectangle(b, x += w / 3, y, w / 3 + 1, h);
}
base.PaintValue(e);
}
}
Related
My code structure is something like this
class Drawing()
{
ObjectType = "Drawing";
}
class Shape() : Drawing()
{
DrawingType = "Shape";
}
class square() : Shape()
{
ShapeType = "square";
}
class circle() : Shape()
{
ShapeType = "circle";
}
class triangle() : Shape()
{
ShapeType = "triangle";
}
class lines() : Drawing()
{
DrawingType = "lines";
}
class horizontal() : lines()
{
linesType = "horizontal";
}
class vertical() : lines()
{
linesType = "vertical";
}
etc...
I am trying to figure out the right logic in how to solve this problem so the picture is a simple representation of what I have.
I have a object structure of something like in the picture where they're all inheriting the level above them. They all have properties of its type. They would all have ObjectType, and the 2nd level has DrawingType, and the 3rd level has either ShapeType, LineType, or PointType...
So for example, square would have
square {
ObjectType = "drawing";
DrawingType = "shapes";
ShapeType = "square"
}
However, vertical would have different properties
vertical {
ObjectType = "drawing";
DrawingType = "lines";
LineType = "vertical"
}
Now my problem is, lets say I wanted a user to select something based on their input, how would i do it?
Like lets say the user typed "square", how would I select square? It would be easy if the properties were the same cause I could just compare them. But how would I do it with a structure like this?
As these all classes are derived from one base class hence all the child classes contains the property coming from parents. Object level comparison will lead you comparison of one Drawing object to another as all the objects are derived from there only. Hence i have implemented IComparer to get your desired result ->
Here is the code ->>
Main Class :
Drawing drawing1 = new Drawing();
Drawing drawing2 = new Drawing();
drawing2.ObjectType = "newdrawing";
Shape shape = new Shape();
triangle triangle1 = new triangle();
triangle triangle2 = new triangle();
triangle2.ShapeType = "another";
//all -1 is not equals and 0 is equals
int result1 = drawing1.Compare(drawing1, drawing2);
int result2 = drawing1.Compare(drawing1, drawing1);
int result3 = drawing1.Compare(drawing1, shape);
int result4 = shape.Compare(shape, triangle1);
int result5 = triangle1.Compare(triangle1, triangle2);
Other classes :
public class Drawing : IComparer
{
public string ObjectType { get; set; } = "Drawing";
public int Compare(object x, object y)
{
//returning 0 is equals
if (x.GetType().Name == y.GetType().Name)
{
switch (x.GetType().Name)
{
case "Drawing":
return ((Drawing)x).ObjectType == ((Drawing)y).ObjectType ? 0 : -1;
case "Shape":
return ((Shape)x).DrawingType == ((Shape)y).DrawingType ? 0 : -1;
case "square":
return ((square)x).ShapeType == ((square)y).ShapeType ? 0 : -1;
case "triangle":
return ((triangle)x).ShapeType == ((triangle)y).ShapeType ? 0 : -1;
default:
return -1; // Not equal
}
}
return -1; // Not equal
}
}
class Shape : Drawing
{
public string DrawingType { get; set; } = "Shape";
}
class square : Shape
{
public string ShapeType { get; set; } = "square";
}
class circle : Shape
{
public string ShapeType { get; set; } = "circle";
}
class triangle : Shape
{
public string ShapeType { get; set; } = "triangle";
}
class lines : Drawing
{
public string DrawingType { get; set; } = "lines";
}
class horizontal : lines
{
public string linesType { get; set; } = "horizontal";
}
class vertical : lines
{
public string linesType { get; set; } = "vertical";
}
Hope this will work for you. please write back with you code snippet specifically not working. Cheers
I am working with Open Closed principle of SOLID in C#. I have abstract class Shape which i want to used to calculate the area of different shapes. How to call areaCalculator class and how to pass different shapes. Here is my code.
public abstract class Shape
{
public abstract double Area();
}
public class Rectangle : Shape
{
public double Height { get; set; }
public double Width { get; set; }
public override double Area()
{
return Height * Width;
}
}
public class AreaCalculator
{
public double TotalArea(Shape[] shapes)
{
double area = 0;
foreach (var objShapes in shapes)
{
area += objShapes.Area();
}
return area;
}
}
I want to call areaCalculator class to calculate the area.
AreaCalculator _obj = new AreaCalculator();
Shape[] _shapes = new Shape[2];
var _result = _obj.TotalArea(_shapes);
Console.WriteLine(_result);
Console.ReadLine();
You need to create the rectangle objects and set their height and width for the calculation. If not the _shapes list is empty. Find below a sample of working code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShapesStacjOverflow {
public abstract class Shape {
public abstract double Area();
}
public class Rectangle : Shape {
public double Height { get; set; }
public double Width { get; set; }
public override double Area() {
return Height * Width;
}
}
public class AreaCalculator {
public double TotalArea(Shape[] shapes) {
double area = 0;
foreach (var objShapes in shapes) {
area += objShapes.Area();
}
return area;
}
}
class Program {
static void Main(string[] args) {
AreaCalculator _obj = new AreaCalculator();
Shape[] _shapes = new Shape[2];
Rectangle rectangle1 = new Rectangle {
Width = 2,
Height = 3
};
Rectangle rectangle2 = new Rectangle {
Width = 1,
Height = 1
};
_shapes[0] = rectangle1;
_shapes[1] = rectangle2;
var _result = _obj.TotalArea(_shapes);
Console.WriteLine(_result);
Console.ReadLine();
}
}
}
Returning 7 as a result.
If you want to create other child shapes, those should override the Area() method, so for each of the objects created in the list, the corresponding Area() method would be applied.
Hope that helps.
I want to serialize my object as binary or xml.
I used this method to serialize my object. but it gives error : System.Windows.Media.SolidColorBrush not serializable.
My object contains Grid, Ellipse, SolidColorBrush objects.
How can I serialize these objects?
You need to create a SerializableBrush class of your own which has constructors taking SolidBrush etc and also a ToBrush method to reconstruct the GDI+ brushes..
The same goes for most other GDI+ objects and structures like Color, Pen etc, some of which, like Color you will even need for the SerializableBrush class..
Adorn it with the [Serializable] attribute!
A simple serializable Color class:
[Serializable]
public class SerColor
{
public byte Red { get; set; }
public byte Green { get; set; }
public byte Blue { get; set; }
public byte Alpha { get; set; }
public SerColor() { }
public SerColor(Color c)
{ Red = c.R; Green = c.G; Blue = c.B; Alpha = c.A; }
static public Color Color(SerColor c)
{ return System.Drawing.Color.FromArgb(c.Alpha, c.Red, c.Green, c.Blue); }
}
'And a simple serializable SolidBrush class:
[Serializable]
public class SerSolidBrush
{
public SerColor sColor { get; set; }
public SerSolidBrush() { }
public SerSolidBrush(Color c)
{
sColor = new SerColor(c);
}
public SerSolidBrush(SolidBrush b)
{
sColor = new SerColor(b.Color);
}
public SolidBrush SolidBrush()
{
Color c = SerColor.Color(sColor);
return new System.Drawing.SolidBrush(c);
}
static public SolidBrush SolidBrush(SerSolidBrush b)
{
Color c = SerColor.Color(b.sColor);
return new System.Drawing.SolidBrush(c);
}
}
And a small test bed:
SolidBrush brush = new SolidBrush(Color.Red);
SerSolidBrush sBrush = new SerSolidBrush(brush);
XmlSerializer xs = new XmlSerializer(sBrush.GetType());
using (TextWriter tw = new StreamWriter(#"d:\xmlBrush.xml"))
{
xs.Serialize(tw, sBrush);
tw.Close();
}
SerSolidBrush sBrush2 = null;
using (TextReader tr = new StreamReader(#"d:\xmlBrush.xml"))
sBrush2 = (SerSolidBrush) xs.Deserialize(tr);
SolidBrush newBrush = sBrush2.SolidBrush();
This is the resulting xml file ((no idead how to insert xml text in an SO answer properly)):
I hope this shows how to do it; note that SolidBrush is a very simple class with only one property. Pen and most others will be more complicated if you want to support all properties..
Note that simple built-in enums, like LineJoin etc are serializable, so you can simply add the proerties and everything works just fine. To demostrate here is a rather incomplete serilizable Pen class. You will want to add several other properties..:
[Serializable]
public class SerPen
{
public SerColor sColor { get; set; }
public float width { get; set; }
public LineJoin lineJoin { get; set; }
// constructors
public SerPen() { width = 1f; }
public SerPen(Color c, float w)
{
sColor = new SerColor(c);
width = w;
}
public SerPen(Pen p)
{
sColor = new SerColor(p.Color);
width = p.Width;
lineJoin = p.LineJoin;
}
// re-constructors
public Pen Pen ()
{
Color c = SerColor.Color(sColor);
Pen pen = new System.Drawing.Pen (c, width);
pen.LineJoin = lineJoin;
return pen;
}
static public Pen Pen (SerPen p)
{
Color c = SerColor.Color(p.sColor);
Pen pen = new System.Drawing.Pen (c, p.width);
pen.LineJoin = p.lineJoin;
return pen;
}
}
Whenever I call the method Draw in Sprite, it won't draw it because X, Y, Width and Height are 0. :(
code:
class Sprite
{
protected int Y;// { get; set; }
protected int X;// { get; set; } { get; set; }
protected int Width;// { get; set; } { get; set; }
public int Height;// { get; set; } { get; set; }
protected Image image;// { get; set; } { get; set; }
public Sprite()
{
}
public void Draw(Graphics drawArea)
{
Image sImage = Image.FromFile("alien.jpg");
drawArea.Clear(Color.White);
drawArea.DrawImage(sImage, X, Y, Width, Height);
}
}
class User:Sprite
{
public User()
{
}
public User(int width, int height, int userWidth, int userHeight)
{
Sprite sprite = new Sprite();
Image UserImage = Image.FromFile("alien.jpg");
X = width;
Y = height;
Width = userWidth;
Height = userHeight;
image = UserImage;
}
}
ps: sprite.Draw is declared in another method in another class, but that all should work just fine.
Thanks for helping and probably saving me hours of time :)
Nick
EDIT
here is the subclass which gives the parameter and other stuff.
Alien mAlien;
User mUser;
protected int mLevel;
public gameLogic()
{
}
public gameLogic(int width, int height, int level)
{
mUser = new User(width / 2, height - 30, 30, 30);
mAlien = new Alien(width / 2, 5, 30, 30, "alien.jpg", 10 * level);
mLevel = level;
}
public void drawAll(Graphics drawArea)
{
Sprite sprite = new Sprite();
sprite.Draw(drawArea);
}
Im sorry for all these errors that'll probably occur, Im a new student :)
Try this:
public User(int width, int height, int userWidth, int userHeight)
{
Sprite sprite = new Sprite();
Image UserImage = Image.FromFile("alien.jpg");
sprite.X = width;
sprite.Y = height;
sprite.Width = userWidth;
sprite.Height = userHeight;
sprite.image = UserImage;
}
In order to access the fields of the sprite, you have to specify which Sprite object you are modifying. This is done by writing the name of the variable followed by .
EDIT: Just realized that there is another problem - your User class is inheriting from the Sprite class. Apparently your users are sprites, according to the comments, so you'll want to never instantiate a Sprite, and just use the User class instead:
class User:Sprite
{
public User(int width, int height, int userWidth, int userHeight)
{
Image UserImage = Image.FromFile("alien.jpg");
X = width;
Y = height;
Width = userWidth;
Height = userHeight;
image = UserImage;
}
}
Then instead of calling draw on a sprite in the other file, use the following:
User user = new User(x, y, width, height)
...
user.draw();
The key here is to make sure you are using new User... and not new Sprite - even if you are assigning to a Sprite variable e.g. Sprite s = new User(...);.
Then just make sure you call draw on the same object - the user.draw line. If you want to check that you're doing it right, try making Sprite abstract - abstract class Sprite - that way there will be compile errors if you try to instantiate Sprite instead of User.
EDIT 2: Ok, all you really need to do is change your calling code:
public void drawAll(Graphics drawArea)
{
mUser.draw(drawArea);
mAlien.draw(drawArea);
}
You are missing trailing '}' for both your classes, Sprite and User. Second you should call the base-Constructor in User's constructor:
public User(int width, int height, int userWidth, int userHeight)
: base(width, height, userWidth, userHeight, Image.FromFile("alien.jpg"))
{
}
Also you need to create a constructor for your base class which accepts these parameters.
I have a Custom UIElement I created below.
class ClickItem : UIElement
{
public ClickItem()
{
}
public ClickItem(Color color)
{
this.Color = color;
Ellipse _e = new Ellipse();
_e.Fill = new SolidColorBrush(this.Color);
_e.StrokeThickness = 1;
_e.Stroke = Brushes.Black;
_e.Width = 10;
_e.Height = 10;
this.Plotter = _e;
}
public Point CenterPoint { get; set; }
public Ellipse Plotter { get; set; }
public Color Color { get; set; }
}
How do I make the Plotter Ellipse the visual for the UIElement so when I add a ClickItem to a canvas, the `Plotter' shows up.
Canvas canvas = new Canvas();
ClickItem clickItem = new ClickItem(Colors.Red);
canvas.Add(clickItem);
I can do this but I don't know how to get the ClickItem from this if I click on it.
canvas.Add(clickItem.Plotter);
I think you should inherit from user control, or some other relevant lower class, and add the ellipse as a child of your ClickItem.