WPF width of window not correct - c#

I am trying to fit an exact amount of list items in the viewbox of my window so I retrieve the width of the corresponding ListBox and divide it by the amount I need (in this case 7) like this:
Width="{Binding Path=ActualWidth, ElementName=WeekView, Converter={StaticResource DivideBySeven}}"
The width of my window is 800 and I checked inside the converter what values it gets (784 for the ListBox what seems reasonable) and what value it returns (112, if my math is correct this is also correct).
Problem is my app looks like this:
I thought ok maybe some calculation got wrong and manually set the item width to 100 (so 700 with all) and I got this:
And on the right side there are not 84 (whatever unit is used) of space.
EDIT: If I resize the window to fullscreen the calculation seems to be correct but I also don't know why or what is going wrong.
Any ideas what I made wrong or how I can get the behaviour I want?
EDIT2:
This is the code to reproduce the issue:
<Window x:Class="Test.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="800">
<Grid>
<ListBox HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
</ListBox>
</Grid>
</Window>

You have a couple different problems:
Don't rely on the Window for calculating precise Width and Height for your content. The Window has invisible OS-level elements that factor into its total size.
Solution: Remove the Height and Width from your Window and use SizeToContent="WidthAndHeight" instead. Set your precise Width and Height in your root layout element.
There are hidden Padding, Margin, and BorderThickness values in many controls.
Solution: I highly recommend that you use Snoop for WPF or the built-in Visual Studio tools to look at elements at runtime and see what they are using. You can then override these values (in most cases) via styles, etc.
Here is an edited version of your example that works:
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<Grid
Width="800"
Height="350">
<ListBox
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type ListBoxItem}}" TargetType="ListBoxItem">
<Setter Property="Padding" Value="0" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
<StackPanel Width="112" />
</ListBox>
</Grid>
</Window>

Have you tired this code?
<Window x:Class="Test.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="800">
<Grid>
<ListBox HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<StackPanel Width="*" />
<StackPanel Width="*" />
<StackPanel Width="*" />
<StackPanel Width="*" />
<StackPanel Width="*" />
<StackPanel Width="*" />
<StackPanel Width="*" />
</ListBox>
</Grid>

Related

ScrollViewer not working with ItemsControl

I'm trying to use a ScrollViewer to be able to scroll the items in an ItemsControl but for some reason, it's not working. The scroll view shows but it is disabled.
<UserControl x:Class="Tool.Views.ShortcutsView"
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"
d:DesignWidth="500"
mc:Ignorable="d" Height="541">
<UserControl.Resources>
<Style x:Key="GlobalShortcutButtonTemplate" TargetType="{x:Type Button}">
<!-- Style code -->
</Style>
</UserControl.Resources>
<Grid Margin="10,40,10,0" Background="White" Height="108" VerticalAlignment="Top">
<ScrollViewer CanContentScroll="True">
<ItemsControl
ItemsSource="{Binding ShortcutsObservableCollection}"
Height="108" VerticalAlignment="Top" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Height="35"
Content="{Binding ShortcutName}"
Command="{Binding ShortcutCommand}"
CommandParameter="{Binding FilePath}"
Margin="10 0 0 10"
Background="#FF30CCFF"
Foreground="White"
Padding="10,0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>
This is what I see...
There are plenty of items in the ItemsControl for the scroller to show and be able to scroll the items in it, the rest of the items are hidden.
Any idea what can I do to make the scroller to show up properly?
Just remove Height="108" from your ItemsControl. You can't scroll because there is nothing to scroll.

Add row of text above a grid of items

I'm building a grid via WPF, and it works great. I need to add a row, or bar, or something to display above the grid, that will have several text items on it that will get populated by code. I've been tooling around, and I can't seem to figure out how to put another panel above my existing (and working) grid. This is my code:
<Window x:Class="GridWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Board" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto">
<Window.Resources>
<DataTemplate x:Key="DataTemplate_2">
<Button Content="{Binding}" Height="25" Width="25" Margin="0,0,0,0"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid Name="GridBoard" ShowGridLines="True">
<ItemsControl x:Name="GridItems" ItemTemplate="{DynamicResource DataTemplate_1}"/>
</Grid>
</Window>
The GridItems gets populated by a jagged array, and displays fine. I just need to put a few text objects above it, be it boxes, or just a horizontal panel that fits the width of the grid.
May be the simplest option is to add a wrapper Grid and put your inner Grid in the second row. So, you will have the first row (row 0) to put anything you need there.
<Window x:Class="GridWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Board" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto">
<Window.Resources>
<DataTemplate x:Key="DataTemplate_2">
<Button Content="{Binding}" Height="25" Width="25" Margin="0,0,0,0"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<!-- WHATEVER YOU NEED -->
</Grid>
<Grid Name="GridBoard" ShowGridLines="True" Grid.Row="1">
<ItemsControl x:Name="GridItems" ItemTemplate="{DynamicResource DataTemplate_1}"/>
</Grid>
</Grid>
There are a few ways to achieve it.
1 - you can add a row and columns to your grid definition:
<Grid.ColumnDefinitions>
<ColumnDefinition width="*" />
<ColumnDefinition width="*" />
<ColumnDefinition width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
Then you can just assign 'headers' to each column, like so:
<TextBlock ... Grid.Column="0" />
<TextBlock ... Grid.Column="1" />
<TextBlock ... Grid.Column="2" />
And place your ItemsControl in the second row:
<ItemsControl ... Grid.Row="1" Grid.ColumnSpan="3" />
A half-assed way that may or may not work in your case. No way of knowing without taking a look at the DataTemplate.
2 - StackPanel at the top, with margin applied to the ItemsControl
You can use a similar half-solution - set the top margin of your ItemsControl to a fixed value, like so:
<ItemsControl ... Margin="2,48,2,2" />
And add a StackPanel to your grid:
<StackPanel Height="48" VerticalAlignment="Top">
<TextBlock Text="First Header" Width="300" />
...
</StackPanel>
I'm sure you get the idea.
3 - Use a GridView -RECOMMENDED-
This would require you to change your ItemsControl to a ListView, though.
Here is a well-written tutorial on GridViews for WPF. Granted, it might not be what you're looking for, but again - without knowing what you're trying to present it's difficult to find best solution.
I don't know what you expect the output to look like, but you can nest your "GridBoard" Grid within another Grid. The new outer Grid defines two rows, whereby in the first row you can put your boxes or whatever you like. This could look like this for example:
<Window x:Class="GridWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Board" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto">
<Window.Resources>
<DataTemplate x:Key="DataTemplate_2">
<Button Content="{Binding}" Height="25" Width="25" Margin="0,0,0,0"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="Put something here ..." />
</Grid>
<Grid Grid.Row="1" Name="GridBoard" ShowGridLines="True">
<ItemsControl x:Name="GridItems" ItemTemplate="{DynamicResource DataTemplate_1}"/>
</Grid>
</Grid>
</Window>
Note that you could use any element for the content of the first row of your outer Grid. This depends on your actual needs. In this example I used another Grid which contains a single TextBlock.

Setting user control position

Hello Stackoverflowers,
I'm using an ItemsControl and my elements are all drawn on the top left corner, which create to a gap between the actual position and the mouse position (where my elements should be). Relevant mouse positions are saved in view model.
<ItemsControl
ItemsSource="{Binding OverlayElementsList}"
Background="Transparent"
BorderBrush="Black"
BorderThickness="2">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding ElementName=X, Path=Client.OverlayElement.StartPoint}" />
<Setter Property="Canvas.Top" Value="{Binding ElementName=Y, Path=Client.OverlayElement.StartPoint}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type elements:OverlayElement}">
<ContentControl Content="{Binding View}" />
</DataTemplate>
</ItemsControl.Resources>
Here is one of the possible View binded in the ItemsControl.Resources :
<UserControl x:Class="Client.ImageOverlayElementView"
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:local="clr-namespace:Genetec.CS.OverlayModule.Client"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:ImageOverlayElement}">
<Grid>
<Image
Source="{Binding ImageSource}"
Height="{Binding Height}"
Width="{Binding Width}"
Canvas.Left="{Binding StartPoint}"
Canvas.Top="{Binding EndPoint}"/>
</Grid>
</UserControl>
The Canvas.Left and Canvas.Top properties seem to be simply ignored, why ?

WPF - WrapPanel doesn't update/redraw images content

I am loading images from a folder, showing them in a wrappanel in a windows 'Large Icons'-kind of way, so the user can choose/mark which images he would like.
Currently I am reading the image filelocations, add them to a list of strings, and sets the ItemSource to an ItemsControl which looks like this:
<ItemsControl x:Name="ItemsControlPhotos" Margin="10" >
<ItemsControl.Template>
<ControlTemplate>
<WrapPanel FlowDirection="LeftToRight" IsItemsHost="true">
</WrapPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate >
<Image Source="{Binding}" Width="100" Height="100" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
That seems to work fine. But some of the images looks wierd, it is as if they are only drawn once.
Please refer to this video for an example.
I´ve tried to manually update the list with several different approaches:
ItemsControlPhotos.Items.Refresh();
ItemsControlPhotos.InvalidateArrange();
ItemsControlPhotos.InvalidateMeasure();
ItemsControlPhotos.InvalidateVisual();
But none of it has any effect, so I´m kind of lost.
Thank you very much for your help! :)
EDIT:
This is the full window markup:
<Window x:Class="Name.Space"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="PhotoChooser"
WindowState="Maximized"
Width="1280"
>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
<System:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">40</System:Double>
</ResourceDictionary>
</Window.Resources>
<Border BorderBrush="Black" BorderThickness="1" Background="White" Width="1280">
<DockPanel>
<Image Source="..\Images\BGTop.png" DockPanel.Dock="Top" Grid.Row="0" />
<Grid DockPanel.Dock="Top" Margin="0,-80,0,0" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer>
<ItemsControl x:Name="ItemsControlPhotos" Margin="10" >
<ItemsControl.Template>
<ControlTemplate>
<WrapPanel FlowDirection="LeftToRight" IsItemsHost="true">
</WrapPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate >
<Image Source="{Binding}" Width="100" Height="100" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
<Grid DockPanel.Dock="Bottom">
<Button Height="50" Width="200" Click="ButtonBase_OnClick">Add photos</Button>
<Image Source="..\Images\BGBottom.png" Grid.Row="99" VerticalAlignment="Bottom" Margin="0,0,0,0" />
</Grid>
</DockPanel>
</Border>
</Window>
I´ve didnt really figure the problem out, but I found a solution while working with the project.
Instead of loading the whole image, I would like to only load a thumbnail, to speed up the process. So I followed this link: http://blogs.msdn.com/b/dditweb/archive/2007/08/22/speeding-up-image-loading-in-wpf-using-thumbnails.aspx
And now the problem is gone.
Thank you for your contribution! :)

I'm not seeing one of my grid rows....(two WrapPanel ListBox's in a XAML Grid)

I have two ListBox's in a grid. The ListBox ItemsPanelTemplate is set to "WrapPanel." I'm expecting to see two separate rows but only see 1. I'm very new to xaml. Your help would be much appreciated. Thanks.
<navigation:Page
x:Class="BusMasterControl.Home"
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"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
xmlns:ds="clr-namespace:BusMasterControl.Web"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
xmlns:Controls="clr-namespace:BusMasterControl.Controls"
xmlns:converters="clr-namespace:BusMasterControl.Controls.Converters"
mc:Ignorable="d"
d:DesignWidth="640"
d:DesignHeight="480"
Style="{StaticResource PageStyle}">
<UserControl.Resources>
<converters:UriToMachineNameConverter x:Key="uriToMachineNameConverter" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" VerticalAlignment="Top" HorizontalAlignment="Left" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<riaControls:DomainDataSource Name="busMasterControlDomainDataSource2" LoadSize="500" QueryName="GetResourceByDomainAndUriFilter" AutoLoad="True">
<riaControls:DomainDataSource.DomainContext>
<ds:BusMasterControlDomainContext />
</riaControls:DomainDataSource.DomainContext>
<riaControls:DomainDataSource.QueryParameters>
<riaControls:Parameter ParameterName="domain" Value="//bus.mydomain.com" />
<riaControls:Parameter ParameterName="uriFilter" Value="red" />
</riaControls:DomainDataSource.QueryParameters>
<riaControls:DomainDataSource.SortDescriptors>
<riaControls:SortDescriptor PropertyPath="resourceId" Direction="Ascending" />
</riaControls:DomainDataSource.SortDescriptors>
</riaControls:DomainDataSource>
<riaControls:DomainDataSource Name="busMasterControlDomainDataSource" LoadSize="500" QueryName="GetResourceByDomainAndUriFilter" AutoLoad="True">
<riaControls:DomainDataSource.DomainContext>
<ds:BusMasterControlDomainContext />
</riaControls:DomainDataSource.DomainContext>
<riaControls:DomainDataSource.QueryParameters>
<riaControls:Parameter ParameterName="domain" Value="//bus.test.mydomain.com" />
<riaControls:Parameter ParameterName="uriFilter" Value="red" />
</riaControls:DomainDataSource.QueryParameters>
<riaControls:DomainDataSource.SortDescriptors>
<riaControls:SortDescriptor PropertyPath="resourceId" Direction="Ascending" />
</riaControls:DomainDataSource.SortDescriptors>
</riaControls:DomainDataSource>
<ListBox Name="machines2" BorderThickness="0" ItemsSource="{Binding Data, ElementName=busMasterControlDomainDataSource2}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Grid.RowSpan="1">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Controls:BusBench
Background="Gray"
BorderBrush="Black"
BorderThickness="1"
Caption="{Binding Path=uri, Converter={StaticResource uriToMachineNameConverter},FallbackValue=Collapsed}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Name="machines" BorderThickness="0" ItemsSource="{Binding Data, ElementName=busMasterControlDomainDataSource}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Grid.RowSpan="1">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Controls:BusBench
Background="Gray"
BorderBrush="Black"
BorderThickness="1"
Caption="{Binding Path=uri, Converter={StaticResource uriToMachineNameConverter},FallbackValue=Collapsed}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I think that you didn't specify that the first list box belongs to grid row 0 and the second to grid row 1. 0 is default for the Grid.Row attached property so you don't need to specify it for the first list box. But for the second you need to set it explicitly to 1:
<ListBox Name="machines" Grid.Row="1" ... >
...
</ListBox>

Categories