ListBox.ItemTemplate with long text does not match ListBox width - c#

I'm trying to put a StackPanel inside a ListBox and this inside a Grid that is a part of a UserControl.
So far I have come up with the following XAML but I cannot fix the width. When you take a look at the screenshot you'll see that the StackPanel is few pixels too wide. I tried HorizontalAlignment=Stretch on every element but on the Width={Binding...} manages to limt the width, although not perfectly. What do I miss?
<UserControl x:Class="Reusable.Apps.ExceptionControl"
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:Reusable.Apps"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Width="{Binding (Window.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<UserControl.Resources>
<local:ExceptionVisualizerData x:Key="DesignViewModel" />
<Style TargetType="TextBlock" x:Key="NameStyle">
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontFamily" Value="Consolas"/>
<Setter Property="Foreground" Value="DarkMagenta"/>
</Style>
<Style TargetType="TextBlock" x:Key="MessageStyle">
<Setter Property="FontSize" Value="16"></Setter>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</UserControl.Resources>
<Grid Width="{Binding (UserControl.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListBox ItemsSource="{Binding Exceptions}" d:DataContext="{Binding Source={StaticResource DesignViewModel}}" Width="{Binding (Grid.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="{Binding (ListBox.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}">
<TextBlock Text="{Binding Name}" Style="{StaticResource NameStyle}" />
<TextBlock Text="{Binding Message}" Style="{StaticResource MessageStyle}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

It sounds like you should get rid of all the width bindings and just put a margin around your StackPanel, e.g:
<StackPanel Margin="10" >
--- UPDATED --
Frustrating is the right word. Okay, here's the solution.
(1) Replace StackPanel with Grid.
(2) In the ListBox, set ScrollViewer.HorizontalScrollBarVisibility to "Disabled"
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<Grid Margin="10" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Title" Style="{StaticResource NameStyle}" />
<TextBlock Grid.Row="1" TextWrapping="Wrap" Text="lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text " Style="{StaticResource MessageStyle}" />
</Grid>
</ListBox>
This should do the trick.

I finally got it! I found a similar question and adopted its answer to my needs:
<Grid >
<ListBox ItemsSource="{Binding Exceptions}" d:DataContext="{Binding Source={StaticResource DesignViewModel}}" Margin="30" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Width" Value="{Binding (Grid.ActualWidth), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" Style="{StaticResource NameStyle}" />
<TextBlock Text="{Binding Message}" Style="{StaticResource MessageStyle}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
The trick was to bind the Width of the ListBoxItem to the parent. Holy cow! This was tricky. Now it also nicely scales when resizing the window.

Instead of referring to ListBox, refer width of ListBoxItem
<StackPanel Width="{Binding (ListBoxItem.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">

The reason for the overlap is due to the default template for a ListBoxItem, which has Padding set to "4,1" and a BorderThickness of "1" on both itself and the inner Border object. You can use a tool such as WPF Snoop to see this.
The solution is to bind to the ActualWidth of the next level of container, in this case the ContentPresenter.
<ListBox ItemsSource="{Binding DataItems}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Yellow"
Width="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContentPresenter}},
Path=ActualWidth}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Id}" />
<TextBlock Grid.Row="1" Text="{Binding Description}" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note the properties set on the ListBox, which prevent the content growing beyond the width of the ListBox, and forcing the content to resize as the ListBox width is increased.

Related

WPF Adding a datatrigger to the ImageSource of a user control from another control

I have the following usercontrol:
<Border
Style="{StaticResource notificationBarBorderStyle}"
Height="21"
>
<StackPanel
Orientation="Horizontal"
Style="{StaticResource notificationBarStyle}"
>
<DockPanel>
<Image
Width="17"
Height="16"
Margin="4,0,11,0"
Source="{Binding ElementName=NotificationControl, Path=ImageSource}"
/>
<TextBlock
x:Name="notificationTextBlock"
VerticalAlignment="Center"
Style="{StaticResource textBlockStyle}"
Text="{Binding ElementName=NotificationControl, Path=Message}"
/>
</DockPanel>
</StackPanel>
</Border>
and then in another usercontrol i try to reference it like so:
<Controls:NotificationBarControl
Grid.Row="2"
Grid.Column="0"
DataContext="{Binding IncomingResult}"
Message="{Binding TaskResultsMessage}"
Visibility="{Binding Path=ShowTaskResults, Converter={StaticResource boolToHiddenVisibilityConverter}}"
Command="{Binding DisplayTaskError}"
ImageSource="{DynamicResource somePicture1}"
>
I want to be able to put a data trigger on the image source so that depending on the state of a boolean flag, a different image will appear (call it somePicture2). I do not really want to do much to change the control itself, as it is referenced a few times in a rather large project and I do not want to break anything.
You could set the Style property of the control to a Style with a DataTrigger that binds to your bool property:
<Controls:NotificationBarControl
Grid.Row="2"
Grid.Column="0"
DataContext="{Binding IncomingResult}"
Message="{Binding TaskResultsMessage}"
Visibility="{Binding Path=ShowTaskResults, Converter={StaticResource boolToHiddenVisibilityConverter}}"
Command="{Binding DisplayTaskError}">
<Controls:NotificationBarControl.Style>
<Style TargetType="Controls:NotificationBarControl">
<Setter Property="ImageSource" Value="{StaticResource somePicture1}" />
<Style.Triggers>
<DataTrigger Binding="{Binding YourBooleanSourceProperty}" Value="True">
<Setter Property="ImageSource" Value="{StaticResource somePicture2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Controls:NotificationBarControl.Style>
</Controls:NotificationBarControl>

Control template: how to create bindings

So, I have a datagrid that has different colour cells depending on the cell's value.
I also have a tooltip that displays some further information. This all works fine.
I, however, would like to alter the tooltip to show further information and also to be the same colour as the cell. So, I thought it would be wise to create a custom style for my tool tips. So, I have the below code.
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="#AA000000"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"/>
<TextBlock Grid.Row="1"/>
<TextBlock Grid.Row="2"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have an object shown below that is bound to my datagrid. I want to bind the three properties to the three textboxes in my tooltip.
class MyTask
{
public string Name;
public int Code;
public string Description;
}
In my DataGrid I do the following to bind my data to my datagrid
ItemsSource="{Binding TaskList}"
Then in the DataGridTextColumn I bind to a property like below
DataGridTextColumn Header="Code" Binding="{Binding Code}"
This makes sense to me. I am however at a loss to see how I use binding when creating my custom tooltip. I read that I can use templatebinding. I still don't understand how my tooltip will bind to my object of type MyTask in my xaml above?
Update - hopefully make my question clearer
I want to know how to create the bindings in my control template (for the 3 textboxes) and then in the main part of my code how I bind to these text boxes. I then would like to know how to create a binding for the background colour of my control template, I believe this is something to do with relativesource?
When I'm reading other examples (changing the Template Property) I see lines like below. I don't really understand why you have to do it? Is it a case of if you didn't right the line below you wouldn't be able to create a binding on the Padding property?
<Border Padding="{Binding Padding}" ...>
You don't need TemplateBindng, as that is used for setting up the resulting template object to layout based on dynamically using the properties of the implementing control. See this CodePlex article for a good example of when you'd need such functionality.
You simply need to set the bindings of your TextBlock elements within your ToolTip. You don't really need a Template at all in this case, except that since you are using the same ToolTip across all your column cells, it will help you out as you don't need to copy-paste the same code three times. You are after something similar to this article, Tooltip in DataGrid in WPF.
A solution which would work specifically to your case would be like:
<DataGrid Name="TestGrid1" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.ToolTip>
<ToolTip />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Code">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Code}">
<TextBlock.ToolTip>
<ToolTip />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Description">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}">
<TextBlock.ToolTip>
<ToolTip />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.Resources>
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="#AA000000"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Text="{Binding Code}"/>
<TextBlock Grid.Row="2" Text="{Binding Description}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
You set the ToolTip property within the CellTemplate so that the resulting ToolTip that pops up has the same DataContext as the active row in the DataGrid. This way, you can simply do your property bindings as normal within your ToolTip ContentTemplate, since it has access to all the same properties as does your DataGrid for the row.
To use underlying DataGridCell Background as ToolTip Background, bind your Border Background as Background="{Binding PlacementTarget.Background, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}" .
You are trying to show all the fields in the tooltip of a cell. That doesn't make sense. But still you can do that easily using
PlacementTarget property, which gives you the underlying Visual element. ContextMenu, Popup too expose this property.
PlacementTarget.DataContext will give you the underlying MyTask object.
PlacementTarget.Content will give you the content of the corresponding DataGridCell, in your case it will be TextBlock.
So, if you want to show 3 fields in your cell's tooltip, below code will work for you using point number 2 above.
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="Tomato"/>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<ToolTip.Style>
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="{Binding PlacementTarget.Background, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding PlacementTarget.DataContext.Name, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
/>
<TextBlock Grid.Row="0" Text="{Binding PlacementTarget.DataContext.Code, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
/>
<TextBlock Grid.Row="0" Text="{Binding PlacementTarget.DataContext.Description, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToolTip.Style>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
And, if you want to show only corresponding cell's field in your cell's tooltip, then remove the remaining 2 textblocks, and use only one as :
<TextBlock Text="{Binding PlacementTarget.Content.Text, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}" />
If you want to show all 3 fields, then apply the ToolTip to DataGridRow using DataGrid.RowStyle. No change in code would be needed.
What about using a color property in MyTask?
class MyTask
{
public string Name { get; set; }
public int Code { get; set; }
public string Description { get; set; }
public SolidColorBrush Color { get; set; }
}
And binding to the color property:
<Style TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border Name="Border" Background="{Binding Color}" BorderBrush="{StaticResource SolidBorderBrush}" BorderThickness="1" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Code}" />
<TextBlock Text="{Binding Description}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Why don't you go for DataTemplate of tool tip like give it's a key and apply it in your cell tool tip style.
<Style TargetType="ToolTip" x:Key="ToolTipStyle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="#AA000000"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"/>
<TextBlock Grid.Row="1"/>
<TextBlock Grid.Row="2"/>
</Grid>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Now, you can bind your property to textblock too.

WPF : How to bind Button's visibility inside ListBox ItemsControl using selecteditem information ?

I have a list box binds to a ObservableCollection of Items. Each listboxItem contains a few textblocks and a few buttons. I would like to show the buttons inside the listboxItem only when the listbox item is selected or hightlight. If the listbox item is not highlighted, the button should be hidden. I tried to use datatrigger to bind to the IsSelected property. However it does not work. Please advice. thanks
<ListBox Name="LayoutListBox" SelectedItem="{Binding Path=SelectedLayout, Mode=TwoWay}" ItemsSource="{Binding Layouts}" SelectionMode="Single" HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Gray"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Gray"/>
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border HorizontalAlignment="Stretch" Margin="2" CornerRadius="2" BorderBrush="#80808080" BorderThickness="1" Background="GhostWhite">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height ="*"/>
<RowDefinition Height ="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="1" Grid.Column="3" DockPanel.Dock="Right">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Visibility="{Binding Path=ViewName, Converter={StaticResource Converter}, ConverterParameter=false}">
<Button ToolTip="Save">
<Image Source="/Common.View;component/LayoutManager/View/Images/PushPin.png" />
</Button>
<Button Click="EditButtonClick" ToolTip="Edit">
<Image Source="/Common.View;component/LayoutManager/View/Images/pencil.png" />
</Button>
<Button Click="DeleteButtonClick" ToolTip="Delete">
<Image Source="/Common.View;component/LayoutManager/View/Images/cross.png" />
</Button>
</StackPanel>
</DockPanel>
</Grid>
</DockPanel>
</Grid>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I attached a simple example to show button in listbox item when you selected. it should have to bind as RelativeSource and have to use Converter
Example:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="booleanVisibleConverter" />
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding ObservableCollection}" Grid.Row="0" SelectedItem="{Binding Path=Item}"
SelectionMode="Single" IsSynchronizedWithCurrentItem="True" Margin="1" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectedIndex="0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding}" />
<Button Content=" X " Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected, Converter={StaticResource booleanVisibleConverter}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
You can create a style to associate with each of your buttons as follows:
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType={x:Type ListBoxItem}},
Path=IsSelected}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>

image binding to the drodown values in wpf

In my xaml form i have a dropdown button the dropdown values will bind from database and my requirement is i want to add one image for each dropdown value in the dropdown box, how can get this.....
<ctrl:DropDownButton Grid.Column="1"
Height="25"
Text="Add Question"
Width="125"
Margin="5,0,10,0">
<ctrl:DropDownButton.DropDownContextMenu>
<ContextMenu ItemsSource="{Binding Source={StaticResource QuestionTypes}}">
<ContextMenu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="WhiteSmoke"/>
</ItemsPanelTemplate>
</ContextMenu.ItemsPanel>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.AddQuestionCommand,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<TextBlock Grid.Column="1" Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ctrl:DropDownButton.DropDownContextMenu>
</ctrl:DropDownButton>
*I wrote this outside of VisualStudio so please check the syntax
you should add an Image element to your DataTempalte and using a converter for the enum value resolve the path to the image you would like to present.
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<Grid>
<Grid.ColumnDefenitions>
<ColumnDefenition />
<ColumnDefenition />
</Grid.ColumnDefenitions>
<Image Source="{Binding Path=., Converter={StaticResource EnumImageConverter}}" />
<TextBlock Grid.Column="1" Text="{Binding Path=., Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>

C# / WPF - DataGrid - Binding the Width of a TextBox in RowDetails to that of the containing DataGrid

My problem is similar to this one;
DataGrid RowDetails Width problem
Except I would like row details never to exceed the width of the columns it spans.
|--0--|--1--|--2--|--3--|--4--|
|---------Row-Details---------|
I have tried AreRowDetailsFrozen and this had no effect. I have also tried binding to the parent Grid's actual width (OneWay) but this causes the width to exceed that of my two screens.
Here is my current attempt (simplified);
<Grid>
<DataGrid x:Name="Grid"
Grid.Row="1"
ItemsSource="{Binding Collection}"
IsReadOnly="True"
AutoGenerateColumns="False"
ColumnWidth="Auto"
CanUserResizeColumns="False"
CanUserResizeRows="False"
RowDetailsVisibilityMode="VisibleWhenSelected"
AreRowDetailsFrozen="True"
SelectionUnit="FullRow"
VerticalAlignment="Top"
HorizontalAlignment="Center">
<DataGrid.RowDetailsTemplate>
<!-- Begin row details section. -->
<DataTemplate>
<TextBox DataContext="{Binding ErrorMessage}"
IsReadOnly="True"
Margin="5"
BorderBrush="Transparent"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"
TextWrapping="Wrap"
Text="{Binding .}">
</TextBox>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>
This results in the following;
|--0--|--1--|--2--|--3--|--4--|
|---------Row-Details are as wide as the longest row in their content ---------|
Binding the width of the TextBox to any parent container (Grid, DataGrid, ItemsPresenter):
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}}, Path=ActualWidth, Mode=OneWay}"
Results in:
|------Viewable Area-------|
|---- Columns ----|
|---------Row-Details --------------------------------------------------------------|
It's very frustrating, I just want the Row Details not to change the width of the DataGrid, is that so much to ask? :)
The only way to accomplish this is to change DataGridRow ControlTemplate. There we can bind row details host width (DataGridDetailsPresenter) to width of cells. For example:
<Style x:Key="{x:Type dg:DataGridRow}" TargetType="{x:Type dg:DataGridRow}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
<Setter Property="ValidationErrorTemplate">
<Setter.Value>
<ControlTemplate>
<TextBlock Margin="2,0,0,0" VerticalAlignment="Center" Foreground="Red" Text="!" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dg:DataGridRow}">
<Border x:Name="DGR_Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<dgp:SelectiveScrollingGrid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<dgp:DataGridCellsPresenter x:Name="cellPresenter" Grid.Column="1"
ItemsPanel="{TemplateBinding ItemsPanel}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<dgp:DataGridDetailsPresenter dgp:SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=AreRowDetailsFrozen, Converter={x:Static dg:DataGrid.RowDetailsScrollingConverter}, ConverterParameter={x:Static dg:SelectiveScrollingOrientation.Vertical}}"
Grid.Column="1" Grid.Row="1"
Visibility="{TemplateBinding DetailsVisibility}" Width="{Binding ElementName=cellsPresenter, Path=ActualWidth}"/>
<dgp:DataGridRowHeader dgp:SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" Grid.RowSpan="2"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=HeadersVisibility, Converter={x:Static dg:DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static dg:DataGridHeadersVisibility.Row}}"/>
</dgp:SelectiveScrollingGrid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Hope this helps.
I answered a similar question here DataGrid RowDetails Width problem
The answers here felt like a workaround so I did some research and did
find the solution on the Telerik forums, since we use their
RadGridView. Turned out the solution worked for DataGrid as well.
The key is to set the ScrollViewer.HorizontalScrollBarVisibility
property to Disabled, see example below.
<DataGrid ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border>
<TextBlock Foreground="White" Text="{Binding RowDetails}"
TextWrapping="Wrap"/>
</Border>
</DataTemplate>
</DataGrid.RowDetailsTemplate> </DataGrid>
I've found another way to fix the Problem:
private void GridOnLoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
var dataGridColumnHeadersPresenter = FindVisualChild<DataGridColumnHeadersPresenter>((DataGrid)sender);
e.DetailsElement.SetBinding(WidthProperty, new Binding("ActualWidth") { Source = dataGridColumnHeadersPresenter });
}
This prevents you from using constant values (e.g. '6') - which won't fit, if someone set DataGrid.RowHeaderWidth - and Converters.
I've added this to the DataGrid.LoadingRowDetails event handler, since I am already tweaking the RowDetails in some other ways.

Categories