that will automatically add children into separate row. So I make own grid, add method to add new rows and handle it to OnVisualChildrenChanged. But anytime I'm adding new UIElement into it, RowDefinitions.IsReadOnly property is true and I cannot do that.
public class GridWithAutoRows : Grid
{
public void AddRow()
{
this.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
this.AddRow();
Grid.SetRow(visualAdded as UIElement, this.RowDefinitions.Count - 1);
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
}
}
Thanks for any idea.
Related
I have make a ContentControl and it has some custom Propertities. The control itself works fine but I like to update its interface during design time in XAML editor. The problem is next: The control's UI update if I change its Size (SizeChanged event will do that) but I cannot find any way to do this if CustomProperty like OffsetX changes during design time.
So, how to change the following code to make this happen? It isn't too convenient to update Control UI changing its size every time.
public sealed class MyControlElement: ContentControl
{
//
//SOME INITIALIZE CODE IS HERE
//
public MyControlElement() => DefaultStyleKey = typeof(MyControlElement);
protected override void OnApplyTemplate()
{
//
//SOME INITIALIZE CODE IS HERE
//
base.OnApplyTemplate();
}
//OFFSET X DESCRIPTION
[Description("OffsetX"), Category("MyControlElementParameters"), Browsable(true)]
//OFFSET X
public int OffsetX
{
get
{
return (int)GetValue(OffsetXProperty);
}
set
{
if (OffsetX != value)
{
SetValue(OffsetXProperty, value);
OnOffsetXChanged(this, new EventArgs());
}
}
}
public static readonly DependencyProperty OffsetXProperty = DependencyProperty.Register("OffsetX", typeof(int), typeof(MyControlElement), PropertyMetadata.Create(0));
public event EventHandler OffsetXChanged;
private void OnOffsetXChanged(object sender, EventArgs e)
{
UpdateControlUI();
this.OffsetXChanged?.Invoke(this, e);
}
}
I found some kind of "Hack". Still hoping to find better solution. The next trick works and it is possible to update Control interface during design time.
First need to add handler for Loaded.
public MyControlElement()
{
this.DefaultStyleKey = typeof(MyControlElement);
this.Loaded += MyControlElement_Loaded;
}
private void MyControlElement_Loaded(object sender, RoutedEventArgs e)
{
//
//SOME INITIALIZE CODE HERE IF NEEDED
//
//RUN CONTROL VISUAL UPDATER ONLY IF IN DESIGN MODE
if (DesignMode.DesignModeEnabled) ControlDesignTimeUIUpdater();
//FLAG - CONTROL HAS BEEN INITIALIZED
IsControlInitialized = true;
}
And lets add ControlDesignTimeUIUpdater void for UI update. This void has a loop to keep UI updated during design time.
private async void ControlDesignTimeUIUpdater()
{
double OldImageWidth = ImageWidth;
double OldImageHeight = ImageHeight;
CornerRadius OldImageCornerRadius = ImageCornerRadius;
double OldBorderThickness = BorderThickness;
ImageSource OldMyImageSource = MyImageSource;
while (this.IsLoaded)
{
//CHECK CHANGES DELAY 100ms
await Task.Delay(100);
//MAKE SURE CONTROL IS INITIALIZED BEFORE ANY UI UPDATES
if (IsControlInitialized)
{
if (OldImageWidth != ImageWidth)
{
OldImageWidth = ImageWidth;
SetImageWidth();
}
if (OldImageHeight != ImageHeight)
{
OldImageHeight = ImageHeight;
SetImageHeight();
}
if (OldImageCornerRadius != ImageCornerRadius)
{
OldImageCornerRadius = ImageCornerRadius;
SetImageCornerRadius();
}
if (OldBorderThickness != BorderThickness)
{
OldBorderThickness = BorderThickness;
SetBorderThickness();
}
if (OldMyImageSource != MyImageSource)
{
OldMyImageSource = MyImageSource;
SetMyImageSource();
}
//
// ETC.
//
}
}
}
By this Hack it is possible update control in "real-time" during design. It's even possible add animations, size changes etc.
You can make custom panels in UWP XAML by deriving from the Panel class and overriding MeasureOverride and ArrangeOverride. How would I go about doing the same thing for a panel with just one child, similar to the Border control? Unfortunately, I cannot derive from Border because it's sealed.
I tried this but it doesn't work (of course, this is a simplified sample for what I'm trying to achieve):
public sealed class ProportionedPresenter : FrameworkElement
{
public static readonly DependencyProperty ChildProperty = DependencyProperty.Register(
"Child",
typeof(object),
typeof(ProportionedPresenter),
new PropertyMetadata(null));
public object Child
{
get { return this.GetValue(ProportionedPresenter.ChildProperty); }
set { this.SetValue(ProportionedPresenter.ChildProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
var minDimension = Math.Min(availableSize.Width, availableSize.Height);
var desiredSize = new Size(minDimension, minDimension);
if (this.Child is UIElement childElement)
{
childElement.Measure(desiredSize);
var maxDimension = Math.Max(childElement.DesiredSize.Width, childElement.DesiredSize.Height);
desiredSize = new Size(maxDimension, maxDimension);
}
return desiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
if (this.Child is UIElement childElement)
{
childElement.Arrange(new Rect(new Point(), childElement.DesiredSize));
}
return finalSize;
}
}
I have added a margin (For adding breakpoints) to the left side of my TextEditor in the following manner:
public partial class LogicSimViewCodeWPFCtrl : UserControl
{
private class BreakPointMargin : AbstractMargin
{
private const int margin = 20;
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
protected override Size MeasureOverride(Size availableSize)
{
return new Size(margin, 0);
}
}
}
private void LogicCodeInit()
{
try
{
TxtEditCodeViewer.TextArea.LeftMargins.Insert(0, new BreakPointMargin());
...
The margin is added successfully but now I'd like to color the background of the margin. How can I accomplish this?
https://web.archive.org/web/20190716171503/http://community.sharpdevelop.net/forums/t/16047.aspx
You would have to override OnRender:
protected override void OnRender(DrawingContext drawingContext)
{
Size renderSize = this.RenderSize;
drawingContext.DrawRectangle(SystemColors.ControlBrush, null,
new Rect(0, 0, renderSize.Width, renderSize.Height));
Also, you aren't required to derived from AbstractMargin - you can use any WPF control you want. AbstractMargin just provides the TextView and Document properties and keeps them up to date. If you don't need those or can implement them yourself, you can just use another base class.
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
};
I have created my own radio button class – namely MyRadioButton, as the built in .NET class did not enlarge effectively. (using this for touch screen)
MyRadioButton Class works well, expect for an issue which I do not know How to resolve - When I have multiple MyRdaioButtons on a form, I can select all of them.... They somehow do not work as they should where when one selects one the others are automatically be deselected.
My code is as follows:
public class MyRadioButton : Control
{
public MyRadioButton()
{
}
private string textTowrite;
private bool checkStatus;
private int width;
private int height;
public event EventHandler CheckedChanged;
public delegate void MyHandler1(object sender, EventArgs e);
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
if (Checked)
Checked = false;
else
Checked = true;
Invalidate(true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
ButtonState btnstate;
Rectangle rRadioButton;
if (checkStatus)
{
btnstate = ButtonState.Checked;
}
else
btnstate = ButtonState.Normal;
rRadioButton = new Rectangle(0, 0, RBWidth, RBHeight);
FontFamily ft = new FontFamily("Tahoma");
Font fnt_radio = new Font(ft, (int)(18), FontStyle.Bold);
ControlPaint.DrawRadioButton(e.Graphics, -2, 10, rRadioButton.Width,
rRadioButton.Height, btnstate);
//RadioButton's text left justified & centered vertically
e.Graphics.DrawString(textTowrite, fnt_radio, new SolidBrush(Color.Black), rRadioButton.Right + 1, 16);
}
protected virtual void OnCheckedChanged(EventArgs e)
{
if (CheckedChanged != null)
{
CheckedChanged(this, e);
}
}
public override string Text
{
get { return textTowrite; }
set { textTowrite = value; }
}
public bool Checked
{
get { return checkStatus; }
set
{
checkStatus = value;
OnCheckedChanged(EventArgs.Empty);
}
}
public int RBWidth
{
get
{
if (width == 0)
{
width = 40;
}
return width;
}
set
{
if (width != value)
{
width = value;
Invalidate();
}
}
}
public int RBHeight
{
get
{
if (height == 0)
{
height = 40;
}
return height;
}
set
{
if (height != value)
{
height = value;
Invalidate();
}
}
}
}
If someone could provide me with a solution it would be greatly appreciated, as I am pulling out my hair
Thanks
Jens
You may also consider inheriting your control directly from RadioButton, giving you access to the RadioButton.GroupName property, or you will need to implement this type of functionality yourself as kbrinley has posted.
Have you considered using images on a RadioButton control instead? According to ButtonBase's documentation (which RadioButton inherits from):
To have the derived button control
display an image, set the Image
property or the ImageList and
ImageIndex properties.
Note that I have no idea how you'd do selected/unselected states with images... I imagine the ImageList is related to this.
Since this is your control, you will have to provide the logic for this to act like a radio button.
First, I'd suggest placing all of your Radio buttons into a Container control.
Then, at the beginning OnClick method of your control, use the GetContainerControl method to retrieve the Container object and iterate over all of the Radio buttons in the container and set the Checked property of them to false.