WPF Forms Integration - A Problem with Grid - wpf

I am doing a feasibility study to find out whether and how we can integrate a Forms application into a WPF project.
I started with a simple example and immediately encountered a problem (here is the code):
<Window x:Class="TestFormsIntegration.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:local="clr-namespace:TestFormsIntegration"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Menue Bar" HorizontalContentAlignment="Center" Background="BlanchedAlmond" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<ScrollViewer x:Name="scrollViewer" Grid.Row="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="200" />
<RowDefinition Height="200" />
<RowDefinition Height="200" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<WindowsFormsHost Grid.Row="0" HorizontalAlignment="Center" Height="190" VerticalAlignment="Center" Width="250">
<wf:Button Text="Button 0" Height="180" Width="200" BackColor="Gray" />
</WindowsFormsHost>
<WindowsFormsHost Grid.Row="1" HorizontalAlignment="Center" Height="190" VerticalAlignment="Center" Width="250">
<wf:Button Text="Button 1" Height="180" Width="200" BackColor="Gray"/>
</WindowsFormsHost>
<WindowsFormsHost Grid.Row="2" HorizontalAlignment="Center" Height="190" VerticalAlignment="Center" Width="250">
<wf:Button Text="Button 2" Height="180" Width="200" BackColor="Gray" />
</WindowsFormsHost>
<WindowsFormsHost Grid.Row="3" HorizontalAlignment="Center" Height="190" VerticalAlignment="Center" Width="250">
<wf:Button Text="Button 3" Height="180" Width="200" BackColor="Gray" />
</WindowsFormsHost>
</Grid>
</ScrollViewer>
<Label Grid.Row="2" Content="Status Bar" HorizontalContentAlignment="Center" Background="Wheat" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</Window>
In a grid with 4 rows I place 4 forms buttons. When I run the code, the inner grid crosses the boundaries and overwrites the labels at the top and bottom (indicating Menu and Status Bar) and takes up the height of the entire window.
Does anyone know this behaviour and how to fix it?

Hint: Try a larger value for height (you have Height="450"), because the sum of the height of your elements is just higher (200 * 4) and don't forget the labels.

I have found the solution here:
WindowsFormsHost ZOrder
Here I read:
In a WPF user interface, you can change the z-order of elements to control overlapping behavior. A hosted Windows Forms control is drawn in a separate HWND, so it is always drawn on top of WPF elements.
But this article also contains an answer with a solution!
You can do a little trick. When you declare an WindowsFormsHost, it's parent is first HWND component. Usually it's root window. So, clip area for controls is whole window.
But there's a way to create "intermediate" HWND item to clip WinForms area over ScrollViewer. Just place another WindowsFormsHost with ElementHost.
Thank you for your answer, ds1709.

Related

How to have filled video at top and show label at bottom. (Fill and Anchor)

In Winforms, I use Fill and Dock to achieve this.
I have a "Page" where I would like to play a video file and also show the label at the bottom. I would like the page to be able to stretch and for the video to stretch and to have the label stay at the bottom of the page.
My attempts so far always results in the video covering the label when it is played. How can this be fixed?
(Other controls in the StackPanel have been omitted)
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.MediaElementExample" >
<DockPanel LastChildFill="True">
<MediaElement Source="media\numbers.wmv" Name="myMediaElement"
LoadedBehavior="Manual" UnloadedBehavior="Stop"
MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded"
DockPanel.Dock="Top" Margin="50" />
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal"
Height="30" DockPanel.Dock="Bottom" Margin="50">
<TextBlock Margin="5" VerticalAlignment="Center">
Video Label
</TextBlock>
</StackPanel>
</DockPanel>
</Page>
Solution (with thanks to Daniel May):
<Grid Height="Auto">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<MediaElement Source="media\numbers.wmv" Name="myMediaElement" LoadedBehavior="Manual" UnloadedBehavior="Stop"
MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded" />
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Height="30" Grid.Row="1">
<Button Content="Button" Height="23" Name="button1" Width="75" Click="button1_Click" />
</StackPanel>
</Grid>
You can achieve this using a Grid.
<Grid Height="300" Width="300">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<MediaElement Source="media\numbers.wmv"
Name="myMediaElement"
LoadedBehavior="Manual"
UnloadedBehavior="Stop"
MediaOpened="Element_MediaOpened"
MediaEnded="Element_MediaEnded" />
<TextBlock Margin="5"
Grid.Row="1"
VerticalAlignment="Center"
Text="Video Label" />
</Grid>
Using the Height attribute on the second RowDefinition, you force that row to size to it's contents. The preceding RowDefinition then fills the rest of the available space (in your case, your MediaElement).

Why the height is incorrect?

I have a simple WPF form with next XAML
<Window x:Class="ReikartzDataConverter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="650" Width="800">
<Grid Width="780" Height="650">
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="500"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Process information" Height="28" HorizontalAlignment="Left" Margin="0,20,0,0" Name="label1" VerticalAlignment="Top" Width="235" />
<DataGrid Grid.Row="1" Width="780" Height="500" Name="paysTable">
</DataGrid>
<Label Grid.Row="2" Height="28" Name="lblError" VerticalAlignment="Top" Visibility="Hidden" Foreground="OrangeRed" FontWeight="Bold" FontSize="12" />
<Button Grid.Row="3" Content="Quit" Height="23" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<Button Grid.Row="3" Content="Start" Height="23" Name="button2" VerticalAlignment="Top" Width="75" Click="button2_Click" />
</Grid>
</Window>
Why my 2 buttons from Grid.Row="3" are partially located out of the visible part of the window?
My window has the height="650" and my Grid also has the Height="650"
I have 4 Rows: 50, 50, 500, 50. So the last row must be located inside the window. Why is not so?
The Window Height of 650 also includes the 'chrome', i.e. the bar at the top of the window with the minimize, maximize buttons. It is a much better approach to create a layout which does not rely on a specific height. In your case, I would make the row that contains your grid auto-sized:
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
</Grid.RowDefinitions>
Then you can remove the height / width from your Grid, and all your other UI elements, just let the grid dictate the size of its children.
#ColinE's answer is the right approach in that you should adopt a "fluid" layout in WPF, but if you really want a fixed height for your content and you need the window to be the right size, you can use the SizeToContent property:
<Window x:Class="ReikartzDataConverter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="800"
SizeToContent="Height">
<Grid Width="780" Height="650">
...
</Grid>
</Window>
Setting SizeToContent to "Height" will make the window resize vertically so that its contents fit. Don't forget to remove the Height property from the Window declaration.

How to stretch the controls as per the page size in wpf

I have a listview of width set to Auto. When I run the windows app, it opens in normal size(not maximized). But when I maximize the window, the listview's width will be same and the space to its right is empty.
normal size
|_________________________|
Maximized
|_________________________|...........
even though the window is now in full screen occupied.
Please guide me in workin on this.
Thanks
Ramm
StackPanel, by design, does not care about visual space. It aims to take up the smallest amount of space possible. You can leave the innermost StackPanel that wraps the radio buttons in place, but your outer layout containers should be changed to Grid or, as in my example below, DockPanel:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="445" Width="515">
<DockPanel Name="spDataFlow" Margin="0,45,0,0" >
<DockPanel x:Name="stkPnlDataFlow" VerticalAlignment="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid>
<StackPanel Grid.Row="1" Background="Red" Margin="20,15,0,0" Orientation="Horizontal" VerticalAlignment="Center" >
<RadioButton Name="rdbtnUploadData" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="12" Foreground="White" Content="Upload Data" IsEnabled="True" CommandParameter="UploadAll"/>
<RadioButton Name="rdbtnDownloadData" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="12" Foreground="White" Content="Download Data" Margin="20,0" CommandParameter="DownloadAll"/>
<RadioButton Name="rdbtnUploadSelected" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="12" Foreground="White" Content="Update Data" Margin="10,0" CommandParameter="UpdateSelected"/>
</StackPanel>
</DockPanel>
</DockPanel>
</Window>
Well, I believe that by default the ListView control automatically fills all available space so it is very strange that have such an issue. Could you paste your code?

XAML Controls offsetting themselves, becoming invisible in Blend and browser

I'm having a problem, visible at runtime and in Expression Blend, where the text blocks (not text boxes, buttons, or custom controls) in my layout grid keep pushing themselves outside their cells, rendering them invisible. If I touch any of their properties in Blend (such as incrementing and then decrementing one of the margins), they become visible in Blend, but still not at runtime. Below is a screenshot showing the phenomenon in Blend. You see the design guides pointed to where the control should be, but its actual location above the top of the canvas.
Controls are offset in Blend http://tinyurl.com/y9ttscf
Update:
Below I've posted the XAML, with the VisualStateGroups removed (since they add considerable complexity to the XAML and the problem manifests itself without them). The control selected above is "loginTextBlock" below.
<navigation:Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
mc:Ignorable="d" xmlns:UserControls="clr-namespace:MyClient.UserControls" xmlns:MyClient_Controls="clr-namespace:MyClient.Controls;assembly=MyClient.Controls" xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" x:Class="MyClient.Views.Login"
d:DesignWidth="640" d:DesignHeight="480"
Title="Login"
>
<Grid x:Name="LayoutRoot">
<Grid HorizontalAlignment="Center" Margin="0,16,0,0" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="loginTextBlock" HorizontalAlignment="Center" Style="{StaticResource HeaderTextStyle}" VerticalAlignment="Center" Text="Login" Grid.ColumnSpan="2" Margin="0,8"/>
<TextBlock x:Name="usernameTextBlock" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Row="1" Text="User name:" TextWrapping="Wrap"/>
<TextBox x:Name="usernameTextBox" HorizontalAlignment="Left" Margin="8,8,0,8" VerticalAlignment="Center" Width="175" Grid.Column="1" Grid.Row="1" TextWrapping="Wrap" TabIndex="0" FontSize="16" Height="28" Padding="2"/>
<TextBlock x:Name="passwordTextBlock" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Row="2" Text="Password:" TextWrapping="Wrap"/>
<PasswordBox x:Name="passwordBox" HorizontalAlignment="Left" Margin="8,8,0,8" VerticalAlignment="Center" Width="175" Grid.Column="1" Grid.Row="2" TabIndex="1" FontSize="16" Height="28" Padding="2"/>
<Button x:Name="okButton" Height="32" HorizontalAlignment="Center" Margin="0,16,0,0" VerticalAlignment="Top" Width="96" Content="OK" Grid.Row="3" TabIndex="2" Click="okButton_Click" Grid.ColumnSpan="2"/>
<UserControls:StatusTextBlockControl x:Name="verifyingStatusTextBlockControl" Margin="8,16,8,8" VerticalAlignment="Center" Grid.Row="4" HorizontalAlignment="Center" Grid.ColumnSpan="2" Text="Verifying credentials..."/>
<MyClient_Controls:LoginAttemptsCounter x:Name="loginAttemptsCounter" HorizontalAlignment="Center" Margin="8" VerticalAlignment="Center" Grid.ColumnSpan="2" Grid.Row="5" FirstFailureMessage="Please re-enter your Windows credentials.
After 2 more failed attempts, your account will be locked." Height="30"/>
</Grid>
</Grid>
</navigation:Page>
For some reason, when my "LoginAttemptsCounter" control is in the grid (at the bottom), it was messing up the TextBlock controls. Instead, I changed my layout to wrap the grid within a StackPanel and place the LoginAttemptsCounter in the StackPanel below the grid rather than in the grid's bottom row. That has worked.
The key thing is that my custom control can't be within the same container (either the StackPanel or the Grid) as the TextBlocks.

Centering a WPF control

I have a window where I add a new UserControl to (with an image), I simply want to center the control in the middle of the screen (both vertically and horizontally). I can only get the vertical one to work. I'm gonna swap content in the DockPanel from my CodeBehind and want to show this startup screen before I start doing my slideshow UI, this means that the content is set from the CodeBehind.
My Window:
<Window x:Class="GreenWebPlayerWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="512" Width="853" WindowStyle="None" WindowState="Maximized" WindowStartupLocation="CenterScreen">
<DockPanel Width="Auto" Height="Auto" Name="TransitionContainer" Background="Black" Margin="0" LastChildFill="True"></DockPanel>
</Window>
My UserControl:
<UserControl x:Class="GreenWebPlayerWPF.FrontPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel Background="Black">
<Image Name="image1" Stretch="None" Source="/GreenWebPlayerWPF;component/gw.png" />
</DockPanel>
</UserControl>
Please note that I'm using maximized/full screen.
Use a Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Replace with your UserControl -->
<Button Content="Foo" Grid.Column="1" Grid.Row="1"/>
</Grid>
You can dock it inside your DockPanel (if you must have a DockPanel there) to stretch. And, of course, while the above is all markup, you can just as easily create such a grid from code.
I keep running into this problem when trying to center elements on the page.
The problem with the StackPanel is that HorizontalAlignment has no effect when the Orientation is Horizontal and VerticalAlignment no effect when Orientation is Vertical. So you keep banging your head trying to set values with no effect. It is not illogical that it works this way but it would be good if this was reported as an error.
The solution I found is to have two imbricated StackPanels one centered horizontally and the other vertically as shown below. Finding the size of the parent is needed to size the intermediate panel otherwise it would be flat and its content hidden - an absolute value would work as well. Although not a panacea itis a bit less verbose than using a grid.
<StackPanel Background="Bisque" Orientation="Vertical" Width="300" Height="300" >
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal"
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel}, Path=ActualHeight}">
<StackPanel VerticalAlignment="Center" Width="200" Height="60" Background="Blue">
</StackPanel>
</StackPanel>
</StackPanel>
It's quite an old one, but centring a control is now as simple as:
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Height="350" Width="600">
<TextBox />
</StackPanel>
</Grid>

Resources