Inherited control not generate code in form designer generated code - c#

I am trying to inherit DevExpress PictureEdit (but, I think this problem is not DevExpress specific). I add some properties to it.
public class RepositoryItemFotoEdit:RepositoryItemPictureEdit
{
private Size _imageSize;
public Size ImageSize
{
get
{
return _imageSize;
}
set
{
_imageSize = value;
OnPropertiesChanged();
}
}
[DefaultValue(InterpolationMode.HighQualityBicubic)]
public new InterpolationMode PictureInterpolationMode
{
get { return base.PictureInterpolationMode; }
set { base.PictureInterpolationMode = value; }
}
[DefaultValue(PictureSizeMode.Zoom)]
public new PictureSizeMode SizeMode
{
get { return base.SizeMode; }
set { base.SizeMode = value; }
}
public RepositoryItemFotoEdit()
{
PictureInterpolationMode = InterpolationMode.HighQualityBicubic;
SizeMode = PictureSizeMode.Zoom;
}
}
public sealed class FotoEdit : PictureEdit
{
private FotoMenu _menu;
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_menu != null)
_menu.Dispose();
}
base.Dispose(disposing);
}
public override Image Image
{
get
{
return base.Image;
}
set
{
if(Properties.ImageSize.Width == 0 || Properties.ImageSize.Height == 0 || value == null)
base.Image = value;
else
{
var heightProp = (double) Properties.ImageSize.Height/value.Height;
var widthProp = (double) Properties.ImageSize.Width/value.Width;
var prop = heightProp < widthProp ? heightProp : widthProp;
base.Image = ImageHelper.ResizeImage(value, (int)(prop*value.Width), (int)(prop*value.Height));
}
}
}
public new RepositoryItemFotoEdit Properties
{
get { return base.Properties as RepositoryItemFotoEdit; }
}
protected override PictureMenu Menu
{
get { return _menu ?? (_menu = new FotoMenu(this)); }
}
protected override EditorClassInfo EditorClassInfo
{
get
{
var beci = base.EditorClassInfo;
var eci = new EditorClassInfo("FotoEdit", typeof (FotoEdit), typeof (RepositoryItemFotoEdit),
beci.ViewInfoType, beci.Painter, beci.DesignTimeVisible);
return eci;
}
}
}
Property ImageSize appear on PropertyEditor.
.
But, if I change it to some value (e.g. 150; 150), it is not generate code in form designer generated code.
Note that ImageSize is inside Properties property.
//
// fotoEdit1
//
this.fotoEdit1.Cursor = System.Windows.Forms.Cursors.Default;
this.fotoEdit1.Location = new System.Drawing.Point(583, 370);
this.fotoEdit1.Name = "fotoEdit1";
this.fotoEdit1.Size = new System.Drawing.Size(150, 150);
this.fotoEdit1.TabIndex = 18;
That will result in ImageSize value revert back to 0;0. How to solve this problem?
EDIT :
I tried to add [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] to Properties property as sugested by someone (I forgot his name and he deleted his answer). It result to properties inside Properties property appear in designer generated code except those who overriden.
My designer generated code look like this:
//
// fotoEdit1
//
this.fotoEdit1.Cursor = System.Windows.Forms.Cursors.Default;
this.fotoEdit1.Location = new System.Drawing.Point(583, 370);
this.fotoEdit1.Name = "fotoEdit1";
this.fotoEdit1.Properties.Padding = new System.Windows.Forms.Padding(5, 2, 0, 0);
this.fotoEdit1.Size = new System.Drawing.Size(150, 150);
this.fotoEdit1.TabIndex = 18;
Note that Properties.Padding appear, but Properties.ImageSize not.

I just solve the problem by add [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] to Properties property and [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] to Properties.ImageSize property.

Related

Changes made in a custom StackLayout (when running) don't appear in the view (Xamarin.Forms)

I have implemented a custom layout whose main characteristic is that the background can be a gradient by giving 2 hex colours. The view itself works perfectly fine, the problem is that changes made in these 2 colours while the program is running do not reflect themselves in the app, the custom Layout receives and changes both new colours and make the gradient correctly, but it doesn't show the changes made. I assume that the problem is that I have not implemented correctly the I INotifyPropertyChanged.
Here is the code of my custom view:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace MyProject.Renderers
{
public class GradientLayout : StackLayout, INotifyPropertyChanged
{
public string ColorsList { get; set; }
public Color[] Colors
{
get
{
//colorsList have the following format: "HexCode1,HexCode2"
string[] hex = ColorsList.Split(',');
Color[] colors = new Color[hex.Length];
for (int i = 0; i < hex.Length; i++)
{
colors[i] = Color.FromHex(hex[i].Trim());
}
return colors;
}
}
//since this is a property that I am not trying to modify, I will not provide the rest of its code to make this as simple as possible.
public GradientColorStackMode Mode { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And that is all, if you need more information I will provide it as soon as I see your request, thank to all of you for your time, I hope you have a good day.
EDIT: Here you have a class in myProject.ios that is related to the background gradient.
[assembly: ExportRenderer(typeof(GradientLayout), typeof(GradientLayoutRenderer))]
namespace MyProject.iOS.Renderers
{
public class GradientLayoutRenderer : VisualElementRenderer<StackLayout>
{
public override void Draw(CGRect rect)
{
base.Draw(rect);
GradientLayout layout = (GradientLayout)Element;
CGColor[] colors = new CGColor[layout.Colors.Length];
for (int i = 0, l = colors.Length; i < l; i++)
{
colors[i] = layout.Colors[i].ToCGColor();
}
var gradientLayer = new CAGradientLayer();
switch (layout.Mode)
{
default:
case GradientColorStackMode.ToRight:
gradientLayer.StartPoint = new CGPoint(0, 0.5);
gradientLayer.EndPoint = new CGPoint(1, 0.5);
break;
case GradientColorStackMode.ToLeft:
gradientLayer.StartPoint = new CGPoint(1, 0.5);
gradientLayer.EndPoint = new CGPoint(0, 0.5);
break;
case GradientColorStackMode.ToTop:
gradientLayer.StartPoint = new CGPoint(0.5, 0);
gradientLayer.EndPoint = new CGPoint(0.5, 1);
break;
case GradientColorStackMode.ToBottom:
gradientLayer.StartPoint = new CGPoint(0.5, 1);
gradientLayer.EndPoint = new CGPoint(0.5, 0);
break;
case GradientColorStackMode.ToTopLeft:
gradientLayer.StartPoint = new CGPoint(1, 0);
gradientLayer.EndPoint = new CGPoint(0, 1);
break;
case GradientColorStackMode.ToTopRight:
gradientLayer.StartPoint = new CGPoint(0, 1);
gradientLayer.EndPoint = new CGPoint(1, 0);
break;
case GradientColorStackMode.ToBottomLeft:
gradientLayer.StartPoint = new CGPoint(1, 1);
gradientLayer.EndPoint = new CGPoint(0, 0);
break;
case GradientColorStackMode.ToBottomRight:
gradientLayer.StartPoint = new CGPoint(0, 0);
gradientLayer.EndPoint = new CGPoint(1, 1);
break;
}
gradientLayer.Frame = rect;
gradientLayer.Colors = colors;
NativeView.Layer.InsertSublayer(gradientLayer, 0);
}
}
}
EDIT: Also here is the xaml and xaml.cs code related to the declaration and calling of the gradient layout.
XAML.CS:
page.ColorsList = Items[0].StartColor+","+Items[0].EndColor;
page.Mode = MyProject.Renderers.GradientColorStackMode.ToBottomLeft;
XAML:
<renderers:GradientLayout
x:Name="page"
Opacity="0"
Mode="ToBottomLeft">
</renders:GradientLayout>
EDIT: I tried to implement Junior Jiang´s, after I did it I noticed 1 change, the IOS class was executing before the constructor of my xaml.cs class, giving me in string[] hex = ColorsList.Split(','); the same error as I previously mentioned because ColorsList had not received any value, so I gave 2 default values to "colors" and implemented a conditional to make sure the constructor code was not executing with ColorsList being null:
public GradientLayout()
{
if(colorsList != null)
{
//colorsList have the following format: "HexCode1,HexCode2"
string[] hex = ColorsList.Split(',');
colors = new Color[hex.Length];
for (int i = 0; i < hex.Length; i++)
{
colors[i] = Color.FromHex(hex[i].Trim());
}
// BindingContext = this;
}
else
{
colors = new Color[2];
colors[0] = Color.FromHex("#000000");
colors[1] = Color.FromHex("#000000");
}
}
After that executing the code did not give any errors, but it was not working as expected since the IOS class only calls the class 1 time (when constructing the view).
I also did not bind anything because I don't understand what I am supposed to do in //BindingContext = this
I think you should implement the INotifyPropertyChanged function something like as follow :
public class GradientLayout : StackLayout, INotifyPropertyChanged
{
private Color[] colors;
public string ColorsList { get; set; }
public Color[] Colors
{
set
{
if (colors != value)
{
colors = value;
OnPropertyChanged("Colors");
}
}
get
{
return colors;
}
}
public GradientLayout()
{
//colorsList have the following format: "HexCode1,HexCode2"
string[] hex = ColorsList.Split(',');
colors = new Color[hex.Length];
for (int i = 0; i < hex.Length; i++)
{
colors[i] = Color.FromHex(hex[i].Trim());
}
// BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And better using Model to bind properties for Control .Having a look at this doc .
==================================Update===================================
You can use Bindable Property to implement that .
Creat a CustomStackLayout :
public class CustomStackLayout : StackLayout
{
public static readonly BindableProperty ColorsProperty = BindableProperty.Create("Colors", typeof(string), typeof(CustomStackLayout), "Default Value");
public string Colors
{
get { return (string)GetValue(ColorsProperty); }
set { SetValue(ColorsProperty, value); }
}
}
In Xaml , adding a Tap method to change property of CustomStackLayout :
<local:CustomStackLayout HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="Gray">
<local:CustomStackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</local:CustomStackLayout.GestureRecognizers>
</local:CustomStackLayout>
The TapGestureRecognizer_Tapped method :
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
Console.WriteLine("--TapGestureRecognizer_Tapped--");
var customStacklayout = sender as CustomStackLayout;
customStacklayout.Colors = "new value";
}
Therefore in iOS Renderer file , there is a OnElementPropertyChanged method can listen which propery changed . Here you also can update UI .
[assembly: ExportRenderer(typeof(CustomStackLayout),typeof(CustomStackLayoutRenderer))]
namespace AppFrameRenderer.iOS
{
public class CustomStackLayoutRenderer : ViewRenderer
{
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var customStack = sender as CustomStackLayout;
Console.WriteLine("----" + customStack.Colors);
if (customStack.Colors == "Default Value")
{
//--
}
else
{
BackgroundColor = UIColor.Red;
}
}
}
}
The effect as follow :

how to update custom control properties at design time immediately?

I created a custom Control which inherits from: Panel Control (using C# Class Library) then I used it inside a Form.
When I select any of the Control Properties (during design time), my custom control won’t update !
However if I close the Form and i reopen it; the changes will take place !
I want to have the Control updated when any of the Properties are changed.
Please find bellow my custom control code:
public class PersoCont : Panel
{
private int borderSize = 2;
private Color borderColor = Color.DarkRed;
private bool isBorder = true;
private int paddingTopTitle = 0;
private int paddingBorder = 0;
public int padding_TopTitle
{
get { return paddingTopTitle; }
set { paddingTopTitle = value; Invalidate(); }
}
public int padding_border
{
get { return paddingBorder; }
set { paddingBorder = value; Invalidate(); }
}
public int border_size
{
get { return borderSize; }
set { borderSize = value; Invalidate(); }
}
public Color border_color
{
get { return borderColor; }
set { borderColor = value; Invalidate(); }
}
public bool is_border
{
get { return isBorder; }
set { isBorder = value; Invalidate(); }
}
public PersoCont()
{
}
protected override void OnHandleCreated(EventArgs e)
{
if (this.Controls.Find("xlblTitle", true).Length == 0)
{
if (isBorder == true)
{
Label lblUp = new Label();
lblUp.Text = "";
lblUp.AutoSize = false;
lblUp.BackColor = borderColor;
int lblUpWidth = this.Width - (2 * paddingBorder) - (2 * borderSize);
lblUp.Size = new Size(lblUpWidth, borderSize);
lblUp.Location = new Point(borderSize + paddingBorder, paddingBorder);
lblUp.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
Label lblDown = new Label();
lblDown.Text = "";
lblDown.AutoSize = false;
lblDown.BackColor = borderColor;
lblDown.Size = new Size(lblUpWidth, borderSize);
int lblDownTop = this.Height - paddingBorder - borderSize;
lblDown.Location = new Point(borderSize + paddingBorder, lblDownTop);
lblDown.Anchor = AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;
Label lblLeft = new Label();
lblLeft.Text = "";
lblLeft.AutoSize = false;
lblLeft.BackColor = borderColor;
int lblLeftHeight = this.Height - (2 * paddingBorder);
lblLeft.Size =new Size(borderSize,lblLeftHeight);
lblLeft.Location = new Point(paddingBorder, paddingBorder);
lblLeft.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom;
Label lblRight = new Label();
lblRight.Text = "";
lblRight.AutoSize = false;
lblRight.BackColor = borderColor;
lblRight.Size = new Size(borderSize, lblLeftHeight);
int lblRightLeft = this.Width - paddingBorder - borderSize;
lblRight.Location = new Point(lblRightLeft, paddingBorder);
lblRight.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
this.Controls.Add(lblUp);
this.Controls.Add(lblDown);
this.Controls.Add(lblLeft);
this.Controls.Add(lblRight);
}
}
base.OnHandleCreated(e);
}
}
Use Refresh instead of Invalidate which only signals to the system that a repaint is needed, but won't actually do it: it will be a call to Refresh which will repaint it.
https://learn.microsoft.com/dotnet/api/system.windows.forms.control.refresh
https://learn.microsoft.com/dotnet/api/system.windows.forms.control.update
You may test if the internal value has changed, before calling Refresh to do it (only if necessary):
public int padding_TopTitle
{
get
{
return paddingTopTitle;
}
set
{
if ( paddingTopTitle != value )
{
paddingTopTitle = value;
Refresh();
}
}
}
You may consider using Framework Design Guidelines and C# Naming Conventions.
For example:
public int PaddingTopTitle
{
get
{
return paddingTopTitle;
}
set
{
if ( paddingTopTitle != value )
{
paddingTopTitle = value;
Refresh();
}
}
}
Or:
public int PaddingTopTitle
{
get
{
return _PaddingTopTitle;
}
set
{
if ( _PaddingTopTitle != value )
{
_PaddingTopTitle = value;
Refresh();
}
}
}
I personally prefer _PaddingTopTitle (or _paddingTopTitle) for private field of a property because paddingTopTitle is used for a method parameter as well as local var.
Therefore you may have for example : BorderSize and _BorderSize (or _borderSize).
Thank you for your help.
Initially I used the OnPaint event, but I really didn't like it because it would fire many times (I don't know why).
My problem was solved as follows:
I created a simple private method that will repaint (refresh the graphics data) according to all my properties. Now everything is working fine.
public class PersoCont2 : Panel
{
Label lblUp = null;
bool isBorder;
Color borderColor;
public bool is_border
{
get { return isBorder; }
set { isBorder = value; Rearrange( ); }
}
public Color border_color
{
get { return borderColor; }
set { borderColor = value; Rearrange( ); }
}
protected override void OnHandleCreated(EventArgs e)
{
Rearrange();
}
private void Rearrange( )
{
if( lblUp != null )
{
Controls.Remove( lblUp );
lblUp = null;
}
if( is_border )
{
lblUp = new Label( );
lblUp.BackColor = borderColor;
// TODO: set properties
// . . .
lblUp.Location = new Point( 10, 10 );
Controls.Add( lblUp );
}
}
}
You can try the following code to rewrite panel control and get what you want.
public class MyPanel : Panel
{
private Color colorBorder = Color.Transparent;
public MyPanel()
: base()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(
new SolidBrush(colorBorder), 6),
e.ClipRectangle);
}
public Color BorderColor
{
get
{
return colorBorder;
}
set
{
colorBorder = value;
}
}
}
Result:
This behaviour however is a little bit awkward because it will only repaint after clicking the form designer.

How to set UserControl's Custom Property, which is a class, from Properties Window

I have created a UserControl which extends PictureBox Control
public partial class AudioMonitor : PictureBox
{
private SelectionSettings _selectionSettings;
[Description("Various settings regarding to the selection visuals"), Category("Custom")]
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public SelectionSettings SelectionSettings
{
get { return this._selectionSettings; }
set { this._selectionSettings = value; }
}
}
SelectionSettings property is a custom class which I have created as follows:
[Serializable]
public class SelectionSettings
{
private SelectionMarker _startMarker;
private SelectionMarker _endMarker;
private SelectionPen _selectionStyle;
public SelectionMarker StartMarker
{
get { return this._startMarker; }
set { this._startMarker = value; }
}
public SelectionMarker EndMarker
{
get { return this._endMarker; }
set { this._endMarker = value; }
}
public SelectionPen SelectionStyle
{
get { return this._selectionStyle; }
set { this._selectionStyle = value; }
}
}
[Serializable]
public class SelectionMarker
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
set { this._style = value; }
}
public float Width
{
get { return this._width; }
set { this._width = value; }
}
public Pen Pen
{
get
{
Pen pen = new Pen(this._color);
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
[Serializable]
public class SelectionPen
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
private float _alpha = 100;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
}
public float Width
{
get { return this._width; }
}
public float Alpha
{
get { return this._alpha; }
}
public int AlphaPercent
{
get { return (int)Math.Round(this._alpha * 100 / 255); }
set
{
if (value > 0 && value <= 100)
this._alpha = (value * 255 / 100);
else
throw new ArgumentException("Alpha percentage should be between (0, 100]");
}
}
public Pen Pen
{
get
{
Pen pen = new Pen(Color.FromArgb((byte)this._alpha, this._color.R, this._color.G, this._color.B));
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
When I place my custom control on a Form and open the Properties Window I can see it as follows :
As you can see I can not set "SelectionSettings" property from the "Properties" window at the design time. What I need is to place the "..." button next to the Property name and open a pop-up to set values.
It should look something like this :
How can I accomplish this task?
What you want to do is add an Editor to AudioMonitor's SelectionSettings property.
To do this, you should create a custom class derived from the UITypeEditor Class.
In order to inherit from UITypeEditor, your project must reference System.Design, which can be done by going to the Project menu, selecting Add Reference to bring up the Reference Manager, navigating to Assemblies->Framework on the left panel and making sure System.Design is checked in the list.
In your custom UITypeEditor derived class, override the method EditValue to bring up a custom dialog that edits a value of type SelectionSettings. Then set the Editor Attribute on your SelectionSettings property to your custom UITypeEditor derived class.
Here's a generic code example of how this would look:
using System;
using System.Drawing.Design;
using System.ComponentModel;
using System.Windows.Forms;
// ...
[Editor(typeof(SomeProperty_Editor), typeof(UITypeEditor))] // You might be able to place this attribute on class SomeType, but I haven't tried yet
public SomeType SomeProperty
{
get { /* stuff */ }
set { /* stuff */ } // optional, really
}
class SomeProperty_Editor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService service = (IWindowsFormsEditorService)(provider.GetService(typeof(IWindowsFormsEditorService)));
SomeProperty_EditorWindow EditorWindow = new SomeProperty_EditorWindow(value as SomeType);
service.ShowDialog(EditorWindow);
if (EditorWindow.EditCancelled)
return value;
else
return EditorWindow.GetEdittedValue();
}
}
class SomeProperty_EditorWindow : Form
{
public SomeProperty_EditorWindow(SomeType CurrentProperty) : base()
{
InitializeComponents();
// Grab info in CurrentProperty here and display it on form
}
public void InitializeComponents()
{
// write yourself or use designer
}
public SomeType GetEdittedValue()
{
// return editted value from form components
}
public bool EditCancelled = false; // Set true if cancel button hit
}
You may want to look up the PropertyGrid Control as it could be very useful in your EditorWindow. Also, you can go to the UITypeEditor Class's MSDN page, look at all of the .NET derived classes under "Inheritance Hierarchy" to see if there is a built in editor that is close in functionality to what you want to do and inherit directly from that class.

C# swap in Designer

I have my Control.When I change the properties of the control. I get this:
this.myLabel1.BorderShadow = true;
this.myLabel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
And I need to get this:
this.myLabel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.myLabel1.BorderShadow = true;
How to do so is done automatically in Form.Desinger.cs ?
If you say Why?
private bool BorderShadow_ = false;
public bool BorderShadow
{
get
{
return BorderShadow_;
}
set
{
if (Border_Style_ == BorderStyle.FixedSingle)
{
BorderShadow_ = value;
}
else
{
throw new ArgumentOutOfRangeException("BorderShadow", "BorderShadow can be true if BorderStyle=FixedSingle");
}
}
}
You could look into the ISupportInitialize interface. It allows you to skip the validity check when initializing your controls. For example (adapted from one of my projects):
public class MyControl : Control, ISupportInitialize
{
private bool _created = true;
public void BeginInit()
{
_created = false;
}
public void EndInit()
{
_created = true;
//check all your properties here too
}
private bool BorderShadow_ = false;
public bool BorderShadow
{
get
{
return BorderShadow_;
}
set
{
BorderShadow_ = value;
if (_created && Border_Style_ != BorderStyle.FixedSingle)
throw new ArgumentOutOfRangeException();
}
}
}
If I remember correctly, the VS designer will automatically add calls to BeginInit and EndInit for you as well.
I think they will appears in the same order as they are defined. So you can override with new old setting:
public new BorderStyle BorderStyle
{
get {return base.BorderStyle;}
set {base.BorderStyle = value;}
}
and then declare your BorderShadow setting.
The designer will always order the properties alphabetically and this can't be changed.
The sense of a property is that it is side effect free and that it can be changed at any time in any order. This means that if you have multiple properties which representing some kind of complex state and not all combinations are making sense, this error should not be reported while switching the property itself.
So to accomplish these problems you have two possibilities:
Like #Andrew already mentioned implement ISupportInitialize and take care if you are within this state.
Within the property setter call a method that checks if all settings currently made are making sense and perform the desired action only in this case:
public class MyControl : Control
{
private bool _BorderShadow;
private BorderStyle _BorderStyle;
public bool BorderShadow
{
get { return _BorderShadow; }
set
{
if(_BorderShadow != value)
{
_BordeShadow = value;
ApplyBorderShadowIfNeeded();
}
}
}
public BorderStyle BorderStyle
{
get { return _BorderStyle; }
set
{
if(_BorderStyle != value)
{
_BorderStyle = value;
ApplyBorderShadowIfNeeded();
}
}
}
private void ApplyBorderShadowIfNeeded()
{
if(_BorderStyle == BorderStyle.FixedSingle
&& _BorderShadow)
{
// ToDo: Apply the shadow to the border.
}
}
}

Accessing AdornerPanel from an AdornerLayout or an Adorner or a adorned Control?

I am trying to add an simple Textblock as adorment to a control. But I want it to be positionned just above my adorned control.
This is the decoration creation ( the problem doesnt rely in this code):
public void AddLabelDecoration()
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
TextBlock textBlockMarkTooltipContent = new TextBlock();
textBlockMarkTooltipContent.Text = "Test Label Adorner";
_labelAdornerMarkTooltipContentAdorner = new Adorner(this)
{
Child = textBlockMarkTooltipContent
};
adornerLayer.Add(_labelAdornerMarkTooltipContentAdorner);
}
What I cannot achieve to do, is the positionning of the Decoration, above the adorned control. I would like to use this MSDN code sample, which makes use of AdornerPanel so as to do the positionning...
However I have not figured out how to access to an AdornerPanel object so as to apply this MSDN code sample... neither from my adorned control, from the AdornedLayout, or the Adorner...
I admit I don't clear understand the WPF class hierarchy between AdornerPanel and AdornerLayout.
Any help appreciated.
public void AddLabelDecoration()
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
TextBlock textBlockMarkTooltipContent = new TextBlock();
textBlockMarkTooltipContent.Text = "Test Label Adorner";
AdornerPanel labelAdornerAdornerPanel = new AdornerPanel();
// add your TextBlock to AdornerPanel
labelAdornerAdornerPanel.Children.Add(textBlockMarkTooltipContent);
// set placements on AdornerPanel
AdornerPlacementCollection placement = new AdornerPlacementCollection();
placement.PositionRelativeToAdornerHeight(-1, 0);
placement.PositionRelativeToAdornerWidth(1, 0);
AdornerPanel.SetPlacements(labelAdornerAdornerPanel, placement);
// create Adorner with AdornerPanel inside
_labelAdornerMarkTooltipContentAdorner = new Adorner(this)
{
Child = labelAdornerAdornerPanel
};
adornerLayer.Add(_labelAdornerMarkTooltipContentAdorner);
}
In order to move your Adorner you have to override the ArrangeOverride method and adjust a new adorner position there.
Here's an example with a simple FrameworkElementAdorner.
public class FrameworkElementAdorner : Adorner
{
private FrameworkElement _child;
public FrameworkElementAdorner(UIElement adornedElement)
: base(adornedElement)
{
}
protected override int VisualChildrenCount
{
get { return 1; }
}
public FrameworkElement Child
{
get { return _child; }
set
{
if (_child != null)
{
RemoveVisualChild(_child);
}
_child = value;
if (_child != null)
{
AddVisualChild(_child);
}
}
}
protected override Visual GetVisualChild(int index)
{
if (index != 0) throw new ArgumentOutOfRangeException();
return _child;
}
protected override Size MeasureOverride(Size constraint)
{
_child.Measure(constraint);
return _child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
// Adjust your offset here:
_child.Arrange(new Rect(new Point(-20, -20), finalSize));
return new Size(_child.ActualWidth, _child.ActualHeight);
}
Usage:
TextBlock textBlockMarkTooltipContent = new TextBlock();
textBlockMarkTooltipContent.Text = "Test Label Adorner";
var adorner = new FrameworkElementAdorner(this)
{
Child = textBlockMarkTooltipContent
};

Categories

Resources