I'm using two classes as Toolbox derived from ItemsControl and ToolboxItem derived from ContnentControl
// Implements ItemsControl for ToolboxItems
public class Toolbox : ItemsControl
{
// Defines the ItemHeight and ItemWidth properties of
// the WrapPanel used for this Toolbox
public Size ItemSize
{
get { return itemSize; }
set { itemSize = value; }
}
private Size itemSize = new Size(50, 50);
// Creates or identifies the element that is used to display the given item.
protected override DependencyObject GetContainerForItemOverride()
{
return new ToolboxItem();
}
// Determines if the specified item is (or is eligible to be) its own container.
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is ToolboxItem);
}
}
// Represents a selectable item in the Toolbox/>.
public class ToolboxItem : ContentControl
{
// caches the start point of the drag operation
private Point? dragStartPoint = null;
static ToolboxItem()
{
// set the key to reference the style for this control
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(ToolboxItem), new FrameworkPropertyMetadata(typeof(ToolboxItem)));
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
this.dragStartPoint = new Point?(e.GetPosition(this));
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton != MouseButtonState.Pressed)
this.dragStartPoint = null;
if (this.dragStartPoint.HasValue)
{
// XamlWriter.Save() has limitations in exactly what is serialized,
// see SDK documentation; short term solution only;
string xamlString = XamlWriter.Save(this.Content);
DragObject dataObject = new DragObject();
dataObject.Xaml = xamlString;
WrapPanel panel = VisualTreeHelper.GetParent(this) as WrapPanel;
if (panel != null)
{
// desired size for DesignerCanvas is the stretched Toolbox item size
double scale = 1.3;
dataObject.DesiredSize = new Size(panel.ItemWidth * scale, panel.ItemHeight * scale);
}
DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Copy);
e.Handled = true;
}
}
}
// Wraps info of the dragged object into a class
public class DragObject
{
// Xaml string that represents the serialized content
public String Xaml { get; set; }
// Defines width and height of the DesignerItem
// when this DragObject is dropped on the DesignerCanvas
public Size? DesiredSize { get; set; }
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DesignerItem ni = new DesignerItem();
ni.AllowDrop = true;
ni.ToolTip = "tooltip goes here";
ni.Width = 100;
ni.Height = 200;
ni.Background = Brushes.Red;
//somehow code should introduce the object shape as object. in the sample on codeproject it is reading shape information from xaml but I need to add it from code behind.
Toolbox tb = new Toolbox();
tb.Items.Add(ni);
AddChild() method from ItemsControl must be the method to add a new object on a toolbox. However, when I instantiate an object from this class it does not give me this method. For simplicity I only want to add a rectangle shape on it this would allow me to to drag/drop it on canvas. So question is how I can add a Rectangle on toobox.
Any help is appreciated.
Thanks.
Amit
Solution:
var TheToolbar = ToolboxContainer.Content as Toolbox;
// Instantiate a ToolboxItem
ToolboxItem TheToolboxItem = new ToolboxItem();
Rectangle myRect = new Rectangle();
myRect.StrokeThickness = 1;
myRect.Stroke = some value for stroke;
myRect.Fill = some value for filling the object;
myRect.IsHitTestVisible = false;
//add to Toolbar
TheToolboxItem.Content= myRect;
TheToolbar.Items.Add(TheToolboxItem);
AddChild() is a protected method ( = accessible only from itself or from an inherited class). Try to use a code like this instead:
Toolbox tb = new Toolbox();
tb.Items.Add(myNewItem);
After you instantiate your objects, you need to add them as children to your canvas/parent element.
myParentElement.Children.Add(tb);
If you're doing it, include it in your example code.
Related
I created dummy custom panel ShelfPanel with attached property Exact influence panel arrange:
class ShelfPanel : Panel
{
#region Start attached property
public static DependencyProperty ExactProperty = DependencyProperty.RegisterAttached("Exact", typeof(int), typeof(ShelfPanel),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.AffectsArrange | // Does this options have auto action?
FrameworkPropertyMetadataOptions.AffectsRender)); // Does this options have auto action?
public static void SetExact(UIElement element, int value)
{
element.SetValue(ExactProperty, value);
}
public static int GetExact(UIElement element)
{
return (int)element.GetValue(ExactProperty);
}
#endregion
protected override Size MeasureOverride(Size availableSize)
{
Size final = new Size();
foreach (UIElement child in InternalChildren)
{
child.Measure(availableSize);
final.Height += child.DesiredSize.Height;
}
return final;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in InternalChildren)
{
Point position = new Point();
int exact = ShelfPanel.GetExact(child);
// Calculate position based on attached Exact
position.X = exact * 100;
position.Y = exact * 100;
child.Arrange(new Rect(position, child.DesiredSize));
}
return finalSize;
}
}
<local:ShelfPanel>
<local:Box local:ShelfPanel.Exact="0" MouseDown="Box_MouseDown"/>
<local:Box local:ShelfPanel.Exact="1" />
<local:Box local:ShelfPanel.Exact="2" />
</local:ShelfPanel>
public partial class MainWindow : Window // Codebehind for previous xaml
{
public MainWindow()
{
InitializeComponent();
}
private void Box_MouseDown(object sender, MouseButtonEventArgs e)
{
// I certainly sure this code get triggered after click.
ShelfPanel.SetExact(sender as UIElement, 3);
}
}
This works perfect <local:Box> are arranged as planned.
As you can deduce from code, after click on first <local:Box> it should change it position to 3, just after others 2. But surprisingly nothing happen.
Doesn't FrameworkPropertyMetadataOptions.Affects Arrange or FrameworkPropertyMetadataOptions.AffectsRender automatically repaint panel?
To make this works I need to add PropertyChangedCallbackand call there InvalidateVisual()?
You are setting the attached property on a Box, but want the parent element of the Box, i.e. the ShelfPanel to be arranged.
You should therefore set FrameworkPropertyMetadataOptions.AffectsParentArrange:
public static readonly DependencyProperty ExactProperty =
DependencyProperty.RegisterAttached("Exact", typeof(int), typeof(ShelfPanel),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.AffectsParentArrange));
I am just a hobby programmer but I come across may cases where I want to switch a type value (e.g. label.backgroundcolor) depending on whether it has been clicked or not, or whether the mouse is over or not. This is normally a trivial change but there has to be code for the event in each case and this then involves passing information such as default colour, mouse-over colour or default fontstyle, mouse-over fontstyle or any of many other types of 'switch'. In all cases it is simply a case of toggling the change between one value and another but, because this could potentially happen over several different types (Labels, Textboxes, Panels etc.) I find I have to code for each type separately.
Is there any good reason why I shouldn't just do this
class AnyObjectBoolean
{
private object objOne;
private object objTwo;
public AnyObjectBoolean(object oneValue, object twoValue)
{
objOne = oneValue;
objTwo = twoValue;
}
public object invert(Object val)
{
if (val.ToString() == objOne.ToString())
{
return objTwo;
}
else
{
return objOne;
}
}
I then create a new instance for each object and style I want to change and the resulting event code becomes (for instance)
private void Label_MouseClick(object sender, EventArgs e)
{
var Label = (Label)sender;
Label.BackColor = (Color)SelectColours.invert(Label.BackColor);
}
where SelectColours is an instance of AnyObjectBoolean.
Maybe not a great question but I ask because I've never found anything like this implemented anywhere.
Disclaimer:- this is my first post so I may not have tagged entirely appropriately or completely.
First of all welcome to StackOverflow.
Personally, I'd prefer to handle this through a catch-all function, in which I would deal with all the types of objects that I wanted. Something like:
private Color ToggleColor(object sender, Color currentColor)
{
Color labelBackColor = Color.White;
Color labelHoverColor = Color.Yellow;
Color textBackColor = Color.Wheat;
Color textHoverColor = Color.Turquoise;
Color defaultBackColor = Color.Tomato;
Color defaultHoverColor = Color.SteelBlue;
Label l = sender as Label;
if (l != null)
{
return currentColor == labelBackColor ? labelHoverColor : labelBackColor;
}
TextBox t = sender as TextBox;
if (t != null)
{
return currentColor == textBackColor ? textHoverColor : textBackColor;
}
return currentColor == defaultBackColor ? defaultHoverColor : defaultBackColor;
}
Doing it this way, I keep the entire color scheme in one place. BTW don't use my suggested colors! Note the use of "as". It does the same as cast, with the important difference that it does not throw an Exception if it fails, it simply returns null. Therefore, you can simply try each object in turn safely until you get a hit.
there is no need for an instance, as i understand your program static class will be more appropriate here and you can change it from outside whenever you need.
use one method that will add events to all controls (you can add click event to all control types not just a label).
I think you want to do something like that:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MyColors.firstColor = Color.Blue;
MyColors.secondColor = Color.Yellow;
// add events to all requested controls
AddEvent(new Control[] {
label1,
label2,
label3,
});
}
public void AddEvent(Control[] myControls)
{
foreach (Control c in myControls)
{
c.MouseClick += Control_Click;
}
}
private void Control_Click(object sender, EventArgs e)
{
((Control)sender).BackColor = MyColors.GetColor(((Control)sender).BackColor);
}
}
static class MyColors
{
public static Color firstColor { get; set; } = Color.Black;
public static Color secondColor { get; set; } = Color.White;
public static Color GetColor(Color color)
{
if (color == firstColor)
{
return secondColor;
}
else
{
return firstColor;
}
}
}
Most of the controls such as TextBox, Label and many other controls inherit from the Control class. The property BackColor is a property of the Control class; therefore, you can write a method or a class that takes a Control type and changes its color. Below is a class which takes 2 colors in its constructors and then inverts the passed in Control instance from one color to another.
public class ColorInverter
{
public Color Color1 { get; private set; }
public Color Color2 { get; private set; }
public ColorInverter(Color color1, Color color2)
{
this.Color1 = color1;
this.Color2 = color2;
}
public void Invert(Control control)
{
if (control.BackColor == this.Color1)
{
control.BackColor = this.Color2;
return;
}
control.BackColor = Color1;
}
}
Usage:
ColorInverter c = new ColorInverter(Color.Black, Color.Red);
TextBox box = new TextBox();
c.Invert(box);
Here is the inheritance hierarchy for Label. You can look for other controls there as well.
You may even check if the control inherits Control before calling the Invert method:
if (box is Control)
{
c.Invert(box);
}
else
{
// ...do something
}
You can use access any properties such as FontSize, FontFamily and many other Control properties as well. Obviously change the class name from ColorInverter to something else if you want to change the FontSize etc. as well.
I wrote a custom collection editor for a WinForms control. Its core code looks like this:
internal class MyCollectionEditor : CollectionEditor
{
public MyCollectionEditor(Type type) : base(type) { }
protected override System.ComponentModel.Design.CollectionEditor.CollectionForm CreateCollectionForm()
{
System.ComponentModel.Design.CollectionEditor.CollectionForm myForm = base.CreateCollectionForm();
#region Adjust the property grid
PropertyGrid myPropGrid = GetPropertyGrid(myForm);
if (myPropGrid != null)
{
myPropGrid.CommandsVisibleIfAvailable = true;
myPropGrid.HelpVisible = true;
myPropGrid.PropertySort = PropertySort.CategorizedAlphabetical;
}
#endregion
return myForm;
}
}
I need to set a custom size and location for the collection editor form, but I could not find a way to do that. It seems the collection editor form is always positioned by VS to its default location. Is there a way to do what I need?
It respects to the StartPosition, DesktopLocation and Size which you set for the form:
public class MyCollectionEditor : CollectionEditor
{
public MyCollectionEditor() : base(typeof(Collection<Point>)) { }
protected override CollectionForm CreateCollectionForm()
{
var form = base.CreateCollectionForm();
// Other Settings
// ...
form.StartPosition = FormStartPosition.Manual;
form.Size = new Size(900, 600);
form.DesktopLocation = new Point(10, 10);
return form;
}
}
Then decorate your property this way:
[Editor(typeof(MyCollectionEditor), typeof(UITypeEditor))]
public Collection<Point> MyPoints { get; set; }
I am creating a custom control in my C# application in order to add a new property (MyProperty below). It is inheriting from Label. One thing I would like it to do, is display at a particular size when I drag it on to my form (200x132). I'd also like it to display no text. However, no matter how I try to do this, it doesn't seem to work. I am able to set BackColor and BorderStyle with no problem, however. I'm fairly new to C#, so maybe I'm missing something obvious.
Here is my code:
using System.Drawing;
using System.Windows.Forms;
namespace MyProgram
{
public enum MyEnum
{
Value1, Value2, Value3
}
public partial class MyControl : Label
{
public MyControl()
{
BackColor = Color.LightCoral;
BorderStyle = BorderStyle.FixedSingle;
AutoSize = false;
Size = new Size(200, 132);
Text = "";
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
private MyEnum myProperty;
public MyEnum MyProperty
{
get { return myProperty; }
set { myPropery = value; }
}
}
}
The answer provided via Dispersia's link has a bug, in my opinion. The text reset should happen once and then whatever a user does after that shouldn't matter. In Dispersia's link you can't actually set the text back to the control name because it will keep blanking it out.
The answer provided by cramopy doesn't technically answer your question, it is a way to do it by using the defaults on a UserControl though. You'll also need to bind the Text property of the UserControl to the label's.
The following should work while inheriting from a Label and will only reset the Text property once.
public partial class MyControl : Label
{
#region fields
private IComponentChangeService _changeService;
private bool canResetText = false;
#endregion
#region properties
protected override Size DefaultSize
{
get { return new Size(200, 132); }
}
[Browsable(false)]
public override bool AutoSize
{
get { return false; }
set { base.AutoSize = false; }
}
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
if (!base.DesignMode)
return;
this._changeService = (IComponentChangeService)base.GetService(typeof(IComponentChangeService));
if (this._changeService != null)
this._changeService.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
#region constructors
public MyControl()
{
base.BackColor = Color.LightCoral;
base.BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region methods
protected override void InitLayout()
{
base.InitLayout();
this.canResetText = true;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
if (ce.Component != null &&
ce.Component == this &&
ce.Member.Name == "Text" &&
base.DesignMode &&
this.canResetText)
{
((MyControl)ce.Component).Text = string.Empty;
this.canResetText = false;
if (this._changeService != null)
this._changeService.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
}
#Dispersia reply only answers the myControl1 thing. (deleted meanwhile)
Here comes a full guide for solving your problem:
Add a new UserControl named MyLabel
Change the following within Designer Mode:
BorderStyle:= FixedSingle
Size:= 200; 132
Now Drag&Drop a new Label onto the control
Edit those Label values (also within Designer Mode):
AutoSize:= false
BackColor:= LightCoral
Dock:= Fill
Text:= clear/empty this box!! (don't write this inside the box, you really have to clear it!)
TextAlign:= MiddleCenter
Just recompile your project && add a MyLabel control from the Toolbar.
Now it show up as you wanted!!
I need to create a user control MyTypeListControl to display collection of objects of type MyType using a user controls MyTypeDisplayControl instance for each of those objects.
So that I could
add instance of MyTypeListControl to my WinForm, then
load collection of MyType and
assign it to MyTypeListControl's DataSource.
In the result it should generate and show appropriate count of MyTypeDisplayControl instances in MyTypeListControl's instance.
In case if I needed to show list of properties - equivalent would be DataGrid with specific fields from MyType assigned to specific DataGrid's columns, but I want to view each MyType item as a user control - with more power for visual representation and functionality than DataGrid provides for it's rows.
Is that even possible?
I found this SO resource how to create My collection type, but this is only small part of the problem solution...
It is quite easy (if you know how) and doesn't take so much effort as you might think in the first place (at least for a simple implementation that handles collection of less then 100 items).
So at first lets create a MyType:
public class MyType
{
public static MyType Empty = new MyType(String.Empty, DateTime.MinValue);
public MyType(string myName, DateTime myBirthday)
{
MyName = myName;
MyBirthday = myBirthday;
}
public DateTime MyBirthday { get; private set; }
public string MyName { get; private set; }
}
At next we need a MyTypeControl:
public partial class MyTypeControl : UserControl
{
private MyType _MyType;
private Label labelBirthday;
private Label labelName;
private Label labelSeparator;
public MyTypeControl()
{
InitializeComponent();
}
public event EventHandler MyTypeChanged;
public MyType MyType
{
get { return _MyType; }
set
{
if (_MyType == value)
return;
_MyType = value ?? MyType.Empty;
OnMyTypeChanged(EventArgs.Empty);
}
}
protected virtual void OnMyTypeChanged(EventArgs eventArgs)
{
UpdateVisualization();
RaiseEvent(MyTypeChanged, eventArgs);
}
protected void UpdateVisualization()
{
SuspendLayout();
labelName.Text = _MyType.MyName;
labelBirthday.Text = _MyType.MyBirthday.ToString("F");
labelBirthday.Visible = _MyType.MyBirthday != DateTime.MinValue;
ResumeLayout();
}
private void InitializeComponent()
{
labelName = new Label();
labelBirthday = new Label();
labelSeparator = new Label();
SuspendLayout();
labelName.Dock = DockStyle.Top;
labelName.Location = new Point(0, 0);
labelName.TextAlign = ContentAlignment.MiddleCenter;
labelBirthday.Dock = DockStyle.Top;
labelBirthday.TextAlign = ContentAlignment.MiddleCenter;
labelSeparator.BorderStyle = BorderStyle.Fixed3D;
labelSeparator.Dock = DockStyle.Top;
labelSeparator.Size = new Size(150, 2);
Controls.Add(labelSeparator);
Controls.Add(labelBirthday);
Controls.Add(labelName);
MinimumSize = new Size(0, 48);
Name = "MyTypeControl";
Size = new Size(150, 48);
ResumeLayout(false);
}
private void RaiseEvent(EventHandler eventHandler, EventArgs eventArgs)
{
var temp = eventHandler;
if (temp != null)
temp(this, eventArgs);
}
}
Then comes our magically list control:
public class MyTypeListControl : UserControl
{
private ObservableCollection<MyType> _Items;
public MyTypeListControl()
{
AutoScroll = true;
_Items = new ObservableCollection<MyType>();
_Items.CollectionChanged += OnItemsCollectionChanged;
}
public Collection<MyType> Items
{
get { return _Items; }
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateVisualization();
}
private void UpdateVisualization()
{
SuspendLayout();
Controls.Clear();
foreach (var item in _Items)
{
var control = new MyTypeControl { MyType = item, Dock = DockStyle.Top };
Controls.Add(control);
Controls.SetChildIndex(control, 0);
}
ResumeLayout();
}
}
And now simply create the list control in your form or parent control and fill it with some meaningful values:
myTypeListControl.Items.Add(new MyType("Adam", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 40))));
myTypeListControl.Items.Add(new MyType("Eva", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 38))));