I have a DataGrid that consists of some columns. In one DataGridTemplateColumn, I want to use a condition. If the condition is False, it should display a single bound property. If the condition is True, it should display multiple bound properties (that's what I can't accomplish). How can I use WrapPanel under DataTrigger Setter?
My XAML code:
<DataGrid x:Name="DG_SipList" ItemsSource="{Binding Items3}" Margin="0 8 0 0" CanUserSortColumns="False" CanUserAddRows="False" AutoGenerateColumns="False" VerticalAlignment="Top" HorizontalAlignment="Left" materialDesign:DataGridAssist.CellPadding="13 8 8 8" materialDesign:DataGridAssist.ColumnHeaderPadding="8" IsReadOnly="True" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" >
<DataGrid.Resources>
<Style TargetType="TextBlock" x:Key="cfgText">
<Style.Triggers>
<DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="False">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="TextDecorations" Value="Underline"/>
<Setter Property="Foreground" Value="Red"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="TextBlock" x:Key="cfgText2">
<Style.Triggers>
<DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="Foreground" Value="Red"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="START" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="cb_MontajStart" Checked="cb_MontajStart_Checked" Unchecked="cb_MontajStart_Unchecked" IsChecked="{Binding LISTE_MONTAJ_START}" HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="ID">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=LISTE_KIMLIK}" Tag="{Binding Path=LISTE_MONTAJ_START}" Style="{StaticResource cfgText2}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="PRODUCT" MaxWidth="450">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding konfTanim}" Value="False">
<Setter Property="Text" Value="{Binding LISTE_URUN}"/>
</DataTrigger>
<DataTrigger Binding="{Binding konfTanim}" Value="True">
<Setter>
<!--This is what I can not combine more than one textblock under Datatrigger Setter-->
<WrapPanel Orientation="Horizontal" MaxWidth="450">
<TextBlock Text="{Binding Path=yeni_ModelTanim}"/>
<TextBlock Text="{Binding Path=MT4}" Tag="{Binding Path=monStd4}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT5}" Tag="{Binding Path=monStd5}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT6}" Tag="{Binding Path=monStd6}" Style="{StaticResource cfgText}"/>
</WrapPanel>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- ........................................................... -->
<DataGridTemplateColumn x:Name="txt_Configuration" Header="configuration" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<WrapPanel Orientation="Horizontal" MaxWidth="450">
<TextBlock Text="{Binding Path=yeni_ModelTanim}"/>
<TextBlock Text="{Binding Path=MT4}" Tag="{Binding Path=monStd4}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT5}" Tag="{Binding Path=monStd5}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT6}" Tag="{Binding Path=monStd6}" Style="{StaticResource cfgText}"/>
</WrapPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
In case of multiple different representations of the same type, depending on a flag or other properties, you have to implement a custom DataTemplateSelector and a data template for each representation, e.g.:
<DataTemplate x:Key="KonfTanimFalseTemplate">
<TextBlock Text="{Binding LISTE_URUN}"/>
</DataTemplate>
<DataTemplate x:Key="KonfTanimTrueTemplate">
<WrapPanel Orientation="Horizontal" MaxWidth="450">
<TextBlock Text="{Binding Path=yeni_ModelTanim}"/>
<TextBlock Text="{Binding Path=MT4}" Tag="{Binding Path=monStd4}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT5}" Tag="{Binding Path=monStd5}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT6}" Tag="{Binding Path=monStd6}" Style="{StaticResource cfgText}"/>
</WrapPanel>
</DataTemplate>
The data template selector checks the konfTanim property and returns the appropriate data template.
public class KonfTanimDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (!(container is FrameworkElement frameworkElement) || !(item is YourItemType yourItem))
return null;
var dataTemplateName = yourItem.konfTanim ? "KonfTanimTrueTemplate" : "KonfTanimFalseTemplate";
return (DataTemplate) frameworkElement.FindResource(dataTemplateName);
}
}
You could also pass in the template names via properties to avoid hard-coding them here. This can help to reuse the selector in your application. In your DataGrid, you could add the data templates to the Resources with an instance of the selector and use it in the template column like this:
<DataGrid ItemsSource="{Binding YourItemTypeList}" AutoGenerateColumns="False">
<DataGrid.Resources>
<DataTemplate x:Key="KonfTanimFalseTemplate">
<TextBlock Text="{Binding LISTE_URUN}"/>
</DataTemplate>
<DataTemplate x:Key="KonfTanimTrueTemplate">
<WrapPanel Orientation="Horizontal" MaxWidth="450">
<TextBlock Text="{Binding Path=yeni_ModelTanim}"/>
<TextBlock Text="{Binding Path=MT4}" Tag="{Binding Path=monStd4}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT5}" Tag="{Binding Path=monStd5}" Style="{StaticResource cfgText}"/>
<TextBlock Text="{Binding Path=MT6}" Tag="{Binding Path=monStd6}" Style="{StaticResource cfgText}"/>
</WrapPanel>
</DataTemplate>
<local:KonfTanimDataTemplateSelector x:Key="KonfTanimDataTemplateSelector"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn CellTemplateSelector="{StaticResource KonfTanimDataTemplateSelector}"/>
</DataGrid.Columns>
</DataGrid>
Isn't there any other way I can do this just using XAML (without C#)?
There is not really anything bad about having a data template selector for complex conditions.
Choosing a DataTemplate Based on Properties of the Data Object
However, often having a flag to determine the type of an object is a sign that the corresponding class should be split into two different types. In this case, you could create a data template with a specific DataType for both types. Unfortunately, automatic data template selection by type works in other items controls, but in DataGrid or more specifically a template column, you would have to use workarounds, which might be more cumbersome than to just create a selector that might as well be reusable in other columns.
Related
I have a ListBox. This ListBox has custom controls. For example, image, textblocks, etc. Initially textblocks are collapsed, image is visible. I want to make the textblocks visible when user enter with mouse and change opacity of image in that item. But I can't access listboxitems controls. I named them but they don't show up.How can I achieve that ? My ListBox XAML codes:
<ListBox SelectionChanged="MoviesDisplay_OnSelectionChanged" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="Black" x:Name="MoviesDisplay">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="MoviesGrid" Height="355" Width="240" Margin="10,20,20,3" HorizontalAlignment="Left">
<Image x:Name="MoviePoster" Opacity="1" Source="{Binding Poster}"></Image>
<TextBlock Visibility="Collapsed" x:Name="MovieName" FontSize="18" Text="{Binding Name}" Foreground="White" Margin="0,15,0,321"></TextBlock>
<TextBlock Visibility="Collapsed" x:Name="MovieGenre" FontSize="16" Text="" Foreground="White" Margin="0,34,0,302"></TextBlock>
<TextBlock Visibility="Collapsed" x:Name="MovieReleaseDate" FontSize="16" Text="{Binding ReleaseDate}" Foreground="White" Margin="0,53,164,284"></TextBlock>
<materialDesign:RatingBar Visibility="Collapsed" Foreground="White"
Value="{Binding Rating}"
x:Name="MovieRatingBar" Margin="59,314,60,17"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
client.GetConfig();
var popularMovies = await client.GetMoviePopularListAsync("en", 1);
PopularMovies.Clear();
foreach (var movie in popularMovies.Results)
{
// ImgListUrls.Add(client.GetImageUrl("w500",movie.PosterPath).AbsoluteUri);
Movie mov = new Movie()
{
Id = movie.Id,
Name = movie.OriginalTitle,
ReleaseDate = movie.ReleaseDate.Value.ToShortDateString(),
Poster = client.GetImageUrl("w500", movie.PosterPath).AbsoluteUri,
Rating = movie.VoteAverage
};
PopularMovies.Add(mov);
}
MoviesDisplay.ItemsSource = PopularMovies;
// DownloadImages(ImgListUrls);
}
You don't need to access these elements in code behind. Add Styles with DataTriggers instead, e.g.:
<Image Source="{Binding Poster}">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsMouseOver,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter Property="Opacity" Value="0.5"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Put the TextBlocks in a StackPanel and add a similar Style:
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsMouseOver,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock FontSize="18" Text="{Binding Name}" Foreground="White"/>
<TextBlock FontSize="16" Text="{Binding ReleaseDate}" Foreground="White"/>
</StackPanel>
I need to show only the Icon in the combo box if the combo box is clicked the icon and the text needs to be shown. As I am not familiar with how to achieve this.
Please help me in achieving this.
<ComboBox Name="cmbColors">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,2,5,2" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This will show the rectangle and text in the combo box and the drop down of the combo box. But my requirement is only to show the color filled rectangle in the combo box.
I am not sure whether I understand what you are trying to do, but my best guess is that you want to hide the text block if some other control has focus. If so, you can do something like this:
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,2,5,2" />
<TextBlock x:Name="text" Text="{Binding Name}" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="False">
<Setter TargetName="text" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If you are instead trying to say that you want a different template for the header than for the dropdown, then you could do something like described in one of the answers to this question.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,2,5,2" />
<TextBlock x:Name="text" Text="{Binding Name}" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBoxItem}}}" Value="{x:Null}">
<Setter TargetName="text" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Okay so my problem is that I generate some elements on the fly, as linked to an array (for each element on the array there is another item in the XAML). I'm doing this with a TreeView control, albiet following an online example (which has been working well so far).
In this example, the groups are families and within each ... well, it's easier to show in an image here. Family is a class which contains an ObservableCollection of FamilyMembers, both of which are used to populate the TreeView.
Here's the main business:
<TreeView Name="trvFamilies">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ImageGroup}" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type self:FamilyMember}">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ImageUserMale}" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Green" />
<TextBlock Text="{Binding Age}" Foreground="Green" />
<TextBlock Text=" years)" Foreground="Green" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
This all works fine, but here's my problem: I have a male image for if a family member is male and a female one for if the family member is female. I don't know how to get at the elements to set the image based on this criterion. As you can see, at the moment the images are static.
Thanks!
You may use a DataTrigger, e.g. on a potential enum Gender property of your FamilyMember:
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{StaticResource ImageUserUnknownGender}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Gender}" Value="Male">
<Setter Property="Source" Value="{StaticResource ImageUserMale}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Gender}" Value="Female">
<Setter Property="Source" Value="{StaticResource ImageUserFemale}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
In my xaml I have a ItemsControl. Is is possible to have the ItemIndex property on ItemsControl? Basically I want to hide one of the child controls (TxtNodeData) if the Item index is 1
<ItemsControl ItemsSource="{Binding ConditionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding NodeData}" Name="TxtNodeData"/>
<Button Content="+" />
<ComboBox ItemsSource="{Binding NodeNames}" DisplayMemberPath="name" SelectedValue="{Binding ConditionalNodeId, Mode=TwoWay}" SelectedValuePath="id"> </ComboBox>
<Button Content="-" />
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
You could do it with combination of AlternationCount set to number of items in the list and trigger on AlternationIndex being 1
<ItemsControl ItemsSource="{Binding ConditionList}" AlternationCount="{Binding ConditionList.Count}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding NodeData}" Name="TxtNodeData">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger
Binding="{Binding
RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}},
Path=(ItemsControl.AlternationIndex)}"
Value="1">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- other controls -->
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have to change the background color of StackPanel (present in DataGrid) when it is selected or focused.
Below is the my XAML:
<DataGrid Background="#DCE1E7" SelectedItem="{Binding Row, Mode=TwoWay}" AutoGenerateColumns="False" CanUserAddRows="False" HeadersVisibility="None" GridLinesVisibility="None" HorizontalAlignment="Left" BorderThickness="0" Name="dtlCuisine" VerticalAlignment="Top" BorderBrush="#DCE1E7" VerticalGridLinesBrush="#DCE1E7" HorizontalGridLinesBrush="#DCE1E7" ColumnWidth="130" SelectionChanged="dtlCuisine_SelectionChanged" CellEditEnding="dtlCuisine_CellEditEnding">
<DataGrid.Columns>
<DataGridTemplateColumn x:Name="Template13">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Name="stkpnlCuisines" Orientation="Horizontal" Background="#DCE1E7" Margin="-1,-1,-1,-1" MouseDown="stkpnlCuisines_MouseDown_1">
<Label Content="{Binding CuisineName}" Height="28" HorizontalAlignment="Left" Name="lblCuisineName" VerticalAlignment="Top" FontSize="14" />
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsFocused}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Margin="-1,-1,-1,-1">
<TextBox Name="txtCuisineCategory" AcceptsReturn="True" Text="{Binding CuisineName}" LostFocus="txtCuisineCategory_LostFocus" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
What is the best way to change the Background color of StackPanel.
Use the IsSelected trigger on the DataGridCell. If you wan't to completely change the selected colors accross all elements, then you can use the following code in your application resources.
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#3271B5" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="AliceBlue" />