UserControl Dimensions Ignored in MainPage Layout - wpf

I have a Canvas with a Grid on it. The Grid contains 3 columns. The first column contains another Grid, the second contains nothing, the third contains an Image (a box). (I have the blank column for animation purposes unrelated to this question.)
In my child Grid I have 2 rows. The first row contains a UserControl (shelves of boxes), the second contains an Image (a barcode).
The problem is that the layout engine seems to completely ignore the dimensions of the user control. In the preview I see my UserControl and directly on top of it the two other images. Granted Canvas doesn't automatically size its children, but I have that resolved with some resize statements in the SizeChanged event of the page.
As it is, the box shows up at runtime in the bottom right corner of my window as it should, however the barcode appears positioned as if the UserControl doesn't exist.
For an illustration of my issue please see http://www.wynright.com/temp/Problem.png
The following is my XAML code:
<Canvas x:Name="MainCanvas" Background="Silver">
<Grid Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid Name="LayoutChild" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<LightPickDemo:Picker x:Name="PickZone" ZoneCleared="PickZone_ZoneCleared" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Image Name="BarCode" Source="/LightPickDemo;component/Images/BarcodeScan.png" HorizontalAlignment="Center" Stretch="None" VerticalAlignment="Center" Grid.Row="1" />
</Grid>
<Image Name="imgShippingBox" Source="/LightPickDemo;component/Images/Empty-Box.png" VerticalAlignment="Bottom" Stretch="None" Grid.Column="2" />
</Grid>
</Canvas>
So I wonder what do I need to do to get the UserControl to offer its size for the layout engine. I can see in my designer the size of the control, so clearly the compiler knows that there is size to this control.
I would like the barcode to show centered below the UserControl.
Here is the XAML for the UserControl:
<Canvas>
<Image Name="ImageBase" Source="/LightPickDemo;component/Images/BayShelves.png" />
<LightPickDemo:ZoneLight x:Name="ZoneLight" Canvas.Left="345" Canvas.Top="0" ButtonPress="ZoneLight_ButtonPress" />
<LightPickDemo:PickerModule x:Name="pickerCalculator" Canvas.Left="74" Canvas.Top="266" OnPicked="Picker_PickChanged" />
<LightPickDemo:PickerModule x:Name="pickerCalendar" Canvas.Left="207" Canvas.Top="266" OnPicked="Picker_PickChanged" />
<LightPickDemo:PickerModule x:Name="pickerPaperClip" Canvas.Left="521" Canvas.Top="266" OnPicked="Picker_PickChanged" />
<LightPickDemo:PickerModule x:Name="pickerRuler" Canvas.Left="649" Canvas.Top="266" OnPicked="Picker_PickChanged" />
<LightPickDemo:PickerModule x:Name="pickerScissor" Canvas.Left="75" Canvas.Top="532" OnPicked="Picker_PickChanged" />
<LightPickDemo:PickerModule x:Name="pickerStapler" Canvas.Left="250" Canvas.Top="532" OnPicked="Picker_PickChanged" />
<LightPickDemo:PickerModule x:Name="pickerStapleRemover" Canvas.Left="435" Canvas.Top="532" OnPicked="Picker_PickChanged" />
<LightPickDemo:PickerModule x:Name="pickerTapeDispenser" Canvas.Left="635" Canvas.Top="532" OnPicked="Picker_PickChanged" />
</Canvas>

Most likely the ActualHeight of your user control is zero. Looking at your XAML for the user control you should be able to just set Height and Width in the XAML of the user control.

I can't determine what is the UserControl from your image, but if you set the property to 'collapsed' the engine won't read the size I don't think. If the UserControl isn't visible, but you want to show the dimensions, animate the opacity property instead of visibility. The barcode is in Row 1 which is the top row, should it be in row 2?
Am I correct in assuming the barcode needs to be below the image with the 2 rows of boxes? Where is the UserControl? What is it?

An answer to my question seems to be to put the following code in my UserControl, however, I can't imagine this is the best answer and I know it doesn't allow for screen scaling at all. So please, if anyone knows how to do this correctly, please let me know.
Thanks to Wallstreet Programmer for leading me to this workaround.
Protected Overrides Function MeasureOverride(ByVal availableSize As System.Windows.Size) As System.Windows.Size
Dim bmp As BitmapImage
bmp = BaseImage.Source
Return New Size(bmp.PixelWidth, bmp.PixelHeight)
'Return MyBase.MeasureOverride(availableSize)
End Function

Related

Let 2 Controls automatic fill space with same size

I want two texboxes fill the available Space in a panel. But both have to contain the same width. So DockPanel lastchildfill doesn't works here.
I have an image where u can see, i simply created 2 textboxes without any width. The empty Space should be filledby both textboxes like the above (the above is made with manual Width="xxx". But I dont' want to hardcore it like this.
I want it like Grid.Column width="*"
Do I have to create a Grid for each panel? (There will be much more textboxes)
my XAML is simple because I dont know how to realize my idea:
<DockPanel LastChildFill="False">
<TextBox DockPanel.Dock="Left" x:Name="nachname_textbox"/>
<TextBox DockPanel.Dock="Right" x:Name="vorname_textbox"/>
</DockPanel>
UniformGrid control might suit your needs : it automaticaly places all controls in cells with the same amount of width en height for each ones. And you can define how many rows and columns you want.
<UniformGrid Columns=2>
<TextBox />
<TextBox/>
</UniformGrid>
If you want two elements to fill 50% each of the avalable width of a Panel, you should put a Grid with two ColumnDefinitions in each Panel, i.e. simply replace the DockPanel with a Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox />
<TextBox Grid.Column="1" />
</Grid>
If you want to decrease the amount of XAML, you could create your own custom Grid class:
public class My2x2Grid : Grid
{
public My2x2Grid()
{
ColumnDefinitions.Add(new ColumnDefinition());
ColumnDefinitions.Add(new ColumnDefinition());
}
}
...and use this one:
<local:My2x2Grid>
<TextBox />
<TextBox Grid.Column="1" />
</Grid>

Recreating WinForms layout in WPF

I've just inherited an old WinForms app with a UI layout like this:
I'm tasked with updating several things about the software one of which is porting it to WPF which I've not previously worked with. I'm also told that the new WPF UI must look identical to the existing UI layout so I'm trying to figure out how to create that layout in WPF. I need a toolbar across the top of the window which stretches the entire width of the fixed-size window. Can I do that in the default grid or do I need a dockpanel to do that? Also, I'm assuming that I would use a grid with 2 columns and 3 rows to layout the six groupboxes?
Anything you can do with a DockPanel can also be done with a Grid -- the DockPanel is just a shortcut. So yes, you can do all this with the default Grid.
As for how to do the layout: it depends on how you want things to resize. Does everything stay proportional when you resize? If so, a single Grid with three columns (and percentage sizes for the ColumnDefinitions) would be fine. You would actually need four rows, though, not three -- the first RowDefinition would be for the ToolBar (using ColumnSpan="3") and would need Height="Auto" so it uses the ToolBar's default size; the remaining rows would be percentage-sized.
Try that, see if it works for you. If the resizing needs to be more complicated than just proportional, then post a second screenshot of the window at a different size, and we could try to help you further.
Personally I would use a DockPanel for the Menu/Content areas, then use a Grid in the Content Area to define the GroupBoxes
<DockPanel>
<Grid x:Name="MenuRegion" DockPanel.Dock="Top" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" />
<Grid Grid.Row="0" Grid.Column="2" />
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" />
<Grid Grid.Row="1" Grid.Column="2" />
<Grid Grid.Row="2" Grid.Column="0" />
<Grid Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" />
</DockPanel>
Of course, you could also make the Menu area part of the Grid and set it to span all rows, but I personally like keeping them separate.
Grid with 3 row (one for the menu). Since you have uneven spacing in the rows just a single column on the main grid. Then grid in the grid for the two column spacings.

WPF: Star in ColumnDefinition not expanding columns

I have a user control that need the 1st and 3rd column to have the same width at all time.
My code is a follows:
<UserControl x:Class="UserControls.ListBoxSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox x:Name="ListBox_Source" Grid.Column="0" Grid.Row="0" />
<StackPanel Grid.Column="1" Grid.Row="0" Orientation="Vertical">
<Button Content=">" Click="Button_Add_Click"/>
<Button Content="<" Click="Button_Remove_Click" />
</StackPanel>
<ListBox x:Name="ListBox_Destination" Grid.Column="2" Grid.Row="0" />
</Grid>
</UserControl>
The result is not as expected as column 3 (ListBox_Destination) is not expanded at all.
Isn't the 5* in ColumnDefinition enough to force the 2 listbox to the same width??
UPDATED : Sorry that I forgot to mention that the problem only occurs when I put the control inside a RibbonGroup using Microsoft Ribbon for WPF
Sometimes, when you put your control in certian types of layout controls (like a StackPanel), it won't size as expected because the parent layout will only size the child to it's minimum desired size (just enough to show the content). This may be why you are seeing this when you put it in the RibbonGroup. Try giving your Grid a Width or MinWidth and see if that makes a difference.
yes it forces the columns 1 and 3 to be of the same size, but it doesnt gaurentee the content (listboxes) inside the colulms will be of the same size. You have to set the size of content to take up whole space

wpf layout help

I have the following xaml which resides in a wpf user control -
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<TextBox
x:Name="MyTxt"
TextWrapping="WrapWithOverflow"
Grid.Row="0"
/>
<ListView
x:Name="MyList"
ItemsSource="{Binding}"
Grid.Row="1"
/>
<Label
Grid.Row="2"
/>
</Grid>
This control is nested within a grid in a view. I would like to have the text box be a set height at the top of the grid, the label at the bottom showing as a fixed height at the bottom of the grid. I want the list view to fill the rest of the screen area.
The problem that I am having is the listview does not size correctly. If I have too many records that show up in it, it extends beyond the window and no scroll bars are available to scroll down. I therefore cannot get to the bottom to see the vertical scroll bar if the data stretches off to the right of the screen.
I was able to set the listview to a fixed height and that worked, but I would like it to be more dynamic and resize with the window if possible.
Does anyone have any tips that might get the sizing correct?
Thanks for any thoughts.
EDIT - Here is the xaml for the containing grid in the mainwindow view. this was adapted from the article by Josh Smith here
<Grid>
<Border
Style="{StaticResource MainBorderStyle}"
>
<HeaderedContentControl
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}"
/>
</Border>
</Grid>
I do have the scrollviewer properties set as mentioned in some of the answers below.
Here is the datatemplate for the workspace
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
</DataTemplate>
Can you just add these properties to the listview?
ScrollViewer.CanContentScroll = "True"
ScrollViewer.VerticalScrollBarVisibility="Visible" <!-- or "Auto" -->
Everything else looks ok to me. You have the 3 rows, 2 of which are absolute, the other stretching. You also have the listview in the 2nd row, so it should stretch with it.
if that doesn't work, try wrapping the ListView in a scrollviewer
<ScrollViewer>
<ListView/>
</ScrollViewer>
What is the VerticalAlignment of a ListBox by default? You might need to set the vertical alignment to Stretch.
<ListView
x:Name="MyList"
ItemsSource="{Binding}"
Grid.Row="1"
VerticalAlignment="Stretch"
/>
I was able to get it working. If I change the containing grid in the main window to use a ContentControl instead of a HeaderedcontentControl, it works as expected.
Thank for any help.

WPF buttons same/recommended width

Suppose you have a window with multiple buttons such as Ok/Cancel or Yes/No/Cancel. All the buttons need to be the same width. Obviously this could be done by just guessing a number and hardwiring all of them to that number.
Is there a better way to do it, one that would take into account preferred/recommended sizes (just how wide should an Ok button be anyway? This is not a rhetorical question, I actually don't know the answer!), what's needed by the text of the longest caption, what happens if the font size is increased etc?
Another, perhaps simpler, way to do this is to use the SharedSizeGroup property on the ColumnDefinition and RowDefinition classes.
Columns (and Rows) in a WPF Grid can automatically resize to fit their contents - when SharedSizeGroup is used, columns with the same group name share their resizing logic.
The Xaml would look something like this ...
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Ok"
Margin="4" />
<Button Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Cancel"
Margin="4" />
<Button Grid.Column="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Long Button Caption"
Margin="4" />
</Grid>
There are several ways to do this:
1) Use a Grid for layout. Each Button gets its own Column, which is Star-sized. That way, all columns are the same size:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Yes</Button>
<Button Grid.Column="1">No</Button>
<Button Grid.Column="2">Cancel</Button>
</Grid>
2) You can have one item as "master size" and bind the width of all others to this item's width.
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton" Width="100">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="Width"/>
</Button.Width>
No
</Button>
</StackPanel>
EDIT: In actual code, you probably will have Width="Auto". Since the other widths are based on the "master width", the button with the widest width (widest text) should be chosen.
Use a "master" control, like in Daniel's answer, but bind to the "ActualWidth" attribute instead of "Width":
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="ActualWidth"/>
</Button.Width>
No
</Button>
</StackPanel>
This way, the value is taken from the master control at run time, after minimum and maximum width and all other layout calculations have been taken into account. Binding to "Width" binds to whatever you happen to put into the attribute at compile time, which may not be the width that is really used.
Also, the binding can be written shorter like
<Button Width="{Binding ElementName=MasterButton, Path=ActualWidth}"/>
According to the MS User Experience Interaction Guidelines for Windows 7 and Windows Vista (p61), standard dimensions for command buttons are 50x14 DLU actual size (75x23 pixels). The guidelines further suggest you "try to work with [these] default widths and heights." Obviously, if you need more width to fit a clear label, then take more width.
These answers are great if you have a fixed number or fixed layout for the buttons, but if like me there is a dynamic number of buttons coming from a binding and contained in a ItemsControl then this is not feasible. But there is a simple way and it still involves used the sharedsize property of Grid.
DataTemplate:
<DataTemplate x:Key="ODIF.Mapping">
<Button HorizontalContentAlignment="Left" Background="#FFEEEEEE" BorderBrush="#FFBDBDBD">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="PluginButtonsWidth"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsIconHeight"/>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsNameHeight"/>
</Grid.RowDefinitions>
<Image Width="32" Height="32" Source="{Binding PluginIcon}" RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Grid.Row="1" Text="{Binding PluginName}"/>
</Grid>
</Button>
</DataTemplate>
Parent container:
<ItemsControl ItemsSource="{Binding MappingPlugins, ElementName=page}" ItemTemplate="{StaticResource ODIF.Mapping}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Grid.IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Essentially the button's content can itself be a Gird which then you can place your labels and icons as needed in, but even though the buttons do not reside in the same grid (they each are their own) the grid can still share it size so long as you set the root container's (ItemsControl) property of Grid.IsSharedSizeScope to True.
This will force the content grid of each button to be the same exact size based on the largest one while not having to have the Buttons themselves in a predefined grid.
In the most general case, you want to create a
Style in your section, then apply this style as desired. Now when you change the style, all buttons change.
Or you can change the Content of the button so that it autosizes to the text.

Resources