WrapPanel not wrapping content - wpf

I have the following xaml code in a user control.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" MinWidth="50"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="scEmails">
<ItemsControl Focusable="False" ItemTemplate="{StaticResource UserDataTemplate}">
<ItemsControl.Items>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>12eee3</system:String>
<system:String>123eee</system:String>
<system:String>123fefef</system:String>
</ItemsControl.Items>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<TextBox Grid.Column="1" TextWrapping="NoWrap" Margin="0,0,0,0" VerticalAlignment="Center" PreviewKeyDown="txtAuto_PreviewKeyDown" MinWidth="50" />
</Grid>
Here I have a set of items to be shown in the left of a text box. The rendering requirement is:
If there are no or little items, let the items occupy as many as space they needed.
If there are too many, the items should wrap to next line and expand the Height of the control, to ensure the text box get 50 or more pixels width, unless this is impossible (i.e. there is a single item that will use too much space)
If there are too many lines (i.e. exceed the limit set by MaxHeight property), show the vertical scroll bar.
In any cases, the text box shall be given all the space left (in the right hand side!).
I use a ScrollViewer to fulfill #3, and use a WrapPanel to fulfill #2. But the code above does not give the desired result. In design mode it looks like (the item template is a TextBlock inside a Border, should'nt matter here)
It is not fulfill the requirement because it is not wrapping.
What can I do to fix?
Update
If I apply the code in the answer by Raviraj Palvankar and removed all items, the layout in design mode is the following
However, the desired output (according to requirement term #4) is
Where my original code does properly (although fails other requirements)

Here is your code without the ItemTemplate and hence looking like that. I added more strings to show that it does wrap.
<Grid Grid.Column="1" Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" MinWidth="50"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="scEmails">
<ItemsControl Focusable="False" >
<ItemsControl.Items>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>12eee3</system:String>
<system:String>123eee</system:String>
<system:String>123fefef</system:String>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>123</system:String>
<system:String>12eee3</system:String>
<system:String>123eee</system:String>
<system:String>123fefef</system:String>
</ItemsControl.Items>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<TextBox Grid.Column="1" TextWrapping="NoWrap" Margin="0,0,0,0" VerticalAlignment="Center" PreviewKeyDown="txtAuto_PreviewKeyDown" MinWidth="50" />

Related

Reproduce CSS property in StackPanels : Space Between

Is it possible in XAML to automatically separate elements in a StackPanel no matter what the size of the window is?
In CSS with the property : space between you can send elements to both sides of the parent. This is what I want to reproduce. Is it possible ?
A StackPanel does not work this way , see the documentation. It does not have a notion of the available space or remaining space to divide, it just stacks elements in one direction.
Arranges child elements into a single line that can be oriented horizontally or vertically.
What you can do instead is use a DockPanel. It allows stacking controls to either side of the panel and by default the last element added to it fills the remaining space. The Border here serves as a dummy element to take the remaining space. Usually, you would put a control there that is actually used. Please be aware that this example only reproduces what you explicitly asked for, the panel allows for much more complex layouts. See remarks from the documentation.
<DockPanel>
<Rectangle DockPanel.Dock="Left" Fill="Black" Width="100" Height="100"/>
<Rectangle DockPanel.Dock="Right" Fill="Black" Width="100" Height="100"/>
<Border/>
</DockPanel>
An alternative is to use a Grid with three columns, where the left and right columns use Auto as Width so the contained controls only take up as much space as they need, while the center column has its Width set to *, which will make it fit the remaining space.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Fill="Black" Width="100" Height="100"/>
<Rectangle Grid.Column="2" Fill="Black" Width="100" Height="100"/>
</Grid>
The relevant portion on size attributes for Grid:
Columns and rows that are defined within a Grid can take advantage of Star sizing to distribute remaining space proportionally. When Star is selected as the height or width of a row or column, that column or row receives a weighted proportion of the remaining available space. This is in contrast to Auto, which distributes space evenly based on the size of the content that is within a column or row.
Update for your list example. You can define a data template for the items.
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="25,60,25,25" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border Padding="0,25" BorderThickness="0 0 0 1" BorderBrush="#3D3C44">
<Grid Width="378" Background="black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="hello" Foreground="white"/>
<Border Grid.Column="1" x:Name="PlaceholderForYourContentAndBindings" Background="Red"/>
<theme:OnOff Grid.Column="2" Height="21" Width="38" HorizontalAlignment="Right" Grid.Column="1" Toggle="{Binding status , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MouseLeftButtonDown="OnOff_MouseLeftButtonDown" />
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want two fixed columns in the ListView, use a UniformGrid instead of a WrapPanel and remove the fixed sizes. With an ItemContainerStyle the items will then scale with the window.
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding StringItems}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" Margin="25,60,25,25" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Border Padding="0,25" BorderThickness="0 0 0 1" BorderBrush="#3D3C44">
<Grid Background="black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="hello" Foreground="white"/>
<Border Grid.Column="1" x:Name="PlaceholderForYourContentAndBindings" Background="Red"/>
<theme:OnOff Grid.Column="2" Height="21" Width="38" HorizontalAlignment="Right" Grid.Column="1" Toggle="{Binding status , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MouseLeftButtonDown="OnOff_MouseLeftButtonDown" />
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Itemscontrol Scrollviewer not working with data

I have searched all over, and even though everyone seems to be having this problem, I can't find the fix for my specific problem..
Here's the problem.
I want to make a custom Calendar Control. In order to do this I am filling an ItemsControl with TextBlocks, and then putting a scrollviewer around it.
But for some reason the scrollviewer scrollbar seems disabled, and it doesn't seem to recognize that it's filled with data.
Here's my Code
<Grid>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding CalendarDates}" Height="75">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Calender">
<TextBlock Name="CalendarDate" FontSize="12" Text="{Binding}" TextAlignment="Right" VerticalAlignment="Top" Height="Auto"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" Columns="7"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
And here's my MainWindow.xaml where I initialize it
<Grid>
<!--Row Definitins -->
<Grid.RowDefinitions>
<RowDefinition Height = "Auto"/>
<RowDefinition Height = "*"/>
<RowDefinition Height = "Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="10*"/>
</Grid.ColumnDefinitions>
<localControl:Calender Grid.Column="1" Grid.Row="1"/>
</Grid>
The code fills the scrollviewer just fine, but like I said above the scrollbar seems disabled, and even when I hard code the size it still doesn't work!
Also I have already tried to set the SccrollViewer.VerticalScrollBar= Visible, and the height of the scrollviewer, as well as over a dozen of the "fixes" here on Stack Overflow, but none of them work in my case
I found the answer... The ItemsControl template itself does not allow for a scrollviewer.
I found the answer in this magazine on page 38.
https://dncmagazine.blob.core.windows.net/edition20/DNCMag-Issue20.pdf
We need to modify the ItemsControl template as follows:
<ItemsControl.Template>
<ControlTemplate TargetType= "ItemsControl">
<ScrollViewer>
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>

How can I apply DataTemplate to each item in a Collection into the cells of a grid

I have a Grid defined as such
<Grid DockPanel.Dock="Top" x:Name="grdFilter" Margin="40 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.ColumnSpan="3" Grid.Row="5" Orientation="Horizontal"
Margin="5" HorizontalAlignment="Center">
<Button Content="Apply"
Margin="0 0 2 0"
Style="{StaticResource MyButtonStyle}"
Command="{Binding ApplyFilterCommand}"/>
<Button x:Name="btnCancelFilter"
Margin="2 0 0 0"
Content="Cancel"
Style="{StaticResource MyButtonStyle}"
Click="btnCancelFilter_Click" />
</StackPanel>
</Grid>
And an ObservableCollection<string> in my ViewModel that defines a list of strings to filter some data on. I want to present each item in the ObservableCollection in a separate cell in the Grid defined above using a checkbox and a label. I realize there is a finite number of possible cells; I'm not worried about that right now.
If I hard-code the items, I'm stuck repeating the definitions for the checkbox and label for every cell of the grid
<DockPanel Grid.Column="0" Grid.Row="0">
<CheckBox x:Name="filterCheckbox"
Command="SomeFilterCommand">
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</CheckBox.LayoutTransform>
</CheckBox>
<Label Content="Filter 1" />
</DockPanel>
<DockPanel Grid.Column="1" Grid.Row="0">
<CheckBox x:Name="filterCheckbox"
Command="SomeFilterCommand">
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</CheckBox.LayoutTransform>
</CheckBox>
<Label Content="Filter 2" />
</DockPanel>
etc... I thought maybe I could create a DataTemplate and somehow bash that into the Grid with some voodoo but I don't know how to do it or if it's even possible.
Here's my DataTemplate
<DataTemplate x:Key="FilterTemplate">
<DockPanel>
<CheckBox x:Name="filterCheckbox"
Tag="{Binding Mode=OneWay}"
Command="SomeFilterCommandFromBindings">
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</CheckBox.LayoutTransform>
</CheckBox>
<Label Content="{Binding Mode=OneWay}" />
</DockPanel>
</DataTemplate>
Is there any other way to load a collection of items into a grid cell-by-cell with a template? Or another control that I can use?
A grid can't be used as an ItemsPanel Template but a Uniform Grid can, which is not as flexible but usually does the job in these kinds of situations
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<!-- Template here -->
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can also use a ListBox or ListView, depending on the functionality required; ItemsControl doesn't have a scrollviewer or SelectedItem for example, but if you just need to display data then ItemxControl is the way to go.

WrapPanel Horizontal orientation doesn't work

With new version of kinect not exist the old kinectScrollviewer so i have used a ScrollViewer with a listView of images. The problem is the unscrollable when hidden ScrollbarVisibility or with horizontal scroll and if i use a SelectionChanged it works fine with mouse, but if i use a hand gesture after first click the selection area not disappear and so i don't select the elements
I would scroll only horizontal (so i have disabled vertical), but also with your code doesn't scroll with gesture. Also the click doesn't works.
If i use orientation="Vertical" it's scroll vertical (although in the example scroll horizontal using this setting), but if i use orientation="Horizontal" it doesn't works :(
<k:KinectRegion x:Name="ChoiceExercise" Background="Black" >
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<k:KinectUserViewer Grid.Row="0" Height="100"/>
<ContentControl Grid.Row="1" x:Name="navigationRegion">
<Grid x:Name="kinectGrid">
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" k:KinectRegion.IsScrollInertiaEnabled="True">
<ListView Grid.Row="0" x:Name="listViewExercise" SelectionChanged="listViewExercise_SelectionChanged" BorderThickness="0" Background="Black" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Center" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</ScrollViewer>
</Grid>
</ContentControl>
</Grid>
</DockPanel>
</k:KinectRegion>
ListView already contains ScrollViewer as part of default template and it's this behaviour that you need to disable by setting attached ScrollViewer.VerticalScrollBarVisibility property to Disabled
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<k:KinectUserViewer Grid.Row="0" Height="100"/>
<ContentControl Grid.Row="1" x:Name="navigationRegion">
<Grid x:Name="kinectGrid">
<ListView
Grid.Row="0"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
x:Name="listViewExercise"
SelectionChanged="listViewExercise_SelectionChanged"
BorderThickness="0"
Background="Black" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Center" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
</ContentControl>
</Grid>
Also WrapPanel with horizontal orientation is meant to stack items horizontally until item cannot fit then move to the next row. Since you you want to scroll horizontally I think horizontal StackPanel would suit you better.
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>

TextBlock Wrapping Property Doesn't Work

In the following XAML, I am trying to wrap the TextBlock which binds to "PortfolioCodes" and "CommentaryText" but it seems that "Wrapping" doesn't work for TextBlock. I tried every possible suggestion I could find on this web site but all in vain. Can someone please help.
<Grid>
<ListBox ItemsSource="{Binding Path=Summaries}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" BorderBrush="LightGray" BorderThickness="1" Padding="4" Margin="4">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="15"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">No Of Security</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding Path=PortfolioSecurityCount}"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1">Portfolio Code(s)</TextBlock>
<Grid Grid.Column="2" Grid.Row="1" >
<TextBlock TextWrapping="Wrap" Text="{Binding Path=PortfolioCodes}"></TextBlock>
</Grid>
<TextBlock Grid.Column="0" Grid.Row="2">Commentary Text</TextBlock>
<Grid Grid.Column="2" Grid.Row="2" >
<TextBlock Grid.Column="2" Grid.Row="2" TextWrapping="Wrap" Text="{Binding Path=CommentaryText}"></TextBlock>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Based on Guge response, I have changed xaml as below and now its working.
<Grid x:Name="LayoutRoot">
<ListBox x:Name="SummaryListBox" ItemsSource="{Binding Path=Summaries}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" BorderBrush="LightGray" BorderThickness="1" Padding="4" Margin="4">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="15"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">No Of Security</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding Path=PortfolioSecurityCount}"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1">Portfolio Code(s)</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=PortfolioCodes}" ></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="2">Commentary Text</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="2" TextWrapping="Wrap" Text="{Binding Path=CommentaryText}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Change the width of your third ColumnDefinition from "Auto" to "*", that way it only takes up whatever is left of your horizontal space.
To try to explain this:
Screen area in WPF is distributed in a two pass algorithm. First each visual element asks each child how much space it needs, with an indication of how much is available. These children do the same for their children.
Then each visual element tells each child how much they are actually going to get. These children, again, do the same for their children.
Your code failed to do what you wanted because the Grid in the DataTemplate told its third column children they could have all the horizontal space they wanted ("auto") in the first run. Those textboxes then thought that they wouldn't have to wrap. So they just reported back their desired width, and one line worth of height.
In the second run the Grid found that "auto" turned out to be a little less than what those children wanted. And the Grid still only gave them one line worth of height, so wrapping was out of the question. The children then had no other option left to them but to truncate the text.
When the third column width is set to "*", the grid will tell the children in the that column exactly how many horizontal pixels are left after the first column got their "auto" and the second column got their 15. Now the textboxes can figure out that they may want to wrap, and they report back "Ok, Dad, I'll make do with those measly horizontal pixels, but at least give me what I want in verticals". There is no limit to the vertical space, so they get what they need to present all their glorious content.
Try to give width to your TextBlock, by default TextBlock takes all available space and doesn't wrap text

Resources