WPF control alignment is wrong when dynamically added - wpf

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.

Related

WPF adding stackpanel with border to grid gives error

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>

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.

Zooming in specific part of screen in WPF

I am implementing one small WPF application which has multiple rows and multiple columns. 0th row and 0th column contains a MediaElement and 1st row and 0th column contains a full screen button. When user clicks on full screen button I want to switch to a gird which has only two rows and one column. 0th row and 0th column will occupy most of the screen space having inside MediaElement and 1st row and 0th column will show a minimize button which will bring original UI back.
In traditional windows we were used to toggle visibility of a full screen panel hosting WindowsMedia player to achieve this behavior. How can I achieve this in WPF?
Adding my XAML code.
<Window x:Class="LearnEnglish.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<MediaElement LoadedBehavior="Manual"
Name="me"
Source="C:\Users\Pritam\Documents\Freecorder\Screen\Northern Ireland Scene 1 LearnEnglish British Council.wmv"
Volume="{Binding ElementName=txtVolume,Path=Text}"
Grid.ColumnSpan="2">
</MediaElement>
<Button Click="Button_Click"
Grid.Row="1"
Margin="4">Play</Button>
<Button Click="Button_Click"
Grid.Row="1"
Grid.Column="1"
Margin="4">Full Screen</Button>
<Button Click="Button_Click"
Grid.Row="1"
Grid.Column="1"
Margin="4"
Visibility="Hidden">Restore</Button>
</Grid>
</Window>
When user clicks on 'Full Screen' button I want my 'MediaElement' to occupy most of the scree space ( by hiding all other controls ) and leaving 'Restore' button in the bottom-right hand side of screen.
Regards,
Hemant
You can do that completely in XAML by using a ToggleButton and a trigger on its IsChecked property which sets the width / height of all columns / rows you don't want to see to 0. Use x:Name to name the elements you want to change, that will make it easier to write the Trigger.
In order to be able to access all controls, you should define the trigger on a parent control which contains all the other controls, e.g. in a UserControl, a panel, a DataTemplate or ControlTemplate. In order to access the properties on different controls, use their names for the TargetName property on the setters. There is also a corresponding SourceName property on Trigger itself, so you don't have to define the Trigger on the ToggleButton itself.
Most simply, you would use code to change the size of your columns. Something like this, wired to the click events of your buttons, would work:
First, name your Grid:
<Grid Name="MyGrid">
Then, wire your buttons:
<Button Click="Button_Click" Grid.Row="1" Margin="4">Play</Button>
<Button Name="FullScreenButton" Click="FullScreenButton_Click" Grid.Row="1" Grid.Column="1" Margin="4">Full Screen</Button>
<Button Name="RestoreButton" Click="RestoreButton_Click" Grid.Row="1" Grid.Column="1" Margin="4" Visibility="Hidden">Restore</Button>
And, use handlers to change the grid:
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
meVid.Play()
End Sub
Private Sub FullScreenButton_Click(sender As Object, e As RoutedEventArgs)
MyGrid.ColumnDefinitions.Item(2).Width = New GridLength(0)
MyGrid.ColumnDefinitions.Item(3).Width = New GridLength(0)
FullScreenButton.Visibility = Windows.Visibility.Hidden
RestoreButton.Visibility = Windows.Visibility.Visible
End Sub
Private Sub RestoreButton_Click(sender As Object, e As RoutedEventArgs)
MyGrid.ColumnDefinitions.Item(2).Width = New GridLength(2, GridUnitType.Star)
MyGrid.ColumnDefinitions.Item(3).Width = New GridLength(1, GridUnitType.Star)
FullScreenButton.Visibility = Windows.Visibility.Visible
RestoreButton.Visibility = Windows.Visibility.Hidden
End Sub
That code will effectively toggle the widths of the columns you want to hide, to zero, and restore them to what you had defined in the XAML. Because you want to dynamically vary your Grid element sizes, you will need to do this in code someplace.
You can, of course, define this behavior in style triggers, or wire your size elements to ViewModel bindings, but those are topics all by themselves which might not pertain to your architecture. Since you defined XAML with code-behind behavior on the buttons, I put the code in the code-behind; the point is, you vary the size of your Grid ColumnDefinition and RowDefinition elements to hide them.
This avoids problems with templates or triggering but also introduces "separation of concerns" issues which may make your stuff harder to maintain, if it's part of a complex project.

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) :

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