WinRT Custom Panel scrolling - c#

I implemented custom Windows.UI.Xaml.Controls.Panel for custom layout items in ListView and put it to <ItemsPanelTemplate>. It's works, but is not possible to scroll.
This answer not applicable because I'm using MVVM data binding.
How to make scrolling work?
My Panel implementation:
public class StaggeredWrapPanel : Panel
{
#region int ItemWidth Property
public int ItemWidth
{
get { return (int)GetValue(ItemWidthProperty); }
set { SetValue(ItemWidthProperty, value); }
}
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(int), typeof(StaggeredWrapPanel), new PropertyMetadata(300, OnItemWidthPropertyChanged));
private static void OnItemWidthPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
(source as StaggeredWrapPanel).InvalidateMeasure();
}
#endregion
protected override Size MeasureOverride(Size availableSize)
{
foreach (var item in Children)
{
item.Measure(new Size(ItemWidth, double.PositiveInfinity));
}
return base.MeasureOverride(availableSize);
}
protected override Size ArrangeOverride(Size finalSize)
{
int columnsCount = (int)(finalSize.Width / ItemWidth);
double[] columnsOffsetsY = new double[columnsCount];
double[] columnsOffsetsX = new double[columnsCount];
for (int i = 0; i < columnsOffsetsX.Length; i++)
{
columnsOffsetsX[i] = i * ItemWidth;
}
int currentColumn = 0;
foreach (var item in Children)
{
if (currentColumn != 0 && columnsOffsetsY[currentColumn] >= columnsOffsetsY[currentColumn - 1])
if (currentColumn == columnsCount - 1)
currentColumn = 0;
else currentColumn++;
Rect rect = new Rect(new Point(columnsOffsetsX[currentColumn], columnsOffsetsY[currentColumn]), new Size(item.DesiredSize.Width, item.DesiredSize.Height));
columnsOffsetsY[currentColumn] += item.DesiredSize.Height;
item.Arrange(rect);
if (currentColumn == 0)
currentColumn++;
if (currentColumn > columnsCount - 1)
currentColumn = 0;
}
return base.ArrangeOverride(finalSize);
}
}

Related

Resizing and positioning panels in another panel

I'm giving my panels inside another panel (this panel is in a usercontrol) a fixed location and a maximum size that changes with the size of the panel there in. Neither the resize or location works properly. The resize does happen but its to quickly. The location is fine if you only have 1 pinpanel for output and input. When you have more then 1 the locations are fixed but you need to resize the panel to resize to see the other panels. Could you point me in the right direction if you see the problem?
I have a panel drawPanel in this case that i use as a sort of background for the usercontrol. Inside this drawPanel i'm placing pinpanels. I want these pinpanels to resize with the usercontrol and give them a fixed location
private void OnClickPinPanel(object source, EventArgs e)
{
if (source is PinPanel p)
{
int index;
if ((index = Array.IndexOf(inputPins, p)) >= 0)
{
ClickedPinPanel?.Invoke(index, true);
}
else
{
ClickedPinPanel?.Invoke(Array.IndexOf(outputPins, p), false);
}
}
//else log here
}
private void CreatePinPanels(bool isInput)
{
int x = 0;
int y = -(int)(this.Width * 0.05)/2;
if (isInput)
{
for (int i = 0; i < inputPins.Length; i++)
{
y += (i + 1) * (this.Height / inputPins.Length + 1);
inputPins[i] = new PinPanel()
{
Location = new Point(x, y),
Size = new Size((int)(this.Width * 0.05), (int)(this.Width * 0.05)),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right,
};
inputPins[i].Click += OnClickPinPanel;
}
}
else
{
x += this.Width - (int)(this.Width * 0.1);
for (int i = 0; i < outputPins.Length; i++)
{
y += (i + 1) * (this.Height / inputPins.Length+1);
outputPins[i] = new PinPanel()
{
Size = new Size((int)(this.Width * 0.1), (int)(this.Width * 0.1)),
Location = new Point(x, y),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right
};
outputPins[i].Click += OnClickPinPanel;
}
}
}
The result i get now is that the pinpanels get a fixed location but when you have more then 1 pinpanel, the location is wrong its like if he thinks that the usercontrol is bigger then it is Reality. In order to see all the pins i have to resize and get this After resize
I want it to look like this
expectations
Try something like this out.
Here is my test rig:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
numericUpDown1.Value = someChip1.NumberInputPins;
numericUpDown2.Value = someChip1.NumberOutputPins;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
someChip1.NumberInputPins = (int)numericUpDown1.Value;
}
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
someChip1.NumberOutputPins = (int)numericUpDown2.Value;
}
}
Sample chip with 5 inputs and 3 outputs:
Here is my PinPanel UserControl (just draws an ellipse/pin the size of the control):
public partial class PinPanel : UserControl
{
public PinPanel()
{
InitializeComponent();
}
private void PinPanel_Paint(object sender, PaintEventArgs e)
{
Rectangle rc = new Rectangle(new Point(0, 0), new Size(this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1));
e.Graphics.DrawEllipse(Pens.Black, rc);
}
private void PinPanel_SizeChanged(object sender, EventArgs e)
{
this.Refresh();
}
}
And finally, my SomeChip UserControl:
public partial class SomeChip : UserControl
{
public event PinPanelClick ClickedPinPanel;
public delegate void PinPanelClick(int index, bool input);
private PinPanel[] inputPins;
private PinPanel[] outputPins;
private int _NumberInputPins = 2;
public int NumberInputPins
{
get {
return _NumberInputPins;
}
set
{
if (value > 0 && value != _NumberInputPins)
{
_NumberInputPins = value;
CreatePinPanels();
this.Refresh();
}
}
}
private int _NumberOutputPins = 1;
public int NumberOutputPins
{
get
{
return _NumberOutputPins;
}
set
{
if (value > 0 && value != _NumberOutputPins)
{
_NumberOutputPins = value;
CreatePinPanels();
this.Refresh();
}
}
}
public SomeChip()
{
InitializeComponent();
}
private void SomeChip_Load(object sender, EventArgs e)
{
CreatePinPanels();
RepositionPins();
}
private void SomeChip_SizeChanged(object sender, EventArgs e)
{
this.Refresh();
}
private void SomeChip_Paint(object sender, PaintEventArgs e)
{
int PinHeight;
// draw the input pin runs
if (inputPins != null)
{
PinHeight = (int)((double)this.Height / (double)_NumberInputPins);
for (int i = 0; i < NumberInputPins; i++)
{
int Y = (i * PinHeight) + (PinHeight / 2);
e.Graphics.DrawLine(Pens.Black, 0, Y, this.Width / 4, Y);
}
}
// draw the output pin runs
if (outputPins != null)
{
PinHeight = (int)((double)this.Height / (double)_NumberOutputPins);
for (int i = 0; i < NumberOutputPins; i++)
{
int Y = (i * PinHeight) + (PinHeight / 2);
e.Graphics.DrawLine(Pens.Black, this.Width - this.Width / 4, Y, this.Width, Y);
}
}
//draw the chip itself (takes up 50% of the width of the UserControl)
Rectangle rc = new Rectangle(new Point(this.Width / 4, 0), new Size(this.Width / 2, this.Height - 1));
using (SolidBrush sb = new SolidBrush(this.BackColor))
{
e.Graphics.FillRectangle(sb, rc);
}
e.Graphics.DrawRectangle(Pens.Black, rc);
RepositionPins();
}
private void CreatePinPanels()
{
if (inputPins != null)
{
for (int i = 0; i < inputPins.Length; i++)
{
if (inputPins[i] != null && !inputPins[i].IsDisposed)
{
inputPins[i].Dispose();
}
}
}
inputPins = new PinPanel[_NumberInputPins];
for (int i = 0; i < inputPins.Length; i++)
{
inputPins[i] = new PinPanel();
inputPins[i].Click += OnClickPinPanel;
this.Controls.Add(inputPins[i]);
}
if (outputPins != null)
{
for (int i = 0; i < outputPins.Length; i++)
{
if (outputPins[i] != null && !outputPins[i].IsDisposed)
{
outputPins[i].Dispose();
}
}
}
outputPins = new PinPanel[_NumberOutputPins];
for (int i = 0; i < outputPins.Length; i++)
{
outputPins[i] = new PinPanel();
outputPins[i].Click += OnClickPinPanel;
this.Controls.Add(outputPins[i]);
}
}
private void OnClickPinPanel(object sender, EventArgs e)
{
PinPanel p = (PinPanel)sender;
if (inputPins.Contains(p))
{
ClickedPinPanel?.Invoke(Array.IndexOf(inputPins, p), true);
}
else if (outputPins.Contains(p))
{
ClickedPinPanel?.Invoke(Array.IndexOf(inputPins, p), false);
}
}
private void RepositionPins()
{
int PinRowHeight, PinHeight;
if (inputPins != null)
{
PinRowHeight = (int)((double)this.Height / (double)_NumberInputPins);
PinHeight = (int)Math.Min((double)(PinRowHeight / 2), (double)this.Height * 0.05);
for (int i = 0; i < inputPins.Length; i++)
{
if (inputPins[i] != null && !inputPins[i].IsDisposed)
{
inputPins[i].SetBounds(0, (int)((i * PinRowHeight) + (PinRowHeight /2 ) - (PinHeight / 2)), PinHeight, PinHeight);
}
}
}
if (outputPins != null)
{
PinRowHeight = (int)((double)this.Height / (double)_NumberOutputPins);
PinHeight = (int)Math.Min((double)(PinRowHeight / 2), (double)this.Height * 0.05);
for (int i = 0; i < outputPins.Length; i++)
{
if (outputPins[i] != null && !outputPins[i].IsDisposed)
{
outputPins[i].SetBounds(this.Width - PinHeight, (int)((i * PinRowHeight) + (PinRowHeight / 2) - (PinHeight / 2)), PinHeight, PinHeight);
}
}
}
}
}

Detect if the TIFF image is Horizontal or Vertical - C#

Im using WinForms. In my Form i have a picturebox and a next button. I use this picturebox to display tiff images and i use the next button to navigate to the next page. The tiff documents are multipage images. The document I'm trying to view has a horizontal images and a vertical images like the example below. If its horizontal i want to size it (1100, 800), but if its vertical i want to size it (800, 1100). How do i do this? currently this is what i have but its not a good solution.
System.Drawing.Image img = System.Drawing.Image.FromFile(path_lbl.Text);
if (img.Height > img.Width)
{
pictureBox1.Width = 800;
pictureBox1.Height = 1300;
}
else
{
pictureBox1.Width = 1300;
pictureBox1.Height = 800;
}
I currently use this approach but this doesn't work because if the first image is vertical the if-statement will always execute the first condition pictureBox1.size(1300 , 800); With this method, if the next image is horizontal the condition will not ever re-size it horizontally.
Example Tiff image
http://www.filedropper.com/verticalandhorizontal
Quick Test Code
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace Demo
{
class TestForm : Form
{
public TestForm()
{
var panel = new Panel { Dock = DockStyle.Top, BorderStyle = BorderStyle.FixedSingle };
openButton = new Button { Text = "Open", Top = 8, Left = 16 };
prevButton = new Button { Text = "Prev", Top = 8, Left = 16 + openButton.Right };
nextButton = new Button { Text = "Next", Top = 8, Left = 16 + prevButton.Right };
path_lbl = new Label { Text = "", Top = 12, Left = 16 + nextButton.Right };
panel.Height = 16 + openButton.Height;
panel.Controls.AddRange(new Control[] { openButton, prevButton, nextButton, path_lbl });
pageViewer = new PictureBox { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.Zoom };
ClientSize = new Size(850, 1100 + panel.Height);
Controls.AddRange(new Control[] { panel, pageViewer });
openButton.Click += OnOpenButtonClick;
prevButton.Click += OnPrevButtonClick;
nextButton.Click += OnNextButtonClick;
Disposed += OnFormDisposed;
UpdatePageInfo();
}
private Button openButton;
private Button prevButton;
private Button nextButton;
private PictureBox pageViewer;
private PageBuffer pageData;
private int currentPage;
private Size pageSize;
public string path;
private Label path_lbl;
private void OnOpenButtonClick(object sender, EventArgs e)
{
using (var dialog = new OpenFileDialog())
{
if (dialog.ShowDialog(this) == DialogResult.OK)
Open(dialog.FileName);
path = dialog.FileName;
}
}
private void OnPrevButtonClick(object sender, EventArgs e)
{
SelectPage(currentPage - 1);
}
private void OnNextButtonClick(object sender, EventArgs e)
{
//var data = PageBuffer.Open(path,Size= new Size(850,1150));
SelectPage(currentPage + 1);
//Debug.WriteLine("Current Size: 1300, 800");
}
private void OnFormDisposed(object sender, EventArgs e)
{
if (pageData != null)
pageData.Dispose();
}
private void Open(string path)
{
var data = PageBuffer.Open(path, new Size(1500, 1500));
pageViewer.Image = null;
if (pageData != null)
pageData.Dispose();
pageData = data;
SelectPage(0);
}
private void SelectPage(int index)
{
pageViewer.Image = pageData.GetPage(index);
currentPage = index;
UpdatePageInfo();
}
private void UpdatePageInfo()
{
prevButton.Enabled = pageData != null && currentPage > 0;
nextButton.Enabled = pageData != null && currentPage < pageData.PageCount - 1;
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
class PageBuffer : IDisposable
{
public const int DefaultCacheSize = 12; //This is how much images it will have in memory
public static PageBuffer Open(string path, Size maxSize, int cacheSize = DefaultCacheSize)
{
return new PageBuffer(File.OpenRead(path), maxSize, cacheSize);
}
private PageBuffer(Stream stream, Size maxSize, int cacheSize)
{
this.stream = stream;
source = Image.FromStream(stream);
pageCount = source.GetFrameCount(FrameDimension.Page);
if (pageCount < 2) return;
pageCache = new Image[Math.Min(pageCount, Math.Max(cacheSize, 5))];
pageSize = source.Size;
if (!maxSize.IsEmpty)
{
float scale = Math.Min((float)maxSize.Width / pageSize.Width, (float)maxSize.Height / pageSize.Height);
pageSize = new Size((int)(pageSize.Width * scale), (int)(pageSize.Height * scale));
}
var worker = new Thread(LoadPages) { IsBackground = true };
worker.Start();
}
private void LoadPages()
{
while (true)
{
lock (syncLock)
{
if (disposed) return;
int index = Array.FindIndex(pageCache, 0, pageCacheSize, p => p == null);
if (index < 0)
Monitor.Wait(syncLock);
else
pageCache[index] = LoadPage(pageCacheStart + index);
}
}
}
private Image LoadPage(int index)
{
source.SelectActiveFrame(FrameDimension.Page, index);
return new Bitmap(source, pageSize);
}
private Stream stream;
private Image source;
private int pageCount;
private Image[] pageCache;
private int pageCacheStart, pageCacheSize;
private object syncLock = new object();
private bool disposed;
private Size pageSize;
public Image Source { get { return source; } }
public int PageCount { get { return pageCount; } }
public Image GetPage(int index)
{
if (disposed) throw new ObjectDisposedException(GetType().Name);
if (PageCount < 2) return Source;
lock (syncLock)
{
AdjustPageCache(index);
int cacheIndex = index - pageCacheStart;
var image = pageCache[cacheIndex];
if (image == null)
image = pageCache[cacheIndex] = LoadPage(index);
return image;
}
}
private void AdjustPageCache(int pageIndex)
{
int start, end;
if ((start = pageIndex - pageCache.Length / 2) <= 0)
end = (start = 0) + pageCache.Length;
else if ((end = start + pageCache.Length) >= PageCount)
start = (end = PageCount) - pageCache.Length;
if (start < pageCacheStart)
{
int shift = pageCacheStart - start;
if (shift >= pageCacheSize)
ClearPageCache(0, pageCacheSize);
else
{
ClearPageCache(pageCacheSize - shift, pageCacheSize);
for (int j = pageCacheSize - 1, i = j - shift; i >= 0; j--, i--)
Exchange(ref pageCache[i], ref pageCache[j]);
}
}
else if (start > pageCacheStart)
{
int shift = start - pageCacheStart;
if (shift >= pageCacheSize)
ClearPageCache(0, pageCacheSize);
else
{
ClearPageCache(0, shift);
for (int j = 0, i = shift; i < pageCacheSize; j++, i++)
Exchange(ref pageCache[i], ref pageCache[j]);
}
}
if (pageCacheStart != start || pageCacheStart + pageCacheSize != end)
{
pageCacheStart = start;
pageCacheSize = end - start;
Monitor.Pulse(syncLock);
}
}
void ClearPageCache(int start, int end)
{
for (int i = start; i < end; i++)
Dispose(ref pageCache[i]);
}
static void Dispose<T>(ref T target) where T : class, IDisposable
{
var value = target;
if (value != null) value.Dispose();
target = null;
}
static void Exchange<T>(ref T a, ref T b) { var c = a; a = b; b = c; }
public void Dispose()
{
if (disposed) return;
lock (syncLock)
{
disposed = true;
if (pageCache != null)
{
ClearPageCache(0, pageCacheSize);
pageCache = null;
}
Dispose(ref source);
Dispose(ref stream);
if (pageCount > 2)
Monitor.Pulse(syncLock);
}
}
}
}

Most Efficient way to do a swipe animation in Xamarin.forms - Android

I had to implement a way to navigate between different element that are stacked in xamarin.forms. In the code below, you can see that we already manage the ability to distinguish every single one of the child of the container. At the moment, what I have written works but I need some help figuring out if it’s the good way or if there is a better way or even, if I can improve what I’ve done.
Currently, when you swipe from left to right or whatever move, I just use a SimpleOnGestureListener.
Here are some samples of what we are currently achieving:
I declared my custom elements such as the container & the children and then I created the customRenderer for each of them in an android library (only working on android at the moment).
“MyContent” is simply a modified frame with a specific Draw method and an event for each move available (up, down, left, right).
Code of the container
using System;
using Xamarin.Forms;
namespace Elements
{
public class MyContainer : Layout<MyContent>
{
public MyContainer ()
{
}
protected override void LayoutChildren (double x, double y, double width, double height)
{
for (int i = 0; i < Children.Count ; i++) {
MyContent child = Children[i];
// skip invisible children
if(!child.IsVisible)
continue;
int soffset = Children.Count - i;
int eoffset = soffset - 1;
Rectangle startBoundingRegion = new Rectangle (12 * soffset + Padding.Left, 14 * soffset + Padding.Top, width - 24 * soffset, height - 8 * soffset);
Rectangle endBoundingRegion = new Rectangle (12 * eoffset + Padding.Left, 14 * eoffset + Padding.Top, width - 24 * eoffset, height - 8 * eoffset);
child.OriginalBounds = endBoundingRegion;
LayoutChildIntoBoundingRegion (child,startBoundingRegion);
child.LayoutTo(endBoundingRegion,100,Easing.Linear);
}
}
protected override SizeRequest OnSizeRequest (double widthConstraint, double heightConstraint)
{
var height = 0;
var minHeight = 0;
var width = 0;
var minWidth = 0;
for (int i = 0; i < Children.Count; i++) {
MyContent child = Children[i];
// skip invisible children
if(!child.IsVisible)
continue;
var childSizeRequest = child.GetSizeRequest (double.PositiveInfinity, height);
height = (int)Math.Max (height, childSizeRequest.Minimum.Height);
minHeight = (int)Math.Max (minHeight, childSizeRequest.Minimum.Height);
width += (int)childSizeRequest.Request.Width;
minWidth += (int)childSizeRequest.Minimum.Width;
}
return new SizeRequest (new Size (width, height), new Size (minWidth, minHeight));
}
}
}
Code of its renderer
{using ...}
[assembly: ExportRenderer (typeof(MyContainer), typeof(MyContainerRenderer))]
namespace MyApp
{
public class MyContainerRenderer : ViewRenderer<Layout<MyContent>,Android.Views.View>
{
private readonly MyContentGestureListener listener;
private readonly GestureDetector detector;
private bool isAnimating;
private MyContent currentMyContent;
public MyContainerRenderer ()
{
listener = new MyContentGestureListener();
detector = new GestureDetector(listener);
}
protected override void OnElementChanged (ElementChangedEventArgs<Layout<MyContent>> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
this.Touch -= HandleTouch;
listener.OnSwipeLeft -= HandleOnSwipeLeft;
listener.OnSwipeRight -= HandleOnSwipeRight;
listener.OnSwipeTop -= HandleOnSwipeTop;
listener.OnSwipeDown -= HandleOnSwipeDown;
listener.OnPanVertical -= HandleOnPanVertical;
listener.OnPanHorizontal -= HandleOnPanHorizontal;
}
if (e.OldElement == null)
{
this.Touch += HandleTouch;
listener.OnSwipeLeft += HandleOnSwipeLeft;
listener.OnSwipeRight += HandleOnSwipeRight;
listener.OnSwipeTop += HandleOnSwipeTop;
listener.OnSwipeDown += HandleOnSwipeDown;
listener.OnPanVertical += HandleOnPanVertical;
listener.OnPanHorizontal += HandleOnPanHorizontal;
}
}
void HandleTouch(object sender, TouchEventArgs e)
{
currentMyContent = ((MyContainer)this.Element).Children.Last();
if (!isAnimating) {
if (e.Event.Action == MotionEventActions.Down) {
currentMyContent.ScaleTo (1.01, 100, Easing.Linear);
} else if ( e.Event.Action == MotionEventActions.Up){
if (!isAnimating) {
isAnimating = true;
currentMyContent.ScaleTo (1, 100, Easing.Linear);
currentMyContent.LayoutTo (currentMyContent.OriginalBounds, 100, Easing.CubicIn);
isAnimating = false;
listener.ResetFlags ();
}
}
detector.OnTouchEvent (e.Event);
}
}
void HandleOnPanVertical(object sender, EventArgs distanceY)
{
Rectangle dest = new Rectangle (currentMyContent.Bounds.X,currentMyContent.Bounds.Y - ((EventArgs<float>)distanceY).Value,currentMyContent.Bounds.Width,currentMyContent.Bounds.Height);
currentMyContent.Layout(dest);
}
void HandleOnPanHorizontal(object sender, EventArgs distanceX)
{
Rectangle dest = new Rectangle (currentMyContent.Bounds.X - ((EventArgs<float>)distanceX).Value,currentMyContent.Bounds.Y,currentMyContent.Bounds.Width,currentMyContent.Bounds.Height);
currentMyContent.Layout (dest);
}
async void animateNext (MyContent mContent, Rectangle dest)
{
isAnimating = true;
await mContent.LayoutTo (dest, 150, Easing.Linear);
((MyContainer)this.Element).Children.Remove (mContent);
((MyContainer)this.Element).Children.Insert (0, mContent);
isAnimating = false;
}
void HandleOnSwipeLeft(object sender, EventArgs e)
{
currentMyContent.OnSwipeLeft();
Rectangle dest = new Rectangle (currentMyContent.Bounds.X,currentMyContent.Bounds.Y,currentMyContent.Bounds.Width,currentMyContent.Bounds.Height);
dest.Left -= Width;
animateNext (currentMyContent, dest);
}
void HandleOnSwipeRight(object sender, EventArgs e)
{
currentMyContent.OnSwipeRight();
Rectangle dest = new Rectangle (currentMyContent.Bounds.X,currentMyContent.Bounds.Y,currentMyContent.Bounds.Width,currentMyContent.Bounds.Height);
dest.Left += Width;
animateNext (currentMyContent, dest);
}
void HandleOnSwipeTop(object sender, EventArgs e)
{
Rectangle dest = new Rectangle (currentMyContent.Bounds.X, currentMyContent.Bounds.Y, currentMyContent.Bounds.Width, currentMyContent.Bounds.Height);
dest.Top -= Height;
animateNext (currentMyContent, dest);
}
void HandleOnSwipeDown(object sender, EventArgs e)
{
Rectangle dest = new Rectangle (currentMyContent.Bounds.X,currentMyContent.Bounds.Y,currentMyContent.Bounds.Width,currentMyContent.Bounds.Height);
dest.Top += Height;
animateNext (currentMyContent, dest);
}
}
class MyContentGestureListener : GestureDetector.SimpleOnGestureListener
{
private static int SWIPE_THRESHOLD = 50;
private static int SWIPE_VELOCITY_THRESHOLD = 20;
private bool isPanningV;
private bool isPanningH;
public event EventHandler OnSwipeDown;
public event EventHandler OnSwipeTop;
public event EventHandler OnSwipeLeft;
public event EventHandler OnSwipeRight;
public event EventHandler OnPanHorizontal;
public event EventHandler OnPanVertical;
public override bool OnScroll(MotionEvent ev1, MotionEvent ev2,float distanceX,float distanceY)
{
if (Math.Abs (distanceX) > Math.Abs (distanceY) && OnPanHorizontal != null && !isPanningV) {
isPanningH = true;
OnPanHorizontal (this,new EventArgs<float> (distanceX));
} else if (OnPanVertical != null && !isPanningH) {
isPanningV = true;
OnPanVertical (this, new EventArgs<float> (distanceY));
}
return base.OnScroll (ev1, ev2, distanceX, distanceY);
}
public override bool OnFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
float diffY = e2.GetY() - e1.GetY();
float diffX = e2.GetX() - e1.GetX();
if (Math.Abs(diffX) > Math.Abs(diffY) && Math.Abs(diffX) > SWIPE_THRESHOLD && Math.Abs(velocityX) > SWIPE_VELOCITY_THRESHOLD)
{
if (diffX > 0 && OnSwipeRight != null)
{
OnSwipeRight (this, null);
isPanningH = false;
}
else if (OnSwipeLeft != null) {
OnSwipeLeft (this, null);
isPanningH = false;
}
}
else if (Math.Abs(diffY) > SWIPE_THRESHOLD && Math.Abs(velocityY) > SWIPE_VELOCITY_THRESHOLD)
{
if (diffY > 0 && OnSwipeDown != null)
{
OnSwipeDown (this, null);
isPanningV = false;
}
else if (OnSwipeTop != null) {
OnSwipeTop (this, null);
isPanningV = false;
}
}
return base.OnFling (e1, e2, velocityX, velocityY);
}
public void ResetFlags(){
isPanningH = false;
isPanningV = false;
}
}
public class EventArgs<T> : EventArgs
{
public EventArgs(T value)
{
Value = value;
}
public T Value { get; private set; }
}
}

WrapPanel with last-in-row fill

What I need: listbox with textboxes inside, textboxes wraps, and last in row fills remaining space:
|word 1||word 2___|
|word 3___________|
I'm trying to implement this behaviour using that advice. My xaml:
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Tags}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<controls:WrapPanelLastChildFill />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MyWrapPanel (inherits form WrapPanel) code:
protected override Size MeasureOverride(Size constraint)
{
Size curLineSize = new Size();
Size panelSize = new Size(constraint.Width, 0);
UIElementCollection children = base.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
UIElement child = children[i] as UIElement;
child.Measure(constraint);
Size sz = child.DesiredSize;
if (curLineSize.Width + sz.Width > constraint.Width) // new line
{
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
if (i > 0)
{
// change width of prev control here
var lastChildInRow = children[i - 1] as Control;
lastChildInRow.Width = lastChildInRow.ActualWidth + panelSize.Width - curLineSize.Width;
}
curLineSize = sz;
}
else
{
curLineSize.Width += sz.Width;
curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
}
}
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
return panelSize;
}
Thats work, but in one side only - textbox width never shrinks.
Any help appreciated.
I have done it recently.
You can see my code at: CodeProject
or use this class directly as shown:
Usage:
<TextBox MinWidth="120" wrapPanelWithFill:WrapPanelFill.UseToFill="True">*</TextBox>
Code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
namespace WrapPanelWithFill
{
public class WrapPanelFill : WrapPanel
{
// ******************************************************************
public static readonly DependencyProperty UseToFillProperty = DependencyProperty.RegisterAttached("UseToFill", typeof(Boolean),
typeof(WrapPanelFill), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
// ******************************************************************
public static void SetUseToFill(UIElement element, Boolean value)
{
element.SetValue(UseToFillProperty, value);
}
// ******************************************************************
public static Boolean GetUseToFill(UIElement element)
{
return (Boolean)element.GetValue(UseToFillProperty);
}
// ******************************************************************
const double DBL_EPSILON = 2.2204460492503131e-016; /* smallest such that 1.0+DBL_EPSILON != 1.0 */
// ******************************************************************
private static bool DoubleAreClose(double value1, double value2)
{
//in case they are Infinities (then epsilon check does not work)
if (value1 == value2) return true;
// This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON;
double delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
// ******************************************************************
private static bool DoubleGreaterThan(double value1, double value2)
{
return (value1 > value2) && !DoubleAreClose(value1, value2);
}
// ******************************************************************
private bool _atLeastOneElementCanHasItsWidthExpanded = false;
// ******************************************************************
/// <summary>
/// <see cref="FrameworkElement.MeasureOverride"/>
/// </summary>
protected override Size MeasureOverride(Size constraint)
{
UVSize curLineSize = new UVSize(Orientation);
UVSize panelSize = new UVSize(Orientation);
UVSize uvConstraint = new UVSize(Orientation, constraint.Width, constraint.Height);
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
bool itemWidthSet = !Double.IsNaN(itemWidth);
bool itemHeightSet = !Double.IsNaN(itemHeight);
Size childConstraint = new Size(
(itemWidthSet ? itemWidth : constraint.Width),
(itemHeightSet ? itemHeight : constraint.Height));
UIElementCollection children = InternalChildren;
// EO
LineInfo currentLineInfo = new LineInfo(); // EO, the way it works it is always like we are on the current line
_lineInfos.Clear();
_atLeastOneElementCanHasItsWidthExpanded = false;
for (int i = 0, count = children.Count; i < count; i++)
{
UIElement child = children[i] as UIElement;
if (child == null) continue;
//Flow passes its own constrint to children
child.Measure(childConstraint);
//this is the size of the child in UV space
UVSize sz = new UVSize(
Orientation,
(itemWidthSet ? itemWidth : child.DesiredSize.Width),
(itemHeightSet ? itemHeight : child.DesiredSize.Height));
if (DoubleGreaterThan(curLineSize.U + sz.U, uvConstraint.U)) //need to switch to another line
{
// EO
currentLineInfo.Size = curLineSize;
_lineInfos.Add(currentLineInfo);
panelSize.U = Math.Max(curLineSize.U, panelSize.U);
panelSize.V += curLineSize.V;
curLineSize = sz;
// EO
currentLineInfo = new LineInfo();
var feChild = child as FrameworkElement;
if (GetUseToFill(feChild))
{
currentLineInfo.ElementsWithNoWidthSet.Add(feChild);
_atLeastOneElementCanHasItsWidthExpanded = true;
}
if (DoubleGreaterThan(sz.U, uvConstraint.U)) //the element is wider then the constrint - give it a separate line
{
currentLineInfo = new LineInfo();
panelSize.U = Math.Max(sz.U, panelSize.U);
panelSize.V += sz.V;
curLineSize = new UVSize(Orientation);
}
}
else //continue to accumulate a line
{
curLineSize.U += sz.U;
curLineSize.V = Math.Max(sz.V, curLineSize.V);
// EO
var feChild = child as FrameworkElement;
if (GetUseToFill(feChild))
{
currentLineInfo.ElementsWithNoWidthSet.Add(feChild);
_atLeastOneElementCanHasItsWidthExpanded = true;
}
}
}
if (curLineSize.U > 0)
{
currentLineInfo.Size = curLineSize;
_lineInfos.Add(currentLineInfo);
}
//the last line size, if any should be added
panelSize.U = Math.Max(curLineSize.U, panelSize.U);
panelSize.V += curLineSize.V;
// EO
if (_atLeastOneElementCanHasItsWidthExpanded)
{
return new Size(constraint.Width, panelSize.Height);
}
//go from UV space to W/H space
return new Size(panelSize.Width, panelSize.Height);
}
// ************************************************************************
private struct UVSize
{
internal UVSize(Orientation orientation, double width, double height)
{
U = V = 0d;
_orientation = orientation;
Width = width;
Height = height;
}
internal UVSize(Orientation orientation)
{
U = V = 0d;
_orientation = orientation;
}
internal double U;
internal double V;
private Orientation _orientation;
internal double Width
{
get { return (_orientation == Orientation.Horizontal ? U : V); }
set { if (_orientation == Orientation.Horizontal) U = value; else V = value; }
}
internal double Height
{
get { return (_orientation == Orientation.Horizontal ? V : U); }
set { if (_orientation == Orientation.Horizontal) V = value; else U = value; }
}
}
// ************************************************************************
private class LineInfo
{
public List<UIElement> ElementsWithNoWidthSet = new List<UIElement>();
// public double SpaceLeft = 0;
// public double WidthCorrectionPerElement = 0;
public UVSize Size;
public double Correction = 0;
}
private List<LineInfo> _lineInfos = new List<LineInfo>();
// ************************************************************************
/// <summary>
/// <see cref="FrameworkElement.ArrangeOverride"/>
/// </summary>
protected override Size ArrangeOverride(Size finalSize)
{
int lineIndex = 0;
int firstInLine = 0;
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
double accumulatedV = 0;
double itemU = (Orientation == Orientation.Horizontal ? itemWidth : itemHeight);
UVSize curLineSize = new UVSize(Orientation);
UVSize uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height);
bool itemWidthSet = !Double.IsNaN(itemWidth);
bool itemHeightSet = !Double.IsNaN(itemHeight);
bool useItemU = (Orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet);
UIElementCollection children = InternalChildren;
for (int i = 0, count = children.Count; i < count; i++)
{
UIElement child = children[i] as UIElement;
if (child == null) continue;
UVSize sz = new UVSize(
Orientation,
(itemWidthSet ? itemWidth : child.DesiredSize.Width),
(itemHeightSet ? itemHeight : child.DesiredSize.Height));
if (DoubleGreaterThan(curLineSize.U + sz.U, uvFinalSize.U)) //need to switch to another line
{
arrangeLine(lineIndex, accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU, uvFinalSize);
lineIndex++;
accumulatedV += curLineSize.V;
curLineSize = sz;
if (DoubleGreaterThan(sz.U, uvFinalSize.U)) //the element is wider then the constraint - give it a separate line
{
//switch to next line which only contain one element
arrangeLine(lineIndex, accumulatedV, sz.V, i, ++i, useItemU, itemU, uvFinalSize);
accumulatedV += sz.V;
curLineSize = new UVSize(Orientation);
}
firstInLine = i;
}
else //continue to accumulate a line
{
curLineSize.U += sz.U;
curLineSize.V = Math.Max(sz.V, curLineSize.V);
}
}
//arrange the last line, if any
if (firstInLine < children.Count)
{
arrangeLine(lineIndex, accumulatedV, curLineSize.V, firstInLine, children.Count, useItemU, itemU, uvFinalSize);
}
return finalSize;
}
// ************************************************************************
private void arrangeLine(int lineIndex, double v, double lineV, int start, int end, bool useItemU, double itemU, UVSize uvFinalSize)
{
double u = 0;
bool isHorizontal = (Orientation == Orientation.Horizontal);
Debug.Assert(lineIndex < _lineInfos.Count);
LineInfo lineInfo = _lineInfos[lineIndex];
double lineSpaceAvailableForCorrection = Math.Max(uvFinalSize.U - lineInfo.Size.U, 0);
double perControlCorrection = 0;
if (lineSpaceAvailableForCorrection > 0 && lineInfo.Size.U > 0)
{
perControlCorrection = lineSpaceAvailableForCorrection / lineInfo.ElementsWithNoWidthSet.Count;
if (double.IsInfinity(perControlCorrection))
{
perControlCorrection = 0;
}
}
int indexOfControlToAdjustSizeToFill = 0;
UIElement uIElementToAdjustNext = indexOfControlToAdjustSizeToFill < lineInfo.ElementsWithNoWidthSet.Count ? lineInfo.ElementsWithNoWidthSet[indexOfControlToAdjustSizeToFill] : null;
UIElementCollection children = InternalChildren;
for (int i = start; i < end; i++)
{
UIElement child = children[i] as UIElement;
if (child != null)
{
UVSize childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
double layoutSlotU = (useItemU ? itemU : childSize.U);
if (perControlCorrection > 0 && child == uIElementToAdjustNext)
{
layoutSlotU += perControlCorrection;
indexOfControlToAdjustSizeToFill++;
uIElementToAdjustNext = indexOfControlToAdjustSizeToFill < lineInfo.ElementsWithNoWidthSet.Count ? lineInfo.ElementsWithNoWidthSet[indexOfControlToAdjustSizeToFill] : null;
}
child.Arrange(new Rect(
(isHorizontal ? u : v),
(isHorizontal ? v : u),
(isHorizontal ? layoutSlotU : lineV),
(isHorizontal ? lineV : layoutSlotU)));
u += layoutSlotU;
}
}
}
// ************************************************************************
}
}
It was misunderstanding where to place width correction. This must be in ArrangeOverride:
private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end)
{
double x = 0;
UIElementCollection children = InternalChildren;
for (int i = start; i < end; i++)
{
UIElement child = children[i];
var w = child.DesiredSize.Width;
if (LastChildFill && i == end - 1) // last сhild fills remaining space
{
w = boundsWidth - x;
}
child.Arrange(new Rect(x, y, w, lineSize.Height));
x += w;
}
}

Textblock added to Panel resizes itself automatically

I have a custom Panel for laying out text. There is a DependancyProperty called "Text" and when that value changes, this piece of code runs:
if( !string.IsNullOrEmpty(Text))
{
Children.Clear();
foreach (char ch in Text)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = ch.ToString();
textBlcok.Foreground = Foreground;
//The rest of these are DPs in the panel
textBlock.FontFamily = FontFamily;
textBlock.FontStyle = FontStyle;
textBlock.FontWeight = FontWeight;
textBlock.FontStretch = FontStretch;
textBlock.FontSize = FontSize;
Children.Add(textBlock);
}
}
}
Now, with font size of 15 and font Arial, these should be giving me a desired size of around 8 width and 10 height. However, when I do a Measure() and check the desired size, I get 40,18 every time!
So in trying to figure out what could've possibly changed the size, I put this code before and after the Children.Add in the code above:
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);
Children.Add(textBlock);
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);
What this gave me, was the proper desired size before it's added to the children collection, and a size of 40,18 (regardless of letter) after it's added to the collection.
What is causing this to happen?
Edit: You can find the full source for the control here:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using IQ.Touch.Resources.Classes.Helpers;
/* TextOnAPath.cs
*
* A slightly modified version of the control found at
* http://www.codeproject.com/KB/WPF/TextOnAPath.aspx
*/
namespace IQ.Touch.Resources.Controls
{
public class TextOnAPath : Panel
{
// Fields
PathFigureHelper pathFigureHelper = new PathFigureHelper();
Size totalSize;
// Dependency properties
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(TextOnAPath),
new PropertyMetadata(OnFontPropertyChanged));
public static readonly DependencyProperty FontFamilyProperty =
DependencyProperty.Register("FontFamily",
typeof(FontFamily),
typeof(TextOnAPath),
new PropertyMetadata(new FontFamily("Portable User Interface"), OnFontPropertyChanged));
public static readonly DependencyProperty FontStyleProperty =
DependencyProperty.Register("FontStyle",
typeof(FontStyle),
typeof(TextOnAPath),
new PropertyMetadata(FontStyles.Normal, OnFontPropertyChanged));
public static readonly DependencyProperty FontSizeProperty =
DependencyProperty.Register("FontSize",
typeof(double),
typeof(TextOnAPath),
new PropertyMetadata(12.0, OnFontPropertyChanged));
public static readonly DependencyProperty FontWeightProperty =
DependencyProperty.Register("FontWeight",
typeof(FontWeight),
typeof(TextOnAPath),
new PropertyMetadata(FontWeights.Normal, OnFontPropertyChanged));
public static readonly DependencyProperty FontStretchProperty =
DependencyProperty.Register("FontStretch",
typeof(FontStretch),
typeof(TextOnAPath),
new PropertyMetadata(FontStretches.Normal, OnFontPropertyChanged));
public static readonly DependencyProperty ForegroundProperty =
DependencyProperty.Register("Foreground",
typeof(Brush),
typeof(TextOnAPath),
new PropertyMetadata(new SolidColorBrush(Colors.Black), OnFontPropertyChanged));
public static readonly DependencyProperty PathFigureProperty =
DependencyProperty.Register("PathFigure",
typeof(PathFigure),
typeof(TextOnAPath),
new PropertyMetadata(OnPathFigureChanged));
// Properties
public string Text
{
set { SetValue(TextProperty, value); }
get { return (string)GetValue(TextProperty); }
}
public FontFamily FontFamily
{
set { SetValue(FontFamilyProperty, value); }
get { return (FontFamily)GetValue(FontFamilyProperty); }
}
public FontStyle FontStyle
{
set { SetValue(FontStyleProperty, value); }
get { return (FontStyle)GetValue(FontStyleProperty); }
}
public double FontSize
{
set { SetValue(FontSizeProperty, value); }
get { return (double)GetValue(FontSizeProperty); }
}
public FontWeight FontWeight
{
set { SetValue(FontWeightProperty, value); }
get { return (FontWeight)GetValue(FontWeightProperty); }
}
public FontStretch FontStretch
{
set { SetValue(FontStretchProperty, value); }
get { return (FontStretch)GetValue(FontStretchProperty); }
}
public Brush Foreground
{
set { SetValue(ForegroundProperty, value); }
get { return (Brush)GetValue(ForegroundProperty); }
}
public PathFigure PathFigure
{
set { SetValue(PathFigureProperty, value); }
get { return (PathFigure)GetValue(PathFigureProperty); }
}
// Property-changed handlers
static void OnFontPropertyChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
(obj as TextOnAPath).OnFontPropertyChanged(args);
}
void OnFontPropertyChanged(DependencyPropertyChangedEventArgs args)
{
Children.Clear();
if (String.IsNullOrEmpty(Text))
return;
foreach (char ch in Text)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = ch.ToString();
textBlock.FontFamily = FontFamily;
textBlock.FontStyle = FontStyle;
textBlock.FontWeight = FontWeight;
textBlock.FontStretch = FontStretch;
textBlock.FontSize = FontSize;
textBlock.Foreground = Foreground;
textBlock.HorizontalAlignment = HorizontalAlignment.Center;
textBlock.VerticalAlignment = VerticalAlignment.Bottom;
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);
Children.Add(textBlock);
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Console.WriteLine(ch.ToString() + ": " + textBlock.DesiredSize);
}
CalculateTransforms();
InvalidateMeasure();
}
static void OnPathFigureChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
(obj as TextOnAPath).OnPathFigureChanged(args);
}
void OnPathFigureChanged(DependencyPropertyChangedEventArgs args)
{
pathFigureHelper.SetPathFigure(args.NewValue as PathFigure);
CalculateTransforms();
InvalidateMeasure();
}
void CalculateTransforms()
{
double pathLength = pathFigureHelper.Length;
double textLength = 0;
double textDesiredWidth = 9;
totalSize = new Size();
foreach (UIElement child in Children)
{
child.Measure(new Size(Double.PositiveInfinity,
Double.PositiveInfinity));
textLength += child.DesiredSize.Width;
}
//textLength = Children.Count * textDesiredWidth;
if (pathLength == 0 || textLength == 0)
return;
//double scalingFactor = pathLength / textLength;
double baseline = FontSize; // * FontFamily.Baseline;
double progress = 0;
if (textLength <= pathLength)
{
progress = ((pathLength - textLength) / 2) / pathLength;
}
foreach (UIElement child in Children)
{
double width = child.DesiredSize.Width;
//double width = textDesiredWidth;
progress += width / 2 / pathLength;
Point point, tangent;
pathFigureHelper.GetPointAtFractionLength(progress,
out point, out tangent);
TransformGroup transformGroup = new TransformGroup();
//ScaleTransform scaleTransform = new ScaleTransform();
//scaleTransform.ScaleX = scalingFactor;
//scaleTransform.ScaleY = scalingFactor;
//transformGroup.Children.Add(scaleTransform);
RotateTransform rotateTransform = new RotateTransform();
rotateTransform.Angle = Math.Atan2(tangent.Y, tangent.X) * 180 / Math.PI;
rotateTransform.CenterX = width / 2;
rotateTransform.CenterY = baseline;
transformGroup.Children.Add(rotateTransform);
TranslateTransform translateTransform = new TranslateTransform();
translateTransform.X = point.X - width / 2;
translateTransform.Y = point.Y - baseline;
transformGroup.Children.Add(translateTransform);
child.RenderTransform = transformGroup;
BumpUpTotalSize(transformGroup.Value, new Point(0, 0));
BumpUpTotalSize(transformGroup.Value, new Point(0, child.DesiredSize.Height));
BumpUpTotalSize(transformGroup.Value, new Point(child.DesiredSize.Width, 0));
BumpUpTotalSize(transformGroup.Value, new Point(child.DesiredSize.Width, child.DesiredSize.Height));
progress += width / 2 / pathLength;
}
Point endPoint, endTangent;
pathFigureHelper.GetPointAtFractionLength(1, out endPoint, out endTangent);
totalSize.Width = Math.Max(totalSize.Width, endPoint.X);
}
void BumpUpTotalSize(Matrix matrix, Point point)
{
point = matrix.Transform(point);
totalSize.Width = Math.Max(totalSize.Width, point.X);
totalSize.Height = Math.Max(totalSize.Height, point.Y);
}
protected override Size MeasureOverride(Size availableSize)
{
foreach (UIElement child in Children)
child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
// return the size calculated during CalculateTransforms
return totalSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in Children)
child.Arrange(new Rect(new Point(0, 0), child.DesiredSize));
return finalSize;
}
}
}
You should horizontally align you TextBox to the left, right, or center. It is aligned as strech per default, thus expandig it to the available area.
Edit
Just testet it with a little class:
public class TextOnAPath : Panel
{
public TextOnAPath() {
var textBlock = new TextBlock();
textBlock.Text = "Test";
textBlock.Background = Brushes.Blue;
textBlock.VerticalAlignment = System.Windows.VerticalAlignment.Top;
textBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
this.Children.Add(textBlock);
this.Background = Brushes.Gray;
}
protected override Size MeasureOverride(Size availableSize) {
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize) {
foreach (UIElement child in this.Children)
child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
return finalSize;
}
}
Removing the alignments takes up all available space ... could it be that your CalculateTransforms method causes the effect? Especially, as the outcome is then used in the MeasureOverride method.
I figured out the problem, it turns out that the problem was not related to the control at all, but somewhere in the code the default template for a textblock got changed, and MinWidth and MinHeight got set to 40,18 for some reason... Now to find the suspect and to yell at them.
Thanks guys

Categories