WPF adding stackpanel with border to grid gives error - wpf

I'm working on a nested grid/stackpanel/grid window in WPF. Its a Calander, with a maingrid cell being a day. In this day there's a textbox and a stackpanel. The stackpanel contains a grid. EVerything is done in c#, build at run time, because the layout changes with the current month/year. No major problems sofar except when I want to add a border to a stackpanel. It gives the following error:
An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: 'The invocation of the constructor on type 'ADBF.ToezAcad.Admin.OpleidingKalender.MainWindow' that matches the specified binding constraints threw an exception.' Line number '3' and line position '9'.
Nothing special at this position.
Funny thing is it only gives this error, the moment I add the stackpanel ( with border ) to the containing grid.
short version of the code:
Border stackpanelborder = new Border();
this.Content = stackpanelborder;
StackPanel stackpanel = new StackPanel();
stackpanelborder.Child = stackpanel;
Grid.SetColumn(stackpanel, m);
Grid.SetRow(stackpanel, d + 1);
mainGrid.Children.Add(stackpanel); // if I uncomment this line, it throws the error.
Any help would be highly appreciated,
Arnold

#ADBF you are adding the StackPanel to two different child collections. Each UIElement should only have one parent in the visual tree, though a UIElement may have many children, depending on it's type.
I think you wanted to add stackpanelborder to the children of the mainGrid instead.
Edit:
Also you should be referencing stackpanelborder in the SetColumn/SetRow Methods not stackpanel.
The reason being since stackpanel is a child of stackpanelborder it will render inside the that UIElement. However stackpanelborder needs to be told where to insert in the grid, assuming you intend to add additional columns/rows later.
Basically your XAML document if you had one should look like this:
<Grid Name="mainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition .../>
<ColumnDefinition .../>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition .../>
<RowDefinition .../>
</Grid.RowDefinitions>
<Border Name="stackpanelborder" Grid.Row="0" Grid.Column="0" ...>
<StackPanel Name="stackpanel" .../>
</Border>
</Grid>

Related

WPF control alignment is wrong when dynamically added

Hi I thought I could solve this easily but it is driving me crazy.
I am using a UserControl to house a video player control based on VLC, along with play and stop buttons etc. I then place the UserControl on my main form. if the UserControl is declared in XAML it behaves normally.
I decided to rewrite the code to instantiate my UserControl dynamically, in case I need to destroy it and create another on the fly. But when I do the video moves to the top of its container instead of the middle.
The UserControl relevant section is here:
<Grid x:Name="LayoutParent" >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
</Grid.RowDefinitions>
<!-- I comment this if adding player dynamically -->
<!--<wpf:VlcPlayer Grid.Row="0" x:Name="Player" />-->
<!-- I un-comment this if adding player dynamically -->
<Grid x:Name="VideoPlayerPanel" Grid.Row="0" Margin="0" />
<StackPanel Grid.Row="1" Opacity="0.8">
...(buttons etc)
</StackPanel>
<ProgressBar ...(progressBar etc) />
</Grid>
My codebehind looks like this:
Dim Player As VlcPlayer = New VlcPlayer ' uncomment If adding the player dynamically
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Player.SetValue(Canvas.ZIndexProperty, -1)
VideoPlayerPanel.Children.Add(Player)
VolumeSlider.Value = 50
End Sub
I have tried VerticalAlignment="Center" and VerticalAlignment="Stretch" in XAML on the VideoPlayerPanel, with Center the video disappears entirely, with Stretch it still aligns to the top.
Any thoughts as to what I might do to align this centrally would be much appreciated!
When adding Player dynamiccaly you have different result, because you wrap Play in additional Grid. Try to add Player directly to first row of LayoutParent:
Player.SetValue(Grid.Row, 0)
LayoutParent.Children.Add(Player)
Thanks to all that replied.
I did some more research, I substituted in a Rectangle for the player control and it aligned perfectly. That led me to discover that the third party control was itself at fault. I had to get the source and change the VerticalAlignment directly.
Sorry for the runaround.
Remove Height="*" from first Row . * is used to occupy remaining space, so it is good to use it for the last Row.
Use fixed width and or Auto.

WPF resize issues when using RowDefinition Height="*" in Grid

UPDATE I added the UserControl definition to the XAML.
I've noticed some strange behavior in a couple of WPF applications I've created lately, that seem to be related to using "*" for a Grid row height or column width.
The behavior I'm referring to is when trying to expand items in a control (like a treeview), the entire window will resize its height instead of creating a scrollbar. So if I just run the application, and start expanding nodes, when the items extend beyond the visible portion of the UI then the window will resize.
BUT if I resize the window first, or even just click on the bottom or right border (without actually resizing), then it will behave normally and leave the window height alone, with a scrollbar on the treeview.
<UserControl x:Class="ProjectZ.Views.GenericDefinitionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cal="http://www.caliburnproject.org"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
xmlns:local="clr-namespace:ProjectZ"
xmlns:behaviors="clr-namespace:ProjectZ.Behaviors"
mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="mnuMainMenu" IsMainMenu="True">
... menu stuff
</Menu>
<xcad:DockingManager ... />
</Grid>
</UserControl>
I've played around with it and it seems to always start happening after I've used the "*" value for a grid row height or a column width. If I take that out, it seems to behave normally.
Has anyone else run into this? Any ideas what I'm doing wrong or could do differently to fix this? The only other information I think might be relevant is that this is using Caliburn.Micro. The only settings passed to the window when launching are: MinHeight, MinWidth, Title, and Icon.
The problem is that you never specify a height in the visual tree above the element.
Your UserControl or the Window that's created needs a specific height if you want to use star sizing effectively. Otherwise, a height is "chosen" at runtime, but the Window is effectively set to size by content. As you change items, the Window resizes.
As soon as you touch a border, the Height is being set (whether or not you resize), in which case it then dictates the layout correctly.
If you specify a default height for the Window as its created, the issue will likely resolve itself.
The resizing must be caused by the code:
<UserControl
//...stuff>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
</UserControl>
Where the < * > can be understood as "Take the rest of all available space, after placing all the other elements first". So when you add another element in the control the view will set itself and the the
<RowDefinition Height="*" />
Will resize itself acordingly to take the remainder of space available. To remove this you can just set a specific height for the row or another idea will be to add another Grid inside the row definition so that it won't resize since it will always be inside the row, but still have all of it's functionality.

Autoscaling to parent object within a ScrollViewer?

I am having an autoscaling issue with my pages. The pages are displayed within a ScrollViewer, which usually contains a column on the ~33% right and a grid on the ~66% left.
I want to only autoscale the grid's Height (ie. independantly from the column at the right), so that it stretches according the size of the window, ergo, the object that contains the ScrollViewer and not the ScrollViewer itself.
So far, I have not managed to put this idea into practice... How could I possibly explain to my xaml code that I want my grid to be stretched in comparison to the ScrollViewer's "parent" rather than the ScrollViwer itself?
Here is a code example of what I'm trying to accomplish here, if it make my case any simpler:
<Grid x:Name="Parent" MinHeight="400" MaxHeight="700">
<ScrollViewer x:Name="Annoying ScrollViewer">
<Grid x:Name="Page Content" ScrollViewer.VerticalScrollBarVisibility="Auto">
<!-- IN ANOTHER USERCONTROL FAR, FAR AWAY... -->
<Grid x:Name="Inner Page Content">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="330" />
</Grid.ColumnDefinitions>
<Grid x:Name="Right column" Grid.Column="1"></Grid>
<Grid x:Name="Left Grid" Grid.Column="0" VerticalAlignment="StretchToParentUserControl? Please?"></Grid>
</Grid>
<!-- BACK TO THE PARENT USERCONTROL -->
</Grid>
</ScrollViewer>
</Grid>
EDIT: I solved my problem by using the Window size in code-behind directly through App.Current.Host.Content.ActualHeight and substracting the height of the parent files's contents (it is static, so I can just type the numbers). Still, I won't put this solution as an answer since it does not actually answer the original question.
And here is a little drawing of what my (reached) goal was as requested (uh... just switch the words "left" and "right" in the picture) :

How to Hittest Grid ColumnDefinition?

I am making kind of WPF Designer. I want to find out ColumnDefinition i have clicked on to delete it from grid control. I will take care of those children who "are in that ColumnDefinition".
Can i get it from sender argument of click event handler?
Now im checking if e.GetPosition is in range of ColumnDefinition.ActualWidth but i wonder if there is more beautiful solution.
From within your click event handler:
int columnIndex = Grid.GetColumn((UIElement)sender);
where sender if a direct grid's child.
Why do you need to capture a click on ColumnDefinition anyway? Is virtual, it does not have any actual body, it is only a hint for Grid on how you want to layout its content.
So you have to set handlers on content objects, not on ColumnDefinition.
If you really need to capture a click on the whole surface of a grid cell, you may try to place a white (or other color the same as background) Reactangle inside it and capture a click on it.
Some clarification on how WPF Grid works.
When you add some controls to the Grid, they all become its children.
<Grid>
<Button/>
<TextBox/>
<Label/>
</Grid>
And they all will be displayed not regarding how you have configured Column or RowDefinitions.
Column and RowDefinitions only tell Grid how you want to aling all the existing elements inside it, but they are not containers, they don't hold elements inside.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button/><!-- this is identical to Grid.Column="0"-->
<TextBox Grid.Column="1"/>
<Label Grid.Column="2"/>
</Grid>
In this example we have created three ColumnDefinitions, even from the grid XAML you can see, that controls are not inside definitions. They are used just like ruler guides to align content.
Then you set attached properties on the elements to tell the grid where you want to put your elements.
When grid begins layout, it will see, that there are three elements, and three ColumnDefinitions, and will try to positions elements as ColumnDefinitions says.
But if you remove or change ColumnDefinitions in the runtime, grid will just realign controls in a new way.
If you want to hide some elements, you have to hide them, not ColumnDefinition.

Detect which element scrollbars belong to

I have a relatively complex layout. It consists of:
A grid with one column and three rows.
In the first row (the on giving me trouble) I have a developer express componenet - another GridControl.
My problem is, that though the height of this first row is Auto, the vertical scrollbar displays even though there's space enough for content.
I've tried setting the ScrollViewer.VerticalScrollBarVisibility="Hidden" on the row's rowdefinition, but this doesn't help.
Likewise, I've set the inner GridControl to not use scrollbars (using some Developer Express magic - not just ScrollViewer as this doesn't work)
Yet, no matter what I do, that damn scrollbar appears... Is there any way to figure out which control renders it, so I can disable the damn thing? It's not just a question of it being ugly - scrolling it actually messes with the layout!
Thanks in advance!
The relevant code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" ScrollViewer.VerticalScrollBarVisibility="Hidden" />
<RowDefinition Height="*" MaxHeight="240" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<dxg:GridControl Name="StudySizeGrid" Grid.Column="0" Grid.Row="0" >
<dxg:GridControl.Resources>
<ControlTemplate x:Key="{dxgt:TableViewThemeKey ResourceKey=ControlTemplate}">
<ScrollViewer x:Name="scr"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled"
Focusable="False"
dxg:GridControl.CurrentView="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Template="{DynamicResource {dxgt:TableViewThemeKey ResourceKey=ScrollViewerTemplate}}">
<ScrollViewer.CanContentScroll>False</ScrollViewer.CanContentScroll>
</ScrollViewer>
</ControlTemplate>
</dxg:GridControl.Resources>
...
</dxg:GridControl>
EDIT FOR CLARIFICATION: This is WPF issue :-)
You could try checking out the VisualTree, i think Snoop might be helpful for that, it probably has some other useful features too. Getting the VisualTree is a trivial matter though, you can write a single recursive method using the VisualTreeHelper, so you might not need the big guns.
e.g.
public static TreeViewItem GetVisualTree(this DependencyObject dpo)
{
TreeViewItem item = new TreeViewItem();
item.Header = dpo.GetType().ToString().Split('.').Last();
if (dpo is FrameworkElement && (dpo as FrameworkElement).Name != string.Empty) item.Header += " (" + (dpo as FrameworkElement).Name + ")";
int cCount = VisualTreeHelper.GetChildrenCount(dpo);
for (int i = 0; i < cCount; i++)
{
item.Items.Add(VisualTreeHelper.GetChild(dpo, i).GetVisualTree());
}
return item;
}
Wrote that quite some time ago, it's very sketchy (wouldn't recommend making it an extension method), gets the whole tree at one, could be modified to only fetch children on expansion of the node.
You could use something like Google Chrome's tools.
I would, in Chrome, right click around the area that has the scroll bars and select "Inspect Element". Chrome will highlight with a border what element you are looking at. You can then navigate the html within Google Chrome's inspector until it is highlighting the element with the scrollbar.
You can then find the reason from there.

Resources