I have a DataTemplate for a ListView in XAML:
<DataTemplate x:Key="ResultItemTemplate">
<Grid Grid.Column="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}"
Margin="0,10,20,0"
Grid.Column="0"
Grid.Row="0"/>
<TextBlock Text="{Binding TimeStamp}"
Margin="0,10,10,0"
Grid.Column="1"
Grid.Row="0"/>
<TextBlock Text="{Binding Text}"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="1"
TextWrapping="Wrap"
Height="auto"
Margin="0,0,10,10"/>
<TextBlock Text="{Binding Additional}"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2"
TextWrapping="Wrap"
Height="auto"
Margin="0,0,10,20" />
</Grid>
</DataTemplate>
So applying this DataTemplate to my ListView, the Additional TextBlock is not present in every list item.
However the spacing exists for the Additional TextBlock whether the DataBinding value is null or not.
How do I get the text block to only be added when the 'Additional' Binding property exists?
This is your Converter:
namespace ValueConveters
{
public class NullToVisibilityConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value == null ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
}
And this is your XAML:
put this in your root element:
xmlns:conveters="clr-namespace:ValueConveters"
and then this in your Resources:
<conveters:NullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
<DataTemplate x:Key="ResultItemTemplate">
<Grid Grid.Column="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}"
Margin="0,10,20,0"
Grid.Column="0"
Grid.Row="0"/>
<TextBlock Text="{Binding TimeStamp}"
Margin="0,10,10,0"
Grid.Column="1"
Grid.Row="0"/>
<TextBlock Text="{Binding Text}"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="1"
TextWrapping="Wrap"
Height="auto"
Margin="0,0,10,10"/>
<TextBlock Text="{Binding Additional}"
Visibility="{Binding Additional,Converter={StaticResource NullToVisibilityConverter}}"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2"
TextWrapping="Wrap"
Height="auto"
Margin="0,0,10,20" />
</Grid>
</DataTemplate>
Based on Flat Eric's suggestion - fields can be bound to the IsVisible property:
<Label IsVisible="{Binding Foo}" Text="{Binding Foo}"/>
When the value is null the Label is hidden.
To collapse the TextBlock when Additional property is not available, try to bind TextBlock's Visiblity property with TargetNullValue set to Collapsed. I would suggest something like this :
<TextBlock Text="{Binding Additional}"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2"
TextWrapping="Wrap"
Height="auto"
Margin="0,0,10,20"
Visibility="{Binding Additional, TargetNullValue=Collapsed}"
/>
But turned out it doesn't work for me, for a reason I don't know. But binding this way did the trick :
<TextBlock DataContext="{Binding Additional}"
Text="{Binding}"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2"
TextWrapping="Wrap"
Height="auto"
Margin="0,0,10,20"
Visibility="{Binding TargetNullValue=Collapsed}"
/>
Related questions :
How to Set TargetNullValue to Visibility.Collapsed in Binding
Control not being hidden when binding source is null
Binding Visibility to DataContext
You can try use the BindingBase.TargetNullValue for this:
<TextBlock Margin="{Binding Path=MyMargin, TargetNullValue=0}" />
Beforehand to create a property MyMargin of Thickness? type with some default value. If value of MyMargin will be null value, then value take from TargetNullValue.
You can edit the style of the textblock and set the visibility to collapsed or visible based on the value of the binding. The latter can be done using datatriggers.
It will be easier if you create a property ShowAdditional, which returns true or false, based on the value of the binding. This way you can easily customize when to show it or not.
As follows:
<TextBlock Text="{Binding Additional}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" TextWrapping="Wrap" Height="auto" Margin="0,0,10,20">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowAddition}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding CameraState}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
With visiblity set to collapsed, the entire element will be collapsed, including its margin.
Related
Here is my code:
<ListView Grid.Row="1" x:Name="viewTicket" Style="{StaticResource ticketListBox}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="Transparent" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center" BorderBrush="{x:Null}" SelectionChanged="ViewTicket_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Visibility="{Binding selectedCheck}" Name="check" Grid.Column="0" Margin="10,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" Source="../../Images/check-donatota.png" Stretch="None" MouseLeftButtonUp="Check_MouseLeftButtonUp"/>
<TextBlock Visibility="{Binding selectedQuantity}" Name="quantity" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding amount}"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="Black" TextWrapping="Wrap" Text="{Binding name}"/>
<TextBlock Visibility="{Binding selectedPrice}" Name="price" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding total, StringFormat=C}"/>
<Image Visibility="{Binding selectedTrash}" Name="trash" Grid.Column="2" Margin="0,0,15,0" HorizontalAlignment="Right" VerticalAlignment="Center" Source="../../Images/trash-donatota.png" Stretch="None" MouseLeftButtonUp="Trash_MouseLeftButtonUp"/>
</Grid>
<ListView
ItemsSource="{Binding ingredients}"
Grid.Row="1"
Margin="-5,0,0,0"
Name="viewTicketIngs"
IsHitTestVisible="False"
Style="{StaticResource ticketListBox}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
Background="Transparent"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Center"
BorderBrush="{x:Null}"
SelectionChanged="ViewTicketIngs_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<TextBlock Visibility="Visible" Name="quantity" Grid.Column="0" Foreground="{DynamicResource GrayTextDonaTotaBrush}" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding amount}"/>
<TextBlock Margin="10,0,0,0" Grid.Column="1" Foreground="{DynamicResource GrayTextDonaTotaBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding ing.name}"/>
<TextBlock Visibility="Visible" Name="price" Grid.Column="2" Foreground="{DynamicResource GrayTextDonaTotaBrush}" HorizontalAlignment="Right" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding total, StringFormat=C}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I add data to the ListView viewTicket, but depending of a property I would like to change the ItemSource Binding of the ListView viewTicketIngs. In other words, is there anyway that I can use an if expression on the binding? Something like ItemsSource="{Binding IF(mode == 0) {ingredients} else {plates}}"
Change the Binding by a DataTrigger in a Style:
<ListView ...>
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="ItemsSource" Value="{Binding plates}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding mode}" Value="0">
<Setter Property="ItemsSource" Value="{Binding ingredients}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
As I understand it, you sometimes dispaly plates, sometimes ingredients. Now there are triggers conditional display. WPF actually has a pretty wide support.
However what might be better is to have 2 different ViewModel classes and two (how I call them) "type targetting data tempaltes". Say you have these clases:
abstract class ViewModelItem { }
class Plate : ViewModelItem { }
class IngredientsList : ViewModelItem { }
The propety you exposie this in, would be set to ViewModelItem. In realtiy you would assign either a Plate or IngredientsList Instance.
Now you define two DataTemplates. A interesting thing about WPF is that if you do not specify a explicit Template, the Code will go out of it's way to try to find one. And it will do the matching via the DataType property of the Template (TargetType for Styles and similar). It works similar to what CSS does, with somebodies code going out of it's way to find a template to apply.
I have ListBox with DataTemplate, like this:
<ListBox ItemsSource="{Binding}" BorderBrush="Transparent"
Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch"
Grid.Row="1"
Grid.Column="0" Grid.ColumnSpan="4"
Name="playerList">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2">
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="16" />
<TextBlock Grid.Column="1" Text="{Binding Drinked }" FontWeight="Bold" FontSize="16" />
<TextBlock Grid.Column="2" Text="{Binding Remaining }" FontWeight="Bold" FontSize="16" />
<Button Grid.Column="3" Name="addButton" Click="addButton_Click" FontWeight="Bold" FontSize="16">+</Button>
<Button Grid.Column="4" Name="substractButton" Click="substractButton_Click" FontSize="16">-</Button>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have class that has the properties Name, Drinked, etc. It's in List<> that is set as DataSource. This part works fine.
But I need to add extra entry to this ListBox, that will be displayed before the DataTemplate. It will not have bindings or the same structure - it will serve as header and will have different layout than the DataTemplate.
Is there any way to do it? If I add it like in a normal ListBox, I then got an error that the ListBox must be empty before using binding.
Now the ListBox looks like this:
But I need to make it looks like this:
Is it possible to do so? If there is way to do it using other element than ListBox, I'm fine with it, as long as I can use Binding and DataTemplate.
If scrollign doesn't matter to you can put simply a static border just above your listbox..
For the scenario where you want to include that extra row in the scrolling you have to modify ListBox's template. Here is an example of an implicit style that would add a TextBlock just before your items and that would participate of the scrolling:
<Style TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<ScrollViewer x:Name="ScrollViewer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="I am an extra row" Grid.Row="0"/>
<ItemsPresenter Grid.Row="1"/>
</Grid>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In fact by overriding the control's template you can give it all the customizations that you need... You can change the textblock for whatever any other control you want..
You could achieve this goal by using a Stackpanel or Grid. However, if you insist on using an extra row in your ListBox, you can make use of a Content Template Selector. In the following solution, I did use a trigger to set the template of the current item according to its type.
<Window.Resources>
<local:ObjectToTypeConverter x:Key="ObjectToTypeConverter" />
<ControlTemplate x:Key="emptyRow">
<Grid HorizontalAlignment="Stretch">
<Border Height="50" BorderBrush="Black" BorderThickness="2">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="Extra row that is not part of the binding or DataTemplate" />
</Border>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="default">
<Border BorderBrush="Black" BorderThickness="2">
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="16" />
<TextBlock Grid.Column="1" Text="{Binding Drinked }" FontWeight="Bold" FontSize="16" />
<TextBlock Grid.Column="2" Text="{Binding Remaining }" FontWeight="Bold" FontSize="16" />
<Button Grid.Column="3" Name="addButton" Click="addButton_Click" FontWeight="Bold" FontSize="16">+</Button>
<Button Grid.Column="4" Name="substractButton" Click="substractButton_Click" FontSize="16">-</Button>
</Grid>
</Border>
</ControlTemplate>
<Style x:Key="ItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template" Value="{DynamicResource emptyRow}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ., Converter={StaticResource ObjectToTypeConverter}}" Value="{x:Type local:Model}">
<Setter Property="Template" Value="{DynamicResource default}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding}" BorderBrush="Transparent"
Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch"
Grid.Row="1"
ItemContainerStyle="{StaticResource ItemStyle}"
Grid.Column="0" Grid.ColumnSpan="4"
Name="playerList" />
</Grid>
The converter simply converts the object to its type
public class ObjectToTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value?.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
To add different objects to your collection, change the type to object or to a type according to your inheritance setup.
Before explaining my problem here is my code:
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Background="WhiteSmoke" >
<Grid Margin="0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<TextBlock Text="Name: " FontWeight="Bold" Grid.Row="0" />
<TextBlock x:Name="parametro" Text="{Binding Username}" Grid.Column="1" Grid.Row="0" />
<TextBlock Text="Creation Date: " FontWeight="Bold" Grid.Row="2" />
<TextBlock Text="{Binding CreationDate}" Grid.Column="1" Grid.Row="2" />
<TextBlock Text="Creation User: " FontWeight="Bold" Grid.Row="4" />
<TextBlock Text="{Binding CreationUser}" Grid.Column="1" Grid.Row="4" />
<Button Style="{DynamicResource MetroCircleButtonStyle}" Grid.RowSpan="3" Foreground="Green" FontSize="13" Width="50" FontFamily="{StaticResource FontAwesome}" Content="" Grid.Column="2" Grid.Row="0" />
<Button Style="{DynamicResource MetroCircleButtonStyle}" Grid.RowSpan="3" Foreground="Red" FontSize="13" Width="50" FontFamily="{StaticResource FontAwesome}" Content="" Grid.Column="3" Grid.Row="0"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.DeleteUser}"
CommandParameter="{Binding ElementName=parametro, Path=Text}"/>
<ComboBox x:Name="combo" SelectedItem="{Binding Role}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}},
Path=DataContext.Roles}" Grid.Row="6" Grid.Column="1"/>
<TextBlock Text="Ruolo: " FontWeight="Bold" Grid.Row="6" Grid.Column="0"/>
<TextBlock Text="Descrizione: " FontWeight="Bold" Grid.Row="8" Grid.Column="0"/>
<TextBox Grid.Row="8" Grid.Column="1"></TextBox>
</Grid>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
My problem is that when I write text in the textbox (last element in the xaml), the width of the textbox grows with it. Now I know that there is a maxwidth property, but since I don't have a defined width for my grid columns, I can't bind it to the width of my textbox. I don't want to set the width in terms of real pixels, since I want my application to be "resizable". I also tried to create a fake control like a Border, and bind the width of the textbox to it but it doesn't work. How can I solve this?
First, we need to make the template assume the size of its parent.
Set TextWrapping="WrapWithOverflow" on the textbox.
At least one column must have a width of * rather than the default of Auto. This will cause the Grid to size itself to its parent. The second column seemed best to me for that role.
Give the Grid HorizontalAlignment="Left" so it doesn't end up getting centered.
You can get rid of the StackPanel; it's not doing anything for you.
Second, we need to make some changes in the DataGrid to constrain the width of its row details area.
This is my example I tested with; your ItemsSource will differ of course.
<DataGrid
ItemsSource="{Binding Items}"
AutoGenerateColumns="False"
RowHeaderWidth="0"
>
<!--
ScrollContentPresenter includes the row header column.
DataGridDetailsPresenter doesn't. So we set RowHeaderWidth="0"
above to avoid making the details too wide by an amount equal to
the width of the row header.
This is just cosmetic, so leave RowHeaderWidth alone if you have
a requirement to keep the row header visible.
-->
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Grid
Margin="0,10"
Background="WhiteSmoke"
HorizontalAlignment="Left"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<TextBlock Text="Name: " FontWeight="Bold" Grid.Row="0" />
<TextBlock x:Name="parametro" Text="{Binding Username}" Grid.Column="1" Grid.Row="0" />
<TextBlock Text="Creation Date: " FontWeight="Bold" Grid.Row="2" />
<TextBlock Text="{Binding CreationDate}" Grid.Column="1" Grid.Row="2" />
<TextBlock Text="Creation User: " FontWeight="Bold" Grid.Row="4" />
<TextBlock Text="{Binding CreationUser}" Grid.Column="1" Grid.Row="4" />
<Button Style="{DynamicResource MetroCircleButtonStyle}" Grid.RowSpan="3" Foreground="Green" FontSize="13" Width="50" Content="" Grid.Column="2" Grid.Row="0" />
<Button Style="{DynamicResource MetroCircleButtonStyle}" Grid.RowSpan="3" Foreground="Red" FontSize="13" Width="50" Content="" Grid.Column="3" Grid.Row="0"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.DeleteUser}"
CommandParameter="{Binding ElementName=parametro, Path=Text}"/>
<ComboBox MaxWidth="185" x:Name="combo" SelectedItem="{Binding Role}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}},
Path=DataContext.Roles}" Grid.Row="6" Grid.Column="1"/>
<TextBlock Text="Ruolo: " FontWeight="Bold" Grid.Row="6" Grid.Column="0"/>
<TextBlock Text="Descrizione: " FontWeight="Bold" Grid.Row="8" Grid.Column="0"/>
<TextBox
Grid.Row="8"
TextWrapping="WrapWithOverflow"
Grid.Column="1"
></TextBox>
</Grid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
How did I figure this out?
I added preview mouse-down handler to the Grid in the DataTemplate:
<Grid Margin="0,10" Background="WhiteSmoke" PreviewMouseDown="Grid_PreviewMouseDown">
Ran my test program with some test data, clicked on a row to expand the row details, and clicked in the row details area. And this is the handler I have for that mouse event:
private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
List<DependencyObject> parents = new List<DependencyObject>();
var parent = VisualTreeHelper.GetParent(sender as DependencyObject);
while (parent != null)
{
parents.Add(parent);
parent = VisualTreeHelper.GetParent(parent);
}
;
}
I set a breakpoint and looked at parents in the watch window.
- parents Count = 16 System.Collections.Generic.List<System.Windows.DependencyObject>
+ [0] {System.Windows.Controls.Primitives.DataGridDetailsPresenter} System.Windows.DependencyObject {System.Windows.Controls.Primitives.DataGridDetailsPresenter}
+ [1] {System.Windows.Controls.Primitives.SelectiveScrollingGrid} System.Windows.DependencyObject {System.Windows.Controls.Primitives.SelectiveScrollingGrid}
+ [2] {System.Windows.Controls.Border} System.Windows.DependencyObject {System.Windows.Controls.Border}
+ [3] {System.Windows.Controls.DataGridRow} System.Windows.DependencyObject {System.Windows.Controls.DataGridRow}
+ [4] {System.Windows.Controls.Primitives.DataGridRowsPresenter} System.Windows.DependencyObject {System.Windows.Controls.Primitives.DataGridRowsPresenter}
+ [5] {System.Windows.Controls.ItemsPresenter} System.Windows.DependencyObject {System.Windows.Controls.ItemsPresenter}
+ [6] {System.Windows.Controls.ScrollContentPresenter} System.Windows.DependencyObject {System.Windows.Controls.ScrollContentPresenter}
+ [7] {System.Windows.Controls.Grid} System.Windows.DependencyObject {System.Windows.Controls.Grid}
+ [8] {System.Windows.Controls.ScrollViewer} System.Windows.DependencyObject {System.Windows.Controls.ScrollViewer}
+ [9] {System.Windows.Controls.Border} System.Windows.DependencyObject {System.Windows.Controls.Border}
+ [10] {System.Windows.Controls.DataGrid Items.Count:10} System.Windows.DependencyObject {System.Windows.Controls.DataGrid}
+ [11] {System.Windows.Controls.Grid} System.Windows.DependencyObject {System.Windows.Controls.Grid}
+ [12] {System.Windows.Controls.ContentPresenter} System.Windows.DependencyObject {System.Windows.Controls.ContentPresenter}
+ [13] {System.Windows.Documents.AdornerDecorator} System.Windows.DependencyObject {System.Windows.Documents.AdornerDecorator}
+ [14] {System.Windows.Controls.Border} System.Windows.DependencyObject {System.Windows.Controls.Border}
+ [15] {CS7Test02.MainWindow} System.Windows.DependencyObject {CS7Test02.MainWindow}
The Grid's parent is DataGridDetailsPresenter:
+ [0] {System.Windows.Controls.Primitives.DataGridDetailsPresenter} System.Windows.DependencyObject {System.Windows.Controls.Primitives.DataGridDetailsPresenter}
His parent is SelectiveScrollingGrid, and on up the chain. By simple trial and error, I found the parent with the ActualWidth I wanted, and bound to that.
I found another way of applying the desired width as a feature of the DataGrid itself rather than the datatemplate. This lets you use arbitrary details templates without having to individually fix up each one to use the correct width.
<DataGrid.RowStyle>
<!--
This style exists only so we can use its Resources to declare
the DataGridDetailsPresenter style someplace where it'll be taken
as an implicit style for DataGridDetailsPresenter in this grid's
row details children.
-->
<Style TargetType="DataGridRow">
<Style.Resources>
<Style TargetType="DataGridDetailsPresenter">
<Setter
Property="Width"
Value="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
/>
</Style>
</Style.Resources>
</Style>
</DataGrid.RowStyle>
I have two grids contained in borders like this :
<Border BorderBrush="Gray" BorderThickness="2" Margin="5" Visibility="{Binding SelectedItem, ElementName=tcAction, Converter={StaticResource LoadChangeHeaderToVisibilityConverter}}">
<Grid x:Name="actionGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text=" Card Name" HorizontalAlignment="Center" Margin="0,4,0,0" FontSize="16"/>
<Label Grid.Row="1" Content="{Binding CardName}" FontSize="14" HorizontalAlignment="Center" />
<TextBlock Grid.Row="2" Text="{Binding ActionType, StringFormat='Action Type: {0}'}" Margin="4" FontSize="13" FontWeight="ExtraBlack" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="3" Text="{Binding Action, StringFormat='Action: {0}'}" Margin="4" FontSize="13" FontWeight="ExtraBlack" HorizontalAlignment="Center"/>
</Grid>
</Border>
<Border BorderBrush="Gray" BorderThickness="2" Margin="5" Visibility="{Binding SelectedItem, ElementName=tcAction, Converter={StaticResource LoadChangeHeaderToVisibilityConverter}}">
<Grid x:Name="actionCANGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text=" CAN Name" HorizontalAlignment="Center" Margin="0,4,0,0" FontSize="16"/>
<Label Grid.Row="1" Content="{Binding CardName}" FontSize="14" HorizontalAlignment="Center" />
<TextBlock Grid.Row="2" Text="{Binding ActionType, StringFormat='Action Type: {0}'}" Margin="4" FontSize="13" FontWeight="ExtraBlack" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="3" Text="{Binding Action, StringFormat='Action: {0}'}" Margin="4" FontSize="13" FontWeight="ExtraBlack" HorizontalAlignment="Center"/>
</Grid>
</Border>
Both of them are binded to the SelectedItem property of a TabControl. The fact here is that i want only one grid visible at a time, depending on the TabItem selected. So when Visibility is Visible for one of them, it should be Hidden for others. I am not sure how can i track the states of all the future grids, and keep only 1 in the front.
Personally I wouldn't mess with Visibility at all. That's the old WinForms way :)
Instead, I'd use a ContentControl and switch the ContentTemplate property in a DataTrigger based on the SelectedItem. I find this easier to maintain, and there's only one set of items in the visual tree at a time.
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyTabControl, Path=SelectedIndex}" Value="0">
<Setter Property="ContentTemplate" Value="{StaticResource ActionTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=MyTabControl, Path=SelectedIndex}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource ActionCANTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
Instead of binding the SelectedItem of the tabcontrol, Bind SelectedIndex and pass the convertParamereter to the Converter
Your Converter will be like below
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((int)value == (int)parameter)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
And in your Xmal change the visibility as below
Visibility="{Binding SelectedIndex, ElementName=tcAction, Converter={StaticResource LoadChangeHeaderToVisibilityConverter}, ConverterParameter=0}">
Firstly, thank you for taking the time out to read this post. All contributions are very much appreciated.
I'm having difficulty understanding how I can bind a ComboBox ItemsSource within a DataTemplate to an ObservableCollection.
Here's my code thus far:
DataTemplate Template (notice the combo's at the bottom of the template):
<DataTemplate x:Key="ListBoxCustomTemplate">
<Grid Margin="4" HorizontalAlignment="Stretch" x:Name="lstBoxItemRoomGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" FontWeight="Bold" Text="{Binding TemplateGroupName}" />
<Image x:Name="imgDeleteListBoxItem" Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Source="/Icons/Print-Groups-Config/delete-32.png" Height="25" Cursor="Hand"
ToolTip="Remove template" VerticalAlignment="Center"
HorizontalAlignment="Right" MouseLeftButtonUp="imgDeleteListBoxItem_MouseLeftButtonUp">
<Image.Style>
<Style>
<Setter Property="Image.Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=lstBoxItemRoomGrid}" Value="True">
<Setter Property="Image.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding TemplateDescription}" TextWrapping="WrapWithOverflow" />
<!-- Header Template Selection -->
<Label Grid.Row="2" Grid.Column="0" Margin="0,3,0,0" HorizontalAlignment="Left" VerticalAlignment="Bottom" Content="Select Header:" FontWeight="DemiBold" Foreground="DarkGray" />
<telerik:RadComboBox x:Name="radComboHeaderTemplate" Grid.Row="3" Grid.Column="0" Width="120" Margin="0,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"
ClearSelectionButtonVisibility="Visible" SelectedValue="{Binding TemplateHeaderID}" />
<!-- Footer Template Selection -->
<Label Grid.Row="2" Grid.Column="1" Margin="0,3,0,0" HorizontalAlignment="Left" VerticalAlignment="Bottom" Content="Select Footer:" FontWeight="DemiBold" Foreground="DarkGray" />
<telerik:RadComboBox x:Name="radComboFooterTemplate" Grid.Row="3" Grid.Column="1" Width="120" Margin="0,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"
ClearSelectionButtonVisibility="Visible" SelectedValue="{Binding TemplateFooterID}" />
</Grid>
</DataTemplate>
When my Window loads, I download the collection data from my database and store into a local collection. Note that there are two Collections, one for each of the 2 ComboBoxes in my DataTemplate.
//Header Templates
private ObservableCollection<TemplateHeaderFooter> templatesHeader = new ObservableCollection<TemplateHeaderFooter>();
//Footer Templates
private ObservableCollection<TemplateHeaderFooter> templatesFooters = new ObservableCollection<TemplateHeaderFooter>();
//--- Constructors ---
public PrintTemplateGroupsConfigWindow()
{
InitializeComponent();
//Download Data From DB
this.templatesHeader = TemplateHeaderFootersDB.GetAllTemplatesOfType(1);
this.templatesFooters = TemplateHeaderFootersDB.GetAllTemplatesOfType(2);
}
How do I get the collection Data templatesFooters & templatesHeader into the the ItemsSources of their respective ComboBoxes?
The datatemplate is for a ListBox.
<telerik:RadListBox x:Name="lstBoxPrintGroupTemplates" Height="300" Width="280" ItemsSource="{Binding}" IsEnabled="False"
ItemTemplate="{StaticResource ListBoxCustomTemplate}" Style="{StaticResource DraggableListBox}" >
Many thanks. Any help is appreciated.
Define properties wrappers over the variables of your collections and then you can bind them to comboboxes as:
ItemsSource="{Binding TemplatesHeader, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
public ObservableCollection<TemplateHeaderFooter> TemplatesHeader
{
get{return templatesHeader;}
}
Similarly you can do for other property