Placing multiple canvases in a Grid - wpf

I want to place several canvases into a grid, each containing an image (and some drawing on top). When I do so, all canvases occupy the same space (almost). Can anyone explain?
This is the XAML:
<Window x:Class="WpfApp4__Various_Tests_.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp4__Various_Tests_"
Title="MainWindow" Height="300" Width="300"
x:Name="root">
<Grid Background="Beige" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Canvas Grid.Column="0">
<Image Source="nav_plain_blue.ico" />
</Canvas>
<Canvas Grid.Column="1">
<Image Source="nav_plain_blue.ico" />
</Canvas>
</Grid>
And this is the result:
EDIT: I noticed, when I use Width="*" in the grid both canvases are shown. I still don't understand why it doesn't work with Auto-size.

The Canvas is not resized automatically so you'll have to set its Width property explicitly if you want the column to auto grow:
<Canvas Grid.Column="0" Width="{Binding ActualWidth, ElementName=img}">
<Image x:Name="img" Source="nav_plain_blue.ico" />
</Canvas>
<Canvas Grid.Column="1" Width="{Binding ActualWidth, ElementName=img2}">
<Image x:Name="img2" Source="nav_plain_blue.ico" />
</Canvas>

Related

Grid column width not respected when inside of a ScrollViewer

I've one view of mine which displays a control based on some factory:
<dxdo:LayoutPanel ShowCaption="False">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ContentControl Margin="15,5,10,10" Content="{Binding SomeObject, Converter={StaticResource ModelToViewModelConverter}}" ContentTemplateSelector="{StaticResource ConventionBasedDataTemplateSelector}" />
</ScrollViewer>
</dxdo:LayoutPanel>
One of the control that is displayed contains one Grid, with some specific controls.
Basically I've the 2 firsts columns that are used with specific sizes and a third one to take the remaining available space, and then some components that takes the whole width:
<UserControl x:Class="ch.VibroMeter.Xms.Configurators.Common.Views.Machinery.MachineStates.CustomMachineStateView"
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"
<!-- skipping namespace declaration -->
mc:Ignorable="d" >
<UserControl.Resources>
<converters:DrawingColorToMediaColorConverter x:Key="DrawingColorToMediaColorConverter" />
</UserControl.Resources>
<StackPanel>
<controls:TitleBlock Title="{Binding State.Name}" Margin="0,0,0, 5" />
<subControls:MainProperties Model="{Binding State, ValidatesOnNotifyDataErrors=False}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Source={x:Static common:Constants.COLUMN_LABEL_WIDTH}}" />
<ColumnDefinition Width="{Binding Source={x:Static common:Constants.COLUMN_FIELD_WIDTH}}" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!--Color-->
<TextBlock Grid.Row="0" Grid.Column="0" Text="Color" Margin="3" />
<dxe:PopupColorEdit Grid.Row="0" Grid.Column="1" ShowAlphaChannel="False" Margin="3" Color="{Binding State.Color, Mode=TwoWay, Converter={StaticResource DrawingColorToMediaColorConverter}}" />
<!--Separator-->
<Separator Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,12,0,12"/>
<!--Measurements Filters -->
<TextBlock Grid.Row="2" Grid.Column="0" Text="Conditions" FontSize="18" Margin="3,3,3,12"/>
<measurementsFilterConfigurationControl:MeasurementsFilterConfigurationControl Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"
FilterRuleTypes="{Binding AvailableFilterRuleTypes}"
MeasurementsFilterConfiguration="{Binding State.FilterConfiguration}"
Margin="0 10 0 0"/>
</Grid>
</StackPanel>
</UserControl>
I found out that the columns sizes are not at all respected. The COLUMN_LABEL_WIDTH is 200, but the ActualWidth of my TextBlock with "Color" text is more than 300.
After some investigation, I found out that if I remove the ScrollViewer or set the HorizontalScrollBarVisibility to Disabled, it works, but then when I resize the window too much(for this control, less than COLUMN_LABEL_WIDTH +COLUMN_FIELD_WIDTH), I don't get the desired scrollbars.
I understand that when I use a scrollViewer, I will get an infinite available size, but I would have thought this space would have been put in the column with the * size.
I cannot removing the scrollViewer Auto, because some components(like the MeasurementsFilterConfigurationControl in this view) might get hidden when resizing the windows).
I don't have access to the ScrollViewer in this component.
So how would you fix this?

Avoid WPF statusbar gets totally collapsed when window gets resized vertically to a smaller size

I have below WPF window which contains a dockpanel as main container. Then I place a main grid at the top (which contains some other grids) and a statusbar at the bottom.
<Window>
<DockPanel>
<Grid DockPanel.Dock="Top">
<!-- Grid stuff here -->
</Grid>
<StatusBar DockPanel.Dock="Bottom"
VerticalAlignment="Bottom">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<TextBlock Text="Item1"/>
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2">
<TextBlock />
</StatusBarItem>
<Separator Grid.Column="3" />
<StatusBarItem Grid.Column="4">
<TextBlock Text="AnotherItem" />
</StatusBarItem>
</DockPanel>
</Window>
I have below problem:
When window is resized vertically to a smaller size, there comes a moment in which statusbar height is reduced and even completely collapsed. So how to avoid this? I want statusbar never gets collapsed and keep all the time its height.
Grids within Grids are so hot right now.
<Window x:Class="GridRoot.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Border BorderBrush="Black" BorderThickness="10" Background="CornflowerBlue">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
Hello!
</TextBlock>
</Border>
</Grid>
<StatusBar Grid.Row="1">
<StatusBarItem>
<TextBlock>
GET OFF ME!
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
The top row takes up as much space is available, and the bottom row takes up as much space as its content desires. Since the StatusBar control pretty much has a set height, it will always stay visible on the bottom.
Some folks might have issue with putting a Grid inside another Grid, but there's absolutely no reason not to.
The above example, tiny
and embiggened

Negative-margin control get clipped when resizing the window in WPF

I try to understand why a border element get clipped when reducing the width of the main window.
Please take a look the code block below.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="500" Name="MainWin">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Background="Blue" Grid.Row="0" BorderBrush="Black" Width="{Binding ElementName=MainWin, Path=Width}" />
<Grid Grid.Row="1" Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Background="Black">
<Border Background="White" Width="150" Height="150" BorderBrush="Black" BorderThickness="2"
Margin="0,-100,0,0">
<TextBlock Text="{Binding ElementName=MainWin, Path=Width}" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Background="Red" />
<StackPanel Grid.Column="2" Background="Yellow" />
</Grid>
</Grid>
</Window>
Here is what the border appears in the original window width:
Non-resized window
As you can see that the border is displayed outside its container because of the negative top margin, -100 in this case. This is what I expect to have for the border. But when I reduce the main window width to reach the right edge of the red rectangle the outside part of the border get clipped.
Resized window
I have tried to place this border element inside a custom StackPanel which overrides ArrangeOverride, MeasureOverride and GetLayoutClip method but unfortunately these methods are not invoked when the main window is being resized.
I appreciate if somebody can explain me what the reason is and how to work around with this issue.
Thanks a lot.
Based on the explanation of #Marks, here is my solution
Create a custom grid and overrides MeasureOverride method
Replace the inner grid by this custom grid
CustomGrid class
public class CustomGrid : Grid
{
private double _originalHeight = 0;
protected override Size MeasureOverride(Size constraint)
{
Size? size = null;
if (constraint.Width <= 300)
{
size = new Size(constraint.Width, _originalHeight);
}
else
{
size = base.MeasureOverride(constraint);
_originalHeight = constraint.Height;
}
return size.Value;
}
}
XAML code
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="300" Width="500" Name="MainWin">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Background="Blue" Grid.Row="0" BorderBrush="Black" Width="{Binding ElementName=MainWin, Path=Width}" />
<wpfApplication1:CustomGrid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Background="Black">
<Border Background="White" Width="150" Height="150" BorderBrush="Black" BorderThickness="2"
Margin="0,-100,0,0">
<TextBlock Text="{Binding ElementName=MainWin, Path=Width}" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Bottom" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Background="Red" />
<StackPanel Grid.Column="2" Background="Yellow" />
</wpfApplication1:CustomGrid>
</Grid>
You're binding the blue border's Width to the MainWindow's Width.
For the future: if you want to bind to the width of any FrameworkElement bind to its ActualWidth property.
The order in which WPF draws its stuff is quite dependent on the containing control. I'd say in your case the outer Grid draws its children that need updating in the order they are defined. So you're good to go as long as the inner grid changes along with the border. This is the case as long as the Width of the third column changes. Once it's at 0 there's no more change so it doesn't get updated.
(2) is speculation =)
don't do (1), there's no need for it
Use one Grid
Some XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="Blue" BorderBrush="Black" Grid.ColumnSpan="3"/>
<StackPanel Grid.Column="0" Grid.Row="1" Background="Black" >
<Border Background="White" Width="150" Height="150" BorderBrush="Black" BorderThickness="2" Margin="0,-100,0,0">
<TextBlock Text="{Binding ElementName=MainWin, Path=ActualWidth}" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1" Background="Red" />
<StackPanel Grid.Column="2" Grid.Row="1" Background="Yellow" />
</Grid>

Use all browser space in Silverlight application

I am currently writing a Silverlight application that will view documents. The main panel is a WrapPanel with controls on the left and bottom of the page. Currently, the WrapPanel width is getting set to whatever it needs to be able to display all document pages on one line and I would like to change it so that it will fill up the rest of the browser window, with no horizontal scroll bar
Here is my current xaml, where <doc:DocumentViewer x:Name="Viewer".. is the wrap panel in question.
<UserControl x:Class="SilverlightXPSViewer.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"
xmlns:doc="http://schemas.firstfloorsoftware.com/documenttoolkit"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="White" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<doc:ThumbnailListBox x:Name="Thumbnails" Grid.Row="0" Grid.Column="0" DocumentDataSource="{Binding ElementName=DataSource}" PageIndex="{Binding PageIndex, Mode=TwoWay, ElementName=PageNavigator}"/>
<doc:DocumentViewer x:Name="Viewer" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" DocumentDataSource="{Binding ElementName=DataSource}" Width="{Binding ElementName=MainWidth}"/>
<doc:DocumentDataSource x:Name="DataSource" Grid.Row="0" LoadError="DataSource_LoadError" />
<StackPanel Grid.Column="3" Grid.Row="1" Orientation="Horizontal">
<doc:PageNavigator x:Name="PageNavigator" Margin="0"
PageCount="{Binding PageCount, ElementName=Viewer}"
PageIndex="{Binding PageIndex, ElementName=Viewer, Mode=TwoWay}"/>
<doc:PageNavigator />
</StackPanel>
<doc:ViewModePicker Grid.Row="1" Grid.Column="0" x:Name="Collection"/>
<Button Width="200" Name="btnZoomIn" Grid.Row="1" Grid.Column="1" Cursor="Hand" Click="btnZoomIn_Click">Zoom In</Button>
<Button Width="200" Name="btnZoomOut" Grid.Row="1" Grid.Column="2" Cursor="Hand" Click="btnZoomOut_Click">Zoom Out</Button>
</Grid>
</UserControl>
Use the System.Windows.Browser.HtmlPage object to get the available screen size from the HTML DOM (thus letting the browser to tell you how much space there is) and set the control size from that.
here's an example:
http://www.jeff.wilcox.name/2008/06/browserscreeninformation/

How to get this window layout in WPF?

im trying to create a Window with the following layout:
Layout http://www.x-toolz.com/downloads/layout.jpg
As you can see the window has 3 rows (15*, 70*, 15*) and 3 columns (same).
How can I redesign a rectangle to fit the geometry of the corners?
If it can't be done with rectangles i would need another control that I can place content (Grid, StackPanel) in.
Any Ideas?
Thanks in advance!
MemphiZ
You could do that with a grid with 9 cells. Create 8 usercontrols to hold your outside content. If you want it size adjustable, you are going to have to work a little magic.
Each corner user control would have a 2x2 grid and for the upper left panel I will give a small example.
<UserControl
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:ec="http://schemas.microsoft.com/expression/2010/controls"
mc:Ignorable="d"
x:Class="TopLeft"
x:Name="UserControl"
d:DesignWidth="480" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Rectangle Stroke="Black" Grid.RowSpan="2" Fill="Black"/>
<Rectangle Fill="Black" Stroke="Black" Grid.ColumnSpan="2"/>
<Path Grid.Column="1" Data="M0.5,0.5 L239.5,0.5 120,120 0.5,239.5 z" Fill="Black" Grid.Row="1" Stretch="Fill" Stroke="Black" />
</Grid>
</UserControl>
In the above example a 2 x 2 grid, with a diagonal path in the bottom right. If your main window is going to resize you will have to decide whether or not your border areas will resize accordingly or be a static frame around the body of the window.
Here is the window:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MegaPanel"
x:Class="MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"/>
<ColumnDefinition Width="0.3*"/>
<ColumnDefinition Width="0.3*"/>
</Grid.ColumnDefinitions>
<local:TopLeft Margin="0"/>
</Grid>
</Window>
I failed to put a Content presenter in the UserControl, but you would put that in there to add content to it.
The body area of the window would have to be handled with some care. You can set the Margin to negative values to allow the contents of the body to spill out into the frame areas.
Edit
Example:
<local:TopLeft Margin="0">
<local:TopLeft.Tag>
<ListBox/>
</local:TopLeft.Tag>
</local:TopLeft>
The above change to Top left assigns the ListBox to the Tag property of the TopLeft usercontrol. In the User control, I bound the ContentPresenter to the Tag Property of the UserControl. ListBox is assigned to the tag, ContentPresenter gets the ListBox from the Tag. Of you can regester custom properties in the UserControl codebehind if you want things in several areas.
<ContentPresenter Grid.RowSpan="2" Grid.ColumnSpan="2" Margin="0,0,125,125" Content="{Binding Tag, ElementName=UserControl}"/>
For registering custom DependencyProperties check this post out.
<Window x:Class="WpfApplication2.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="15*" />
<RowDefinition Height="15*" />
<RowDefinition Height="40*" />
<RowDefinition Height="15*" />
<RowDefinition Height="15*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="40*" />
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="15*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Background="Blue">
<!-- Top Left Content Goes Here -->
</Grid>
<Grid Grid.Column="2" Grid.Row="0" Background="Aqua">
<!-- Top Middle Content Goes Here-->
</Grid>
<Grid Grid.Column="3" Grid.Row="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Background="Gold">
<!-- Top Right Content Goes Here -->
</Grid>
<Grid Grid.Column="0" Grid.Row="2" Background="Magenta">
<!-- Middle LEft Content goes here -->
</Grid>
<Grid Grid.Column="4" Grid.Row="2" Background="Lime">
<!-- Middle Right Content goes here -->
</Grid>
<Grid Grid.Column="0" Grid.Row="3" Grid.RowSpan="2" Grid.ColumnSpan="2" Background="Red">
<!-- Bottom Left Content Goes Here -->
</Grid>
<Grid Grid.Column="2" Grid.Row="4" Background="DarkGoldenrod">
<!-- Bottom Middle Content Goes Here-->
</Grid>
<Grid Grid.Column="3" Grid.Row="3" Grid.RowSpan="2" Grid.ColumnSpan="2" Background="Silver">
<!-- Bottom Right Content Goes Here -->
</Grid>
<!-- This is used to shape the center" -->
<Polygon x:Name="main" Grid.Column="1" Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="3" Fill="White" Points="0,15 15,0 55,0 70,15 70,55 55,70 15,70 0,55" Stretch="Fill" StrokeThickness="0"/>
<Grid Grid.Column="1" Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="3" Background="Pink" >
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=main}" />
</Grid.OpacityMask>
<!-- Centre Content Goes Here-->
</Grid>
</Grid>
</Grid>
</Window>
This produces this layout. The limitation is that WPF does its bounds clipping to rectangles so any content that overflows the regions will be made invisible (i.e. Clipped).
You could partially work around this by applying padding to each grid element to a create a rectangular area that fits inside each region.

Resources