Adding children to stackpanel programmatically not working - c#

Can anybody spot the mistake that I am doing?
Here is the code:
StackPanel stackPanel = new StackPanel();
stackPanel.Orientation = Orientation.Vertical;
for (int index = _elements.Count - 1; index >= 0; index--)
{
FrameworkElement element = _elements[index];
WriteableBitmap tempBitmap = new WriteableBitmap(element, null);
Image image = new Image();
image.Source = tempBitmap;
stackPanel.Children.Add(image);
}
stackPanel.UpdateLayout();
_bitmap = new WriteableBitmap(stackPanel, null);
_bitmap.Invalidate();
As you can see I am creating a temporary Image and then adding it to stackpanel and then creating a final WriteableBitmap. Myy 1st children of stackpanel is of height 154 and 2nd one is of 389. After this line:
_bitmap.Invalidate();
when I see PixelHeight it is just 389. Where did my 1st child go?

While both of the answers given by sLedgem and bathineni are correct, it doesn't seem to fit my situation. Also why would I want to add them to the layout? If it's convenient for you, you can but in my case I want them to be in 100% memory because my helper class used basically for printing didn't have any reference to any of the UIElements present on the screen. In that case obviously I wouldn't want to pass my LayoutRoot or some other Panel to my helper class just to make this hack!
To ask the Silverlight runtime to render the elements in memory. You need to call Measure and Arrange:
So typically all I was missing was:
stackPanel.Measure(new Size(width, height));
stackPanel.Arrange(new Rect(0, 0, width, height));
before:
_bitmap = new WriteableBitmap(stackPanel, null);
And I can get rid of this line:
stackPanel.UpdateLayout();
Hope this helps someone who comes on this thread and has same problem like me where it is not feasible to find LayoutRoot in your helper class.

stackPanel or any other panels will not render until they added to visulatree (any visual element)..
means.. if you have added 100 items to stackpanel those 100 elements will not be generated until stackpanel has its visual appearence on screen..
if you dont want to show stackpanel on screen then add stackpanel and remove it immediatly. it will make stackpanel render it child elements...
OR
Create a grid or stackpanel in XAML which has 1 pixel height and width and add your stackpanel to that grid or stackpanel ... so it will not be visiable on screen and it will be rendered on background....

bathineni is right, you need to render the stackpanel before taking a snapshot. I would suggest letting it render a frame and then grab the bitmap after it has rendered
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
_bitmap = new WriteableBitmap(stackPanel, null);
_bitmap.Invalidate();
}

Related

Textblock margin causes out of bounds text

I'm currently trying to create a visual component to have scrolling text (left to right and right to left) - pretty much an html marquee.
I have a grid divided in several columns & rows, and I want to place my component inside one of the grid slots.
The grid (named UIGrid) is generated like this :
for (int i = 0; i < xDivisions; i++)
{
ColumnDefinition newColumn = new ColumnDefinition();
UIGrid.ColumnDefinitions.Add(newColumn);
}
for (int i = 0; i < yDivisions; i++)
{
RowDefinition newRow = new RowDefinition();
UIGrid.RowDefinitions.Add(newRow);
}
The component I'm adding is just a border with a textblock as a child. I place the border inside the Grid like this :
border = new Border();
Grid.SetColumn(border, xPosition);
Grid.SetRow(border, yPosition);
textBlock = new TextBlock();
border.Child = textBlock;
textBlock.Text = "Scrolling text from left to right";
UIGrid.Children.Add(border);
I'm using a timer to increment the textblock margin, here's the timer callback simplified body :
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double textWidth = textBlock.DesiredSize.Width;
double visibleWidth = componentBase.ActualWidth;
double targetMargin = textWidth < visibleWidth ? visibleWidth : textWidth;
if (margin.Left == targetMargin)
{
margin.Left = -textWidth;
} else
{
margin.Left++;
}
When the text slides from left to right, it behaves nicely :
https://s10.postimg.org/p0nt7vl09/text_good.png
Text "leaving" the grid slot is hidden.
However, when I set the textblock's margin as negative so it may come back inside the viewable area from the left, the text is visible even though it's outside its allocated slot :
https://s10.postimg.org/pownqtjq1/text_bad.png
I've tried using padding instead, but I can't set a negative padding. I've tried a few other things, but I feel like I've encountered a roadblock.
What could I do to get a nicely scrolling text ?
If you want nicely scrolling text ListView might be a better option. It is dynamic and you can bind it to your object. It would take a lot of this guess work out.
Ed Plunkett led me in the right direction with the Clip property. The idea is to do this :
border.Clip = new RectangleGeometry
{
Rect = new Rect(0, 0, border.ActualWidth, border.ActualHeight)
};
Of course, that doesn't work if the border hasn't been rendered yet (and of course it isn't when my code is running). You can force the measurement to take place using 'Measure' as I did to measure the text length in pixels, but it behaved strangely on my border. I wouldn't get the correct size at all.
In the end, I simply subscribed to the border's SizeChanged event :
border.SizeChanged += OnSizeComputed;
When that event is fired, I create the RectangleGeometry using ActualWidth & ActualHeight.

Maximum Hight of a panel in Split Container

How can I set the minimum & maximum height of a panel in a horizontal split container in my C# form?
I realize this question is old, but I couldn't find a suitable answer anywhere I looked. Some people suggested setting Panel1 as a fixed panel, which was not what I wanted. I solved this issue by using by using the splitcontainers sizeChanged and SplitterMoved events:
private const int Panel1MaxWidth = 1075;
private void splitContainer1_SizeChanged(object sender, EventArgs e)
{
if(splitContainer1.Panel1.Width > Panel1MaxWidth)
{
splitContainer1.SplitterDistance = Panel1MaxWidth;
}
}
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
if (splitContainer1.Panel1.Width > Panel1MaxWidth)
{
splitContainer1.SplitterDistance = Panel1MaxWidth;
}
}
Just set the Pane1MaxWidth constant to whatever you want your maximum size of Panel1 to expand out to.
SplitContainer has 2 fields: Panel1MinSize and Panel2MinSize. To set the maximum size for panel1 just set the appropriate min size for panel2.
Irrespective of where the Panel is, you could normally specify the maximum height and width by doing:
panel1.MaximumSize = new Size(300, 300); //max 300 x 300
If you use SplitContainer and your Panel is inside the SplitContainer, and you want to change it while it is in the SplitContainer, however, you might need to identify if the Panel is in the Panel1 or Panel2 of the SplitContainer before you specify the max height and width as above. Something like this:
//assuming the name "panel1" in the Panel1 of the SplitContainer
Panel panel = splitContainer1.Panel1.Controls["panel1"];
panel.MaximumSize = new Size(300, 300); //max 300 x 300
However, if what you want is to change the splitContainer size itself, you could apply the MaximumSize for the splitContainer as well:
splitContainer1.MaximumSize = new Size(300, 300);
Or, if you want to change the splitContainer.Panel1 or splitContainer.Panel2, you could also try to play with SplitContainer.SplitterDistance property.

Create Objects in panel and arrange objects the new in first not in end

I want create manually PictureBox and Label on horizontal Panel, The count of PictureBox and Label on panel Unknown maybe 200 or more or less, I use the below code to do that but i face two troubles first one:
I want add the new object created in the first not in the end for example if i created items "A B C D E" want it add on Panel "E D C B A" want always the new come to first.
Note: Panel width "230" Height "710"
Second trouble:
Currently when i use Panel scroll bar to go down than add new objects find happen big free space between the last object created and the new and if i used scroll again to go down and created new object on panel happen more big free space.
int Right = 50, Top = 0;
// Create Image + Text
PictureBox pbox = new PictureBox();
pbox.Size = new Size(140, 80);
pbox.Location = new Point(Right, Top);
pbox.Image = Image.FromFile("");
Panel1.Controls.Add(pbox);
// Create label
Label lblPlateNOBAR = new System.Windows.Forms.Label();
lblPlateNOBAR.Text = lblPlateNO.Text;
lblPlateNOBAR.Location = new Point(Right + 20, Top + 80);
Panel1.Controls.Add(lblPlateNOBAR);
Top = Top + 150;
In order to make the objects insert into the panel, you'd need to move the controls which already exist in the panel:
int right = 50;
// Create Image + Text
PictureBox pbox = new PictureBox();
pbox.Size = new Size(140, 80);
pbox.Location = new Point(right, 0);
pbox.Image = Image.FromFile("");
// Create label
Label lblPlateNOBAR = new System.Windows.Forms.Label();
lblPlateNOBAR.Text = lblPlateNO.Text;
lblPlateNOBAR.Location = new Point(right + 20, 80);
foreach(var control in Panel1.Controls)
{
control.Top = control.Top + 150;
}
Panel1.Controls.Add(pbox);
Panel1.Controls.Add(lblPlateNOBAR);
I know it might seem that I'm not answering your question, but you can take little time to know my suggestion too. I don't know what you wanna achieve, but when you want to make such a sophisticated program, it's a better practice that you do all the work needed yourself, not relying on Windows Forms Controls. Trying to add, remove, change location of lots of controls will reduce the application performance very much. I suggest that you draw, for example your pictures, yourself, using Graphics and Image objects and Paint event. Also handle things like clicking and selecting pictures by MouseEvents. It might seem a little hard at first, but after you've done this you have far better performance and flexibility. This becomes more important considering you mentioned you wanna place 200 pictures in the panel. This also prevents trouble of flickering that appears when you change position of many controls. It's good to mention that to do scrolling in this case, you can place a Panel inside of a Parent Panel and use AutoScroll feature only for the parent panel to handle scrolling. This way you don't have to care about the scrolling anymore.

WPF: Creating image of dynamically created usercontrol

I want to create the image of a dynamically created usercontrol and show it in a window.
I am creating the usercontrol using the Below code .
MyViews.MyViewsUserControl myViewsCanvas = new MyViews.MyViewsUserControl(AllFoundationMyViewsViewModel,item.Id);
//myViewsCanvas.Height = 5;
//myViewsCanvas.Width = 5;
Size size = new Size(50, 50);
myViewsCanvas.Measure(size);
double width = myViewsCanvas.DesiredSize.Width;
double height = myViewsCanvas.DesiredSize.Height;
myViewsCanvas.Arrange(new Rect(new Point(), size));
Then i am creating the image of the myViewsCanvas and adding it to a view box of another usercontrol called _DashBoardUserControl using the below code.
_DashBoardUserControl.Viewbox2.Child = CreateImage(myViewsCanvas);
Then i am adding the _DashBoardUserControl to a window.
UserControls.Controls.PopupWindow popup = new UserControls.Controls.PopupWindow();
popup.PopupContent = _DashBoardUserControl;
popup.ShowDialog();
The problem is, I can only see a portion of the Image. I guess that is because of the measure() and arrange() methods. Can anybody tell me about these methods or what size should i pass to these methods. Do i need to scale down the image? If yes how do i do that?
The easiest way I know of is this:
Viewbox v = new Viewbox();
v.Child = uielem;
uielem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
v.Measure(uielem.DesiredSize);
v.Arrange(new Rect(new Point(), uielem.DesiredSize));
v.UpdateLayout();
r.Render(v);
where uielem is the element you want to render and r is the RenderTargetBitmap. (v.UpdateLayout might not be needed there, but I'm not sure anymore).

how to get a labels width before it is rendered in WPF

Tricky one to explain this. I have a custom built properties grid. The left hand column has the property labels. Sometimes depending on the property, I want a little elipsis button to show the user that they can launch a dialog. I want the buttons to be inline vertically to make the UI look neat. The labels vary in width depending on the name of the property "onEnterPressed" or "upLink" for example.
If I add the elipses button alone and use a margin like so ...
elipsisButton.Margin = new Thickness(135, 0, 0, 0);
135 from the left is exactly where I want to place the button.
I was hoping to be able to do something like
Label newLabel = new System.Windows.Controls.Label();
newLabel.Content = anInfo;
aPanel.Children.Add(newLabel);
elipsisButton.Margin = new Thickness(135 - newLabel.Width, 0, 0, 0);
It would appear however, that the label doesn't get a width until it's been rendered on screen so I can't find out what size margin to add to my elipsis button. Any ideas?
You can call the Measure() method in order to ask the control the size it needs to be displayed:
var l = new Label() { Content = "Hello" };
l.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Size s = l.DesiredSize;
And then use the value of the DesiredSize property.

Categories