Viewbox makes WindowsFormsHost disappear in WPF - c#

I have a WPF project where I maintain video ratio, by placing video control inside a ViewBox. The video control is a WinForms object wrapped inside a WindowsFormsHost. I also added a gridSplitter to resize the ViewBox. It all worked great, until I noticed a strange bug. When I would use the gridSplitter control to minimize the ViewBox close to zero, the video ratio would get screwed up after expanding it again. And when I used gridSplitter to contract the ViewBox all the way down to 0, the WindowsFormHost would completely disappear after expanding (it's still there, but it's ActualWidth and ActualHeight are now 0).
Removing the ViewBox control, and just using WindowsFormsHost works fine, but I need the ViewBox to control the aspect ratio. If I can't find a solution, I will have to control the ratio myself as a workaround.
I figured I must be doing something wrong, so I wrote a quick test program that reproduces the problem. I will post a link to the complete program below (it's in C#, VS2008).
In the test program I create a grid with two columns with a gridsplitter that lets you resize them. I then place a ViewBox in the left control and place a WindowsFormsHost inside it. I then host a red _winFormsButton inside the WindowsFormsHost.
The button scales fine, until you completely contract the left column and then expand it again. The button is gone. How do I prevent that from happening, so the button continues to scale after contracting and expanding left column? Thanks.
XAML:
<Window x:Class="DisappearingHost.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" Name="gridSplitter1" Width="10" VerticalContentAlignment="Center" />
<Viewbox>
<WindowsFormsHost Name="windowsFormsHost1" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Viewbox>
</Grid>
</Window>
Code Behind:
public partial class Window1 : Window
{
System.Windows.Forms.Button _winFormsButton = new System.Windows.Forms.Button();
public Window1()
{
InitializeComponent();
_winFormsButton.Text = "I disappear!";
_winFormsButton.Size = new System.Drawing.Size(50, 50);
_winFormsButton.BackColor = System.Drawing.Color.Red;
windowsFormsHost1.Child = _winFormsButton;
}
}
Sample code output:
Link to test project code (VS2008):
code

Place your grid splitter in Column zero, so you won't have WPF layout fight. Here is the modified code (notice Grid Splitter column, horiz alignment, and view box margin):
<GridSplitter Grid.Column="0" HorizontalAlignment="Right" Name="gridSplitter1" Width="10" VerticalContentAlignment="Center" />
<Viewbox Margin="10">
<WindowsFormsHost Name="windowsFormsHost1" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Viewbox>
You can make the ViewBox margin just on the right to make it look better

I ended up getting rid of the Viewbox, and controlling WidowsFormHost dimensions myself.

Related

How to efficiently scale all elements in a WinUI 3 desktop app when Window size changes?

I'd like to have everything in my WinUI 3 (v1.2) desktop app UI (MainWindow) scale up in size as the MainWindow changes in size. I'd also like to maintain the relative positioning of everything in the MainWindow. The purpose is to allow elderly users a quick and easy way to "enlarge" content by simply expanding the size of the window.
For example, take a vanilla Template Studio WinUI 3 desktop app with menu bar style navigation and a single page (call it MainPage). Replace MainPage.xaml with
<Page
x:Class="TestScale.Views.MainPage"
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"
mc:Ignorable="d">
<Grid Grid.Row="0" Grid.RowSpan="4">
<Border x:Name="Border" Width="300" Height="200" BorderThickness="2" BorderBrush="Red">
<TextBlock Text="The quick brown fox." HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
</Page>
which just draws a red rectangle with a TextBlock in the middle of the page (ignoring space used by ShellPage.xaml).
I'd like a straightforward way to scale everything, if possible, so that the rectangle and text increase in size (x and y) in the same proportions (dx and dy) that MainPage does when MainWindow changes, but also maintain their relative positions in the xy plane. I've tried using Scale and various Translations, but it always seems certain elements respond differently or only one dimension responds while the other stays fixed.
For example, adding
Scale="{x:Bind ((app:App)app:App.Current).ScaleToInitialSize, Mode=OneWay}"
to the definition of ShellPage.xaml almost does the trick except the position of the rectangle changes because the actual size of MainPage changes to reflect pixels used but the rectangle size reflects "scaled" sizes.
Is there a good discussion of scaling in WinUI 3 or UWP I've missed? I currently achieve this by binding everything to a Ratio property (Vector2 or Size) in my App class that changes whenever MainWindow.SizeChanged fires. Unfortunately, this means I also have to bind things like XAML Thickness, FontSize, and even CornerRadius properties to maintain the look and layout of the UI.
I expect this is a noob question but I can't seem to find any discussion anywhere. Any help gratefully appreciated.
You can use the ViewBox. The ViewBox scales its content automatically.
<Viewbox>
<Grid
Grid.Row="0"
Grid.RowSpan="4">
<Border
x:Name="Border"
Width="300"
Height="200"
BorderBrush="Red"
BorderThickness="2">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="The quick brown fox." />
</Border>
</Grid>
</Viewbox>

How to make use of the ViewBox control to make the application re-sizable?

ViewBox seems to be the go to tool if you want your application to be re-sizable, however, I still cannot get my head around on how to properly use it.
Here's my current code (this approach was recommended by a colleague)
<Window x:Class="WpfApp3.MainWindow"
WindowState="Maximized"
Width="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenWidthKey}}"
Height="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenHeightKey}}">
<Viewbox Stretch="UniformToFill">
<Grid Background="Peru"
Height="1080"
Width="15000">
<Grid Background="Bisque"
HorizontalAlignment="Left"
Width="{DynamicResource {x:Static SystemParameters.MaximizedPrimaryScreenWidthKey}}" >
</Grid>
</Grid>
</Viewbox>
</Window>
The approach is the following:
Wrap everything in a ViewBox with Stretch property set to UniformToFill, such that the aspect ratio of inner-elements in preserved
For the outer most grid, set the Width to some arbitrary huge number and Height to some arbitrary small number
We now have a huge rectangle going off screen to the right, while the height is the height of the window
Create a second grid and horizontally align it the the left
Put all other elements in this grid
Problems
The width of the second grid is set to the width of the primary screen, but because of the ViewBox and its stretch property the width if cut off, so I cannot really use it, as all the elements will be slightly-aligned to the left of the screen
Current layout
Questions
Is this the recommended way of creating dynamic / re-sizable applications in WPF?
Is this the correct way of using the ViewBox control?
How can I fix my problem?
Are there other solutions to using the ViewBox control?
I had this issue when I was trying to make a telerik grid re-sizable. Using a viewbox is the recommended way of creating re-sizable applications in WPF. I've found that using a dock panel is better than margins because it holds the control in place, while the viewbox controls the sizing. You can also add a grid with rows / columns if you need multiple controls. I'm very new to development, so this may not be best practice, however it works for our applications.
<Viewbox Stretch="Fill"
Grid.Row="1"
Grid.Column="1">
<DockPanel Height="300">
<Grid>
*User controls*
</Grid>
</DockPanel>
</Viewbox>

In Windows 8.1 XAML/C# app, how can I style the buttons precisely?

I'm new to Windows 8.1 development, so forgive me if the answer to this is obvious, but I've been beating on this for awhile, and can't get it to work. Here's the situation:
I made a UserControl in order to style a button as well as adding additional functionality to it. Here's the code for that:
<UserControl
x:Class="MyCalculator.CalculatorButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyCalculator"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d"
>
<!-- THIS GRID TAKES UP THE WHOLE WIDTH AND HEIGHT -->
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- THIS BUTTON TAKES UP THE WHOLE SIZE OF THE GRID -->
<Button
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderBrush="Blue"
BorderThickness="1"
Background="AliceBlue"
Padding="0"
Content="How can I make this bigger?"
Foreground="Blue"
Grid.Row="0"
Grid.Column="0"
/>
</Grid>
</UserControl>
Here's what it looks like in the designer:
You'll notice that the Button appears to be centered both horizontally and vertically, and is rather small. However, when I click into the Button in the code, the entire size of the Grid (which takes up the whole 400 x 300 area in the designer) is filled by the Button.
So the Button is filling the Grid, which is filling the area. But the Border and Background of the Button is super-small, in the middle of the Button. Worse yet, it doesn't even stretch to contain the text. This is obviously not the desired look and feel.
What am I trying to get it to do? I want the AliceBlue button background to fill the entire Grid, which fills the entire 400 x 300 area. I'd like the 'How can I make this bigger?' blue Content string centered vertically and horizontally in the big swath of AliceBlue-ness that will result.
I've tried innumerable combinations of specifications of Margin, Padding, Height, Width, and other attributes on the Grid and on the Button. Nothing has affected the size or placement of the AliceBlue area of the Button.
Clearly I'm missing something. Can anyone help me understand what that is, and how I can format my button (or the UserControl in general) to look the way I want it to?
Thanks in advance for your help!
Instead of assigning a string value to the Content property have you tried to put in a TextBlock child element? This might give you more control Hi. Button has HorizontalContentAlignment and VerticalContentAlignment that you can set. And then you can modify the HorizontalAlignment and VerticalAlignment of the child TextBlock inside the button independently.

XY coordinates of WPF button move on runtime?

I got a pretty weird behavior of my WPF application: the XY position of my button on runtime seems to be divergent to that when I set it in my xaml-Editor of Visual Studio (is there a name for it btw?)
It has no alignments set or panels around it, i have only set it by margins. My button has the following code:
<Button Content="OK" Height="23" Margin="213,319,4,7" Name="button3" Width="75" IsCancel="True" Click="button3_Click" IsEnabled="False" />
Edit:
The margins are fixed because it is a non-resizable dialog. As you can see, the button's slightly moved to the left and up:
xaml-Editor:
Runtime:
Why is that and how can I fix it?
I guess the below link about the Alignment, Margins, and Padding Overview will help you to understand how it is works?
Else place a panel wrappers such as Stackpanel, Wrappand or Grid. It's suitable to work the layout of the controls
EDIT : The problem was with the ResizeMode="NoResize". If you remve this attribute in Window tag, then alignment would be good
Link to Refer
Man, that's the worst way to set the position of a UI element in WPF!
Refactor your XAML to something like this:
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Grid.Row="0" Name="TableList" Margin="5"/>
<Button Grid.Row="1" Name="button3" Content="OK"
Margin="5"
Width="75"
HorizontalAlignment="Right"/>
</Grid>
You see? There is a Grid that handles the position of all its children (in this case, a ListView and a Button).
The Button is put on the second Row, aligned to the right (HorizontalAlignment property).
Both the Grid and its children have Margin=5. This guarantees that the margin of every children is equal respect to the adiacent children and to other controls outside the Grid.
Also, the ListView and the Button are perfectly aligned.
The problem with your approach is that you set the Button Width and its Left Margin and its Right Margin. Maybe the total is not correct because the border of the Window eats some pixel, or simply WPF can't handle all the informations together and misses the calculation, who knows, but the consequence is that you must leave at least one parameter free. In my example, I left free the Margins from the Window. The Margin=5 sets only the relative Margin respect to the other controls, but how much the Button is distant from the left border of the Window is something I leave to the WPF graphical engine to calculate.

WPF .NET4.0 Content control growing out of screen

Ive got an issue which has been frustrating the heck out of me for a while. The content which is displayed grows out of screen. I.e. no window sizing is applied.
Following is what Ive tried:
<Window ...
d:DesignHeight="768" d:DesignWidth="1024"
MinHeight="768" MinWidth="1024"
mc:Ignorable="d" SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterScreen" WindowState="Maximized"
Closed="WindowClosed" Loaded="Window_Loaded">
Ive tried setting MaxWidth and MaxHeight on both the host and main window and still no luck.
and in code behind Window:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
MinWidth = ActualWidth;
MinHeight = ActualHeight;
ClearValue(SizeToContentProperty);
}
Code on the host control:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height= "*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentControl x:Name="content"
Grid.Row="1" Grid.Column="0" Content="{Binding ContentToDisplay}"
VerticalAlignment="Stretch" VerticalContentAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"/>
</Grid>
Ive tried sticking the ContentControl in a ViewBox, DockPanel and still no luck. As soon as I set the content to something "big" (which is within a ScrollViewer) it doesnt bother contain it. What Im trying to achieve is to simply contain the content within the content control within screen.
Thanks!
Maybe as a workaround you could listen to SizeChanged and disable the auto-resizing when you hit the dimensions of the screen?
You have the window marked SizeToContent="WidthAndHeight", so the content is going to size to what it contains. That isn't posted, so it's difficult to say what is causing it. Are you using DataBinding to set any sizes, for example?
Also, you are not setting the maximum width and height of the window to the screen size. That would at least prevent it from getting larger than the screen.
The best thing to do, perhaps, would be to set the window to a specific size, and then in the codebehind, in the Loaded event, set the window to the desired size, or instead set it to maximized once loaded.

Categories