RichTextbox, FlowDocument unable to left align - wpf

I have a RichTextBox and a FlowDocument like so in my ResourceDictionary (no-code behind), and my original window loads with a relatively wider size.
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="55"/>
</Grid.ColumnDefinitions>
<FlowDocumentScrollViewer DockPanel.Dock="Left" Padding="0" Grid.Column="0"
HorizontalAlignment="Left" VerticalContentAlignment="Center" BorderBrush="LightGreen" BorderThickness="2">
<FlowDocument Name="Msg" TextOptions.TextFormattingMode="Display">
<FlowDocument.Tag>
<MultiBinding Converter="{StaticResource MyConv}">
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding Path="Speed"/>
</MultiBinding>
</FlowDocument.Tag>
</FlowDocument>
</FlowDocumentScrollViewer>
<Button Content="Button" Margin="1,0,4,0" MinWidth="25" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
Here we can see that the FlowDocumentScrollViewer's default size/border extends till the full (grid) width irrespective of its inner content, and because of which the Button goes out of visual scope.
I also tried RichTextbox instead of FlowDocumentScrollViewer but its the same problem.
As a work-around I can do Margin='-20,0,0,0' on the Button but that's not a good idea as that will force the button over the text when window is made much smaller.
Please help!

Related

JAWS screen reader reads off all TextBlocks in WPF application when first cell is selected

We are building a 508 compliant WPF application and testing it with JAWS 18, and one thing we found was that TextBlocks in every cell of the same Grid are being read off back to back when the first item is selected. Also, it does not work if you are using 'shift' + 'tab' to move selection backwards, only when you use 'tab' alone and move forward. I tested if it was somehow a property of Grids by making a sample application with a Grid and TextBlocks in the columns and rows and selecting the upper left cell, and that did not make JAWS read all items on the Grid. So I don't think it is a general property of the Grid in WPF. I would like to disable this feature.
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/> //More of these
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0"
Grid.Column="0"
Style="{StaticResource FacilityDetailsStackPanelStyle}"
Visibility="{Binding SelectedContact.FirstName, Converter={StaticResource StringToVisibilityConverter}}">
<TextBlock Text="{Binding Source={StaticResource ApplicationSettings}, Path=ContactNameLabelText}"/>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="SelectedContact.FirstName"/>
<Binding Path="SelectedContact.LastName"/>
</MultiBinding>
</TextBlock.Text>
<AutomationProperties.Name>
<MultiBinding StringFormat="{}{0} {1} {2}">
<Binding Source="{StaticResource ApplicationSettings}" Path="ContactNameText"/>
<Binding Path="SelectedContact.FirstName"/>
<Binding Path="SelectedContact.LastName"/>
</MultiBinding>
</AutomationProperties.Name>
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="1"
Grid.Column="0"
Style="{StaticResource FacilityDetailsStackPanelStyle}"
Visibility="{Binding Path=SelectedContact.JobTitle, Converter={StaticResource StringToVisibilityConverter}}">
<TextBlock Text="{Binding Source={StaticResource ApplicationSettings}, Path=ContactJobTitleLabelText}"/>
<TextBlock Text="{Binding SelectedContact.JobTitle}">
<AutomationProperties.Name>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Source="{StaticResource ApplicationSettings}" Path="ContactJobTitleText"/>
<Binding Path="SelectedContact.JobTitle"/>
</MultiBinding>
</AutomationProperties.Name>
</TextBlock>
</StackPanel>
<Grid/> //After all the same type of StackPanel/TextBlock structure.
<ScrollViewer/>
SelectedContact is a Contact business class that is selected by binding to SelectedItem property on a DataGrid where the ItemsSource is a list of Contact objects.
The grid was wrapped in a ScrollViewer, but I commented this out and there was no changes to the function. edit: there was no changes to function commenting out ScrollViewer, but adding AutomationProperties.Name to said ScrollViewer is what solved the problem.
We came to a solution by adding the AutomationProperties.Nameproperty to the ScrollViewer that wraps the Grid. When we enter into the ScrollViewer it now reads out the text we bound that to.

Fit a Textbox and a button in DataGrid Cell

I am trying to fit Textblock and a button in DataGrid Cell. The Textblock holds a portion of my text and when I click the button a dialog is display. I general my code looks like the one below
<DataGridTemplateColumn Header="Message" Width="Auto" MinWidth="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientetion="Horrizontal">
<TextBlock MinWidth="200" TextTrimming="CharacterEllipsis" Text="{Binding Text}" />
<Button DockPanel.Dock="Right" Width="90" Margin="1" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I want the Button to be always at righter side of the cell and its width to be fixed. The TextBlock needs to be variable, for example when I resize the window, and so the DataGrid, the TextBlock should stretch also.
The problem is that, I can not achieve this behaviour / view. The TextBlock varies on each DataGrid line and in some case the button is not at the righter of the cell.
I tried to change the StackPanel to Grid or DockPanel but still I can not get the desirable result.
Using Grid
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="100" Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" TextTrimming="CharacterEllipsis" Text="{Binding Text}" />
<Button Grid.Column="1" Margin="1" />
</Grid>
Any thoughts to share?
StackPanel doesn't really have the concept of aligning to the right. It stacks the elements as close as it can. You can get around this in different ways but in this case, use a DockPanel instead:
<DockPanel>
<Button DockPanel.Dock="Right" Width="90" Margin="1" />
<TextBlock MinWidth="200" TextTrimming="CharacterEllipsis" Text="{Binding Text}" />
</DockPanel>
Note that I moved the TextBlock to be the last child element of the DockPanel. DockPanel, after laying out the other child elements, allocates the remaining space to the last child element (unless you specify LastChildFill=false). In this case, we want the TextBlock to take up the remaining space.
UPDATE: based on the comments above, in addition to changing the panel type to a DockPanel (or Grid), you can use DataGridTemplateColumn.Width to a fixed value instead of Auto. This would make the column load with the specified with but the user can still modify the column with if they want to:
<DataGridTemplateColumn Header="Message" Width="60" MinWidth="60">
I'd set a static value to the DataGridTemplateColumn.Width--it can help with rendering performance. Set the size on your buttons too, so it doesn't size to fit text.
This works for me (I used the border for visualization purposes):
<Window ...
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Model}"
x:Key="VmItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Grid.Column="0" TextTrimming="CharacterEllipsis" Text="{Binding Original}" />
</Border>
<Button Grid.Column="1" Margin="1" Content="{Binding Encoded}" MinWidth="90" MaxWidth="90"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Message" CellTemplate="{StaticResource VmItem}" Width="300" MinWidth="100"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Proof:

TextBox in UserControl doesn't take all the space

I have a WPF listbox with custom items. Each item is a user control consisting of a grid with two textboxes. I want that the right one takes all the space to fill the ListBoxItem. But all I get working is that the item itself takes the whole space but the textbox takes the size of its content.
So this is the listbox:
<ListBox x:Name="leftListBox" Grid.Column="0" Margin="10"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<local:CustomLine />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the user control:
<UserControl x:Class="SharpComparer.CustomLine"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="30">
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox x:Name="NumberColumn" x:FieldModifier="public"
Text="{Binding LineNumber}"
Grid.Column="0" HorizontalAlignment="Right" />
<TextBox x:Name="TextColumn" x:FieldModifier="public"
Text="{Binding Text}"
Grid.Column="1" HorizontalAlignment="Left" />
</Grid>
</UserControl>
What I have already tried after some research at some MSDN posts and around here on Stack Overflow: Setting the HorizontalContentAlignment to Stretch for Listbox.ItemContainerStyle. I used some borders to find the piece which makes problems. The ListBoxItems seem to take the whole width, the usercontrol and its grid, too. The textbox does not take all the space although I thought that Width="*" inside the grid's ColumnDefinition would do that.
Another idea was to bind the textbox width to its parent size, but then it also takes the space of the left textbox (which makes sense, because it gets the whole width) and subtracting this width doesn't seem to work.
What am I doing wrong?
You have to change your UserControl code from this:
<TextBox x:Name="TextColumn" x:FieldModifier="public"
Text="{Binding Text}"
Grid.Column="1" HorizontalAlignment="Left" />
to this:
<TextBox x:Name="TextColumn" x:FieldModifier="public"
Text="{Binding Text}"
Grid.Column="1" HorizontalAlignment="Stretch" />

WPF Validation Control Template overlapping

I've got a user control with a control template to show validation errors, validation template:
<ControlTemplate x:Key="TextBoxPropertyValidationTemplate">
<StackPanel>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="MyAdorner" />
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" MaxHeight="16" MaxWidth="16"
Source="{Binding Source={StaticResource ValidationIcon}, Converter={StaticResource UriConverter}}"
Margin="1" RenderOptions.BitmapScalingMode="HighQuality"
VerticalAlignment="Center" HorizontalAlignment="Center" />
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left"
Text="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
TextWrapping="Wrap" Grid.Column="1" FontSize="10" Foreground="Red" />
</Grid>
</StackPanel>
</ControlTemplate>
And I can't seem to get around a rather irritating problem which looks like this:
I've been trying to play around with margins on the user control and on the template also some Height=Auto etc but all these don't really help. Any ideas anyone?
If that helps the main user control (which nests the ones with validation) is in a TabItem with a AdornerDecorator.
Any help appreciated.
I'd say this is because your error message is on the AdornerLayer, which doesn't participate in the same layout as your control. MSDN says "rendering of an adorner is independent from rendering of the UIElement that the adorner is bound to." and that is why the message is just put on top of everything.
You could put the error text into the original template, hide it based on Validation.HasError and include it in the layout process that way.
But changing the layout of the control might not be the best way to go if a validation error occurs. You might consider providing additional information in a ToolTip.
Alternately, instead of using ControlTemplate, you could put the error message TextBlock beside the TextBox, and set its Text property binding the TextBox's ErrorContent.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="txtName" Grid.Row="0">
<TextBox.Text>
<Binding Path="Name" NotifyOnValidationError="True" ValidatesOnExceptions="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<common:RequiredFieldValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Grid.Row="1" Text="{Binding ElementName=txtName,Path=(Validation.Errors)[0].ErrorContent}"
Visibility="{Binding ElementName=txtName,Path=Validation.HasError,Converter=...}" />
</Grid>

How can I make a column in a listbox in WPF the same width for all items?

I have a ListBox with an ItemTemplate consisting of a TextBlock and a ComboBox. The problem is that the width of the text inside the TextBlock is not the same for each item and the ComboBox controls are not aligned.
How can I set the TextBlock in the template so all items are the same width, that is the one of the widest?
Here is my xaml:
<ListBox MinHeight="100" ItemsSource="{Binding Trainees}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Grid.Column="0">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1}">
<Binding Path="LastName" />
<Binding Path="FirstName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<ComboBox HorizontalAlignment="Left" Grid.Column="1"
ItemsSource="{Binding Source={StaticResource Functions}}" SelectedValue="{Binding Path=Function}"
MinWidth="100" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can use the IsSharedSizeScope attached property. In your template definition, attach a "shared size group" to each column, like this:
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="col1" />
<ColumnDefinition SharedSizeGroup="col2" />
</Grid.ColumnDefinitions>
... then define your ListBox as a shared size scope so it knows to size each "size group" the same way:
<ListBox Grid.IsSharedSizeScope="True">...</ListBox>

Resources