WrapPanel hides half of the last uiElement of each line - c#

Each UserControl I have in a stackPanel has a WrapPanel, and each UserControl in this WrapPanel is a word (so they have a different size from each other, depending on the length of the word), so that a sentence appears.
Here is an image to help you understand better:
In pink it is the UserControl "sentence" that contain each of them ONE wrapPanel
In Green it is all the UserControl "word" that have all different size according to the word length.
Here is the UserControl "word":
<UserControl x:Class="Coran_seul.UC.UserControl_WordInVerse"
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:Coran_seul.UC"
mc:Ignorable="d" Margin="5,0,5,0">
<StackPanel>
<TextBlock x:Name="textBlock_arabic" Text="ٱلْأَنفَالُ " FontSize="20" HorizontalAlignment="Center" FontFamily="Noto Naskh Arabic" MouseEnter="textBlock_arabic_MouseEnter" MouseLeave="textBlock_arabic_MouseLeave" Cursor="Hand" MouseDown="textBlock_arabic_MouseDown"/>
<TextBlock x:Name="textBlock_french" Text="Les surplus (de bénéfice)" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
And here is the UserControl "sentence" (with the wrapPanel I have a problem with)
<UserControl x:Name="userControl" x:Class="Coran_seul.UC.UserControl_VerseInSurah"
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:Coran_seul.UC"
mc:Ignorable="d" Margin="0,5,0,5"
>
<Border x:Name="Border_Verse" >
<StackPanel Orientation="Horizontal" x:Name="grid">
<Label x:Name="Label_VersetNum" Content="2" Height="{Binding ActualHeight, ElementName=WrapPanel_Mots, Mode=OneWay}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" VerticalAlignment="Stretch" HorizontalAlignment="Left" MouseDown="Label_VersetNum_MouseDown"/>
<WrapPanel x:Name="WrapPanel_Mots" Width="{Binding ActualWidth, ElementName=userControl, Mode=OneWay}" />
</StackPanel>
</Border>
The problem :
Now that I've entered the code, here's the problem I'm having with my wrapPanel:
The last word of each line does not wrap, even if it is larger than the space left (= it is therefore hidden/cut off at the edge of the wrapPanel)
Again I have a video to better explain the problem :
https://streamable.com/lpdf38
I don't know what it's due to and I'm desperately looking since yesterday not to have this problem anymore, knowing that the stackPanel containing all the userControls has a right margin of 20 so that it's not hidden behind the scrollbar.
Please help me to find a solution so that the word as long as it is hidden even by one pixel is wrapped to the next line.
Thank you,

The problem is that you bind the WrapPanel's Width to that of its UserControl parent without subtracting the Width of the Label element.
You should not need to bind any Width at all, when you use suitable Panel elements, like e.g. a Grid or a DockPanel instead of StackPanel.
<Border x:Name="Border_Verse">
<DockPanel>
<Label x:Name="Label_VersetNum" DockPanel.Dock="Left" .../>
<WrapPanel x:Name="WrapPanel_Mots">
</DockPanel>
</Border>
<Border x:Name="Border_Verse">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label x:Name="Label_VersetNum" Grid.Column="0" .../>
<WrapPanel x:Name="WrapPanel_Mots" Grid.Column="1">
</DockPanel>
</Border>
You may also consider to use an ItemsControl to display a sentence. It would use a WrapPanel as its ItemsPanel and the word UserControl in its ItemTemplate:
<ItemsControl ItemsSource="{Binding Words}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:UserControl_VerseInSurah/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
where Words is a collection of word data item objects in the view model, i.e. the object in the DataContext of the view. The two TextBlocks in the word UserControl would have their Text property bound to an appropriate property on the word data item class.
You may not even need the word UserControl at all, since you can simply move the elements from its XAML into the DataTemplate that is used as ItemTemplate.
See Data Templating Overview for details.

Related

WPF Dockpanel extending beyond width of parent

I am attempting to create a dock panel inside of a list view data template that takes up all of the available width of its parent tab control. I started by basing my XAML off of this question and thus came up with this:
<TabControl x:Name="tabControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TabItem Header="Clipboard Buffer">
<ListView Name="clipboardListView" ScrollViewer.CanContentScroll="True">
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="Black" CornerRadius="2,2,2,2">
<DockPanel Background="AliceBlue" Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}">
<TextBlock Text="{Binding Text}" DockPanel.Dock="Top" />
</DockPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</TabItem>
...
This almost works, however the dock panel always seems to be just a little bit wider than the space available in the tab control as you can see here.
I can use the scroll bar to move over and see the end of my dockpanel as one would expect...
However, if I attempt to re-size the form, the dockpanel remains slightly larger than the amount of space available in the tab control.
What am I missing here? How can I make my dock panel stay within the width of its parent tab control?
Try this:
<TabControl x:Name="tabControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TabItem Header="Clipboard Buffer">
<ListView Name="clipboardListView" ScrollViewer.CanContentScroll="True" HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="Black" CornerRadius="2,2,2,2" >
<DockPanel Background="AliceBlue" >
<TextBlock Text="asdfasdkfhkasdfkasdgfkjhdgfkuwegyfkwegfbkuweyfuksyadukfykweugbyfu" DockPanel.Dock="Top" />
</DockPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</TabItem>
</TabControl>
Note the HorizontalContentAlignment="Stretch" in the ListView
Your DockPanel is nested into a Border element:
<Border BorderThickness="2" BorderBrush="Black" CornerRadius="2,2,2,2">
<DockPanel Background="AliceBlue" Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}">
The Border occupies some width of its own - it has a border thickness of 2, so it is 4 pixels wider than its content.
The content of the Border is your DockPanel - and you have bound its width to the actual width of the enclosing TabControl. As a result, the aforementioned Border will always be a bit wider than the enclosing TabControl.
For a start, you could try and bind the width of the Border rather than that of the DockPanel. This should help to some extent, although I am not completely sure the TabControl doesn't require some extra inner space for its page contents.
It's most likely caused by this line in your DockPanel XAML:
Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}
Your setting the Width to the ActualWidth of the TabControl, without taking into account any Padding or Margin on the Children.
You need to check if the TabControl, TabItem or ListView have any padding or margins, then take this off too. (Don't forget the 2pixel border!)

The XAML code expect my Command in different VM

On the main window xaml, I have a Frame that will host 2 pages, like this
<Window x:Class="Monitor.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" FontSize="14">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Frame Content="Frame" Grid.Row="1" Source="/Monitor;component/Views/Pages/GroupPage.xaml"/>
</Grid>
</Window>
On the first page (GroupPage.xaml) xaml code is
<Page.DataContext>
<VM:GroupPageVM />
</Page.DataContext>
<Grid>
<ItemsControl x:Name="GroupsOfItemsControl" ItemsSource="{Binding GroupsOfItems}">
<ItemsControl.Template>
<ControlTemplate>
<WrapPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
FlowDirection="LeftToRight" IsItemsHost="true">
</WrapPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button HorizontalAlignment="Right" Margin="3"
Content="{Binding Name}"
Width="1.2in" Height=".75in" FontSize="14"
Command="{Binding Path=GroupSelectedCommand }"
CommandParameter= "{Binding}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The GroupPageVM is the VM for this page. GroupsOfItems is a collection of VM for groups. Each Group VM has a Name where I bind it for the content of a button that represent for that group. So on the screen, I will see a collection of buttons in a WrapPanel. Everything display correctly.
Now the issue come with the command that handle the click on the button, the code expect I put my Command (GroupSelectedCommand) and its execute function inside the VM of the Group, instead in the MainWindow VM.
Can someone explain why?
If it is how it suppose to work, how do I put the command in the MainWindow VM? because without that I cannot access to the Frame that host the pages or any NavigationService to go to page 2 by clicking on one of those buttons.
(My simple goal is clicking on a button that represent a group, and it will navigate to a page to display items in that group)
You have bound GroupsOfItems as the ItemsSource of the ItemsControl. Inside GroupsOfItems you have GroupVM objects. So each item inside the ItemsControl will be holding a GroupVM object. Also the DataTemplate defined will be representing the GroupVM object. Thus the Command will be expected inside GroupVM object.
To access parent DataContext, you can try giving ElementName as shown below,
Command="{Binding DataContext.GroupSelectedCommand, ElementName=GroupsOfItemsControl }"
In the above code the button will try to take the Command from the DataContext of ItemsControl which is the GroupPageVM

WPF: Synchronize width of all items in ItemsControl

Is it possible to adjust the width of all TextBlocks in the WrapPanel to the size of the largest TextBlock within the WrapPanel? The end result should be that the width of the control containing "Some data" has the same width as the control containing "Even more data than before." I have attached to my initial code as a starting point. I have used strings as an example, but the collection data and template could be anything, so I can't rely on the length of the string.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Collections:ArrayList x:Key="data">
<System:String>Some data</System:String>
<System:String>Some more data</System:String>
<System:String>Even more data than before</System:String>
</Collections:ArrayList>
</Window.Resources>
<ItemsControl ItemsSource="{StaticResource data}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="5" BorderThickness="1" BorderBrush="Black">
<TextBlock Text="{Binding}"></TextBlock>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
And an image of the desired output:
Use a shared size grid:
<ItemsControl ItemsSource="{StaticResource data}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="ColumnSize" />
</Grid.ColumnDefinitions>
<Border Margin="5" BorderThickness="1" BorderBrush="Black">
<TextBlock Text="{Binding}"></TextBlock>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
All columns are guaranteed to be the same width as they share a size group. As they are sized auto, they will also size to the largest instance of any of the grid's content.
You have to create your own custom version of a wrappanel that is derived from wrappanel to achieve what you want.
Normally for a custom panel you have to override following 2 methods :
MeasureOverride
ArrangeOverride
In your case you need measueoverride where you will iterate over the elements and find out which one is the biggest and then use that same size for all elements in arrangeoverride.
Some more info on how to create a custom panel :
http://www.wpftutorial.net/CustomLayoutPanel.html
You can add HorizontalContentAlignment=Stretch and Add UniformGrid in itemsPanel.
<Window.Resources>
<Collections:ArrayList x:Key="data">
<System:String>Some data</System:String>
<System:String>Some more data</System:String>
<System:String>Even more data than before</System:String>
</Collections:ArrayList>
</Window.Resources>
<ListBox ItemsSource="{StaticResource data}" HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True"></UniformGrid>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5" BorderThickness="1" BorderBrush="Black" >
<TextBlock Text="{Binding}" Width="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=Width}"></TextBlock>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is an alternative to Philip Stuyck's custom panel suggestion that you may find more straightforward for your particular scenario. (It is a bit of a hack admittedly.)
You can calculate the length of a string using the FormattedText class. So you could iterate over your strings and calculate the maximum length (this also assumes that you know the font family and size). Then just bind the width of your text blocks to the maximum width. I would store the width value in a single property at the parent level, then using a RelativeSource binding:
<TextBlock Text="{Binding}"
Width="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl},
Path=MaximumWidth}}"
/>
(One drawback of this approach is that if the items collection changes, you'll have to recalculate MaximumWidth.)

ScrollViewer changes Size of Content(ItemControl) instead of showing a scrollbar

I have a WPF UserControl containing an ItemsControl with a Horizontal StackPanel as ItemsPanel (Basically some kind of the WinRT Hub Control). And the content extends the Size of the UserControl
If I try to add a ScrollViewer around my ItemsControl, the ScrollViewer shrinks the ItemsControl so all Items fit into the UserControl bounds.
Thats somehow exactly the opposite of what i expected can anybody tell me why the scrollviewer behaves this way?
Here is my UserControl:
<UserControl x:Class="ContentModule.ContentView"
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"
mc:Ignorable="d"
xmlns:contentModule="clr-namespace:ContentModule"
xmlns:regions="http://www.codeplex.com/CompositeWPF"
xmlns:statics="clr-namespace:Infrastructure.Statics;assembly=Infrastructure"
d:DesignHeight="300" d:DesignWidth="300" d:DataContext="{d:DesignInstance contentModule:ContentViewModel}"
VerticalAlignment="Top" HorizontalAlignment="Left">
<ItemsControl regions:RegionManager.RegionName="{x:Static statics:ContentRegions.ContentCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</UserControl>
The Items are injected via Prism RegionManager.
EDIT1:
The UserControl is getting Injected into my MainForm. It is assigned to the ContrentControl => ShellRegions.Content (the third one)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" cal:RegionManager.RegionName="{x:Static statics:ShellRegions.MainMenu}" />
<ItemsControl Grid.Row="1" cal:RegionManager.RegionName="{x:Static statics:ShellRegions.NavigationBar}" />
<ContentControl Grid.Row="2" cal:RegionManager.RegionName="{x:Static statics:ShellRegions.Content}" />
</Grid>
EDIT 2:
Some more details.
The ItemsControl looks like : (Gray is the UserControl, Orange are the items in the ItemsControl)
The Content scales when changing the bounds of the Form/UserControl as expected but the ItemsControl does not show a ScrollBar.
If i add a ScrollViewer the Content does not scale with changing bounds anymore and is Vertically scrollable instead of horizontal, or it does change the width of the items to fit the UserControll depending on the ScrollBar properties.
But i can't get it working to keep the scaling and add a scroll bar to the bottom of the ItemsControl.
You can try doing following:
Wrap your control in a ScrollViewer and set CanContentScrol property to TRUE.
Futhermore change the Panel of your ItemsControl to VirtualizingStackPanel.
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Tell us whether it worked or what kind of new issues those changes will bring up.
After a day full of research i found a working solution:
<ItemsControl regions:RegionManager.RegionName="{x:Static statics:ContentRegions.ContentCollection}">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

How to show a scheme in a document viewer?

I'm trying to show in screen something like a grades scheme.
The first solution which I did was drawing some rows definitions and columns in a Grid, accumulating some stacks panel (inside there are some rectangles) and setting in the row. But I don't like that idea.
I was using several bucles and I'd like to show that in a document viewer. Althoug I can imagine I may create a scheme o something easier.
UPDATE 1:
At this moment, I've got a class which contains as properties:
SubjectName: string
IsCorrect: bool
And others properties but they don't matters right now. Then I have all in a List. I don't know if it's useful to user a LINQ function to group by SubjectName and get its average score:
Classify elements from a list to another classification with average
But I really have interest in the control.
Personally I would (assuming the data is in a database of some kind) host those rows in an ItemsControl of some kind. The ItemsControl would be bound to a collection of the Items.
If I had a little leeway, I would make it a plain old list box and customize the styling of the items). The bar chart portion at the end should be fairly easy to accomplish by having a rectangle whose width is bound to the grade property or whatever which is applied as a percentage of the width of the container it is in.
So I would have a Subject Class (which is created from the data source) with several properties :
Name (e.g. "Times Tables") Represented by a TextBlock
Grade (e.g. 52% ) Represented by a rectangle bound to a property which multiplies that value by the width of the grid in which the rectangle resides
Action (e.g. "Repeat") represented by another textblock.
I will post an example in a little while.
Example
OK so The window has a very simple layout, a Grid with a listbox in it. The listbox is bound to a design time datasource that I set up in Expression Blend.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewMFagic"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
x:Class="MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" MouseRightButtonDown="ShowContext" DataContext="{Binding Source={StaticResource dsSubjects}}">
<ListBox ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" HorizontalContentAlignment="Stretch"/>
</Grid>
</Window>
The ItemTemplate I put in the App.Xaml resource Library as I like keeping my Window and UserControl xaml files nice and clean and using resource dictionaries as much as possible to keep stuff out of the way. Anyhow, below is the ItemTemplate.
<DataTemplate x:Key="ItemTemplate">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.029*"/>
<ColumnDefinition Width="0.67*"/>
<ColumnDefinition Width="0.168*"/>
<ColumnDefinition Width="0.133*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding SubjectName}" VerticalAlignment="Bottom" d:LayoutOverrides="Width" Grid.ColumnSpan="1" Margin="0" Grid.Column="1"/>
<TextBlock Text="{Binding Action}" VerticalAlignment="Top" d:LayoutOverrides="Width, GridBox" Grid.ColumnSpan="1" Grid.Column="3" Margin="0"/>
<Border Grid.ColumnSpan="1" Grid.Column="2" Margin="0" d:LayoutOverrides="Height" Background="#A3000000" CornerRadius="5" Width="{Binding PercentCorrect}" HorizontalAlignment="Left" >
<TextBlock Text="{Binding PercentCorrect}" HorizontalAlignment="Center"/>
</Border>
<TextBlock TextWrapping="Wrap" Text="{Binding Number}" d:LayoutOverrides="Width, Height"/>
</Grid>
</DataTemplate>
And the finished product looks like this:
Now I cheated a little here. I bound the width of the border element for the grade directly to the percent. Given more time to mess with this, I probably would have made a ViewModel and had values for both the shaded part and the unshaded part which added up to 100%. Then bound column widths of a grid that I would put the border in to those values to have true percentage. Regardless, there is a starting point.

Categories