DataTrigger WPF - wpf

I have two lists. List of Things and List of Fruits. If an item in the list of things is a fruit from the fruits list, I would like that item to be highlighted.
I would like this with data-binding and thru xmal and not code-behind b/c I am doing MVVM pattern. I have tried it with DataTrigger and Converter but can't get it to work. Please help.
Thanks.
<ListBox ItemsSource="{Binding Things}"
Name="ListOfThigns"
Grid.Row="1">
<DataTemplate>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Fruits}" >
<Setter Property="ListBoxItem.Background" Value="Green"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox>
<ListBox ItemsSource="{Binding Fruits}"
Name="ListOfFruits"
Grid.Column="1"
Grid.Row="1">
</ListBox>

Or, if Fruit inherits from Thing (a fruit is a thing):
<ListView ItemsSource="{Binding Things}">
<ListView.Resources>
<DataTemplate DataType="{x:Type dm:Fruit}">
<TextBlock Text="{Binding Name}" Background="RoyalBlue"/>
</DataTemplate>
<DataTemplate DataType="{x:Type dm:Thing}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.Resources>
</ListView>

Related

WPF ContentControl inside of TabControl does not show DataTemplates

I have a ListView which has TabControl which shows 2 type of views.
<ListView x:Name="Devices" ItemsSource="{Binding Devices}">
<ListView.Resources>
<DataTemplate DataType="{x:Type app:DeviceViewModel}">
<Expander Header="{Binding Type}">
<StackPanel Orientation="Vertical">
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="{x:Type app:DeviceAViewModel}">
<local:DeviceAView/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:DeviceBViewModel}">
<local:DeviceBView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}" DisplayMemberPath="Index">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Resources> <------ this part does't work
<DataTemplate DataType="{x:Type app:ChannelAViewModel}">
<local:ChannelAView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:ChannelBViewModel}">
<local:ChannelBView DataContext="{Binding}"/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</StackPanel>
</Expander>
</DataTemplate>
</ListView.Resources>
</ListView>
The content for each tab item is just empty instead of showing the matching channel view.
If I remove the whole <TabControl.ContentTemplate> Tree It does show the matching objects strings inside the tabitem content.
Any idea why it doesn't show the data templates?
Thank you.
Try to Listview item template like this.
like this:
<ListView x:Name="Devices" ItemsSource="{Binding Devices}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical">
<ContentControl>
</ContentControl>
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}">
</TabControl>
</StackPanel>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
or this:
<ListView x:Name="Devices" ItemsSource="{Binding Devices}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical">
<ContentControl>
</ContentControl>
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}">
</TabControl>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Okay so I figured it out.
I had to add Content={Binding} to the ContentControl or I could remove it and instead of using TabControl.ContentTemplate I placed the DataTemplates inside TabControl.Resources and it works fine.
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}" DisplayMemberPath="Index">
<TabControl.Resources>
<DataTemplate DataType="{x:Type app:ChannelAViewModel}">
<local:ChannelAView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:ChannelBViewModel}">
<local:ChannelBView DataContext="{Binding}"/>
</DataTemplate>
</TabControl.Resources>
</TabControl>

Change displayed text in XAML Combobox multi selection

I have the following code to allow users to select multiple items from a combobox. However when they click one item, it makes this the displayed text when combobox closes. Can I change the displayed text to something that isnt just the item selected. For example if the users select items A,B and D, I want the text part of the combobox to show "A, B, D"
<ComboBox ItemsSource="{Binding ListOfItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Width="20" />
<TextBlock Text="{Binding DisplayName}" Width="110" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Thanks
You could use a ContentControl with a Style that changes the ContentTemplate property for the selected item. The following sample markup should give you the idea.
<ComboBox ItemsSource="{Binding ListOfItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- the template for the items in the dropdown list -->
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Width="20" />
<TextBlock Text="{Binding DisplayName}" Width="110" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- the template for the selected item-->
<DataTemplate>
<ItemsControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=ComboBox}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" Margin="0 0 5 0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Please refer to the following similar question for more information.
Can I use a different Template for the selected item in a WPF ComboBox than for the items in the dropdown part?

Hiding null elements in databound ObserveableCollection

I have multiple ObservableCollection<T>s that are bound to a TreeView using a HierarchicalDataTemplate similar to the implementation found in How to mix databound and static levels in a TreeView?.
My problem is one (or more) of the elements in the collections can be null on occasion but they are still displayed in the TreeView as a blank line, such as in this example:
Other than removing them from the collection, is there a method to hiding these elements so they will not appear in the User Interface until they are changed to a non-null value?
If the structure of the XAML is relavant, this is roughly what I am doing:
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:FolderNode}"
ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Path=DisplayName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ParentItem}">
<HierarchicalDataTemplate.ItemsSource>
<MultiBinding>
<MultiBinding.Converter>
<local:MultiCollectionConverter />
</MultiBinding.Converter>
<Binding Path="Children1" />
<Binding Path="Children2" />
</MultiBinding>
</HierarchicalDataTemplate.ItemsSource>
<TextBlock Text="{Binding Path=DisplayName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ChildItemWithChildCollection}"
ItemsSource="{Binding}">
<TextBlock Text="{Binding Path=DisplayName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ChildItemWithChild}"
ItemsSource="{Binding Path=GrandChildren}">
<TextBlock Text="{Binding Path=DisplayName}" FontWeight="Bold" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:GrandChildItem}">
<TextBlock x:Name="Item" Text="{Binding Path=DisplayName}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ChildItemWithoutChildCollection}">
<TextBlock Text="{Binding Path=DisplayName}" />
</DataTemplate>
</TreeView.Resources>
If it's whole collection item that is null you can collapse TreeViewItem when DataContext is null
<TreeView>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>

Sorting Datagrid with DataGridTemplateColumn

I am using a DataGrid and the ItemSource is Bound to a list of strings.
The problem is that the sorting is not working.
The header is enabled and can be clicked but the data is not sorted.
<DataGrid ItemsSource="{Binding CollectionNames}" SelectedItem="{Binding CurrentName}" SelectionUnit="FullRow" CanUserAddRows="False" AutoGenerateColumns="False" SelectionMode="Single" >
<DataGrid.Columns>
<DataGridTemplateColumn Width="400" CanUserSort="True" SortMemberPath="Name">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="Name" Foreground="#FF40A4E0" HorizontalAlignment="Left" VerticalAlignment="Center"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding}"/>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
I think the problem is SortMemberPath="Name" but I dont know what to put instead of "Name"
Simply you can set SortMemberPath=".". This usage is similar when you set the Binding's Path to ".", which means the whole item will be bound, in this case the whole item will be used as the input value for the sorter.

Binding a checkbox in a datagrid header

I have a datagrid where the first column contains a checkbox to let the user selects specific rows. I have added a checkbox in the datagrid column header to check or uncheck all the rows.
Is it possible to add this functionality only with binding in XAML (no checked event).
<sdk:DataGrid AutoGenerateColumns="False" Grid.Row="1" Name="grid" ItemsSource="{Binding myCollection, Mode=TwoWay}" >
<sdk:DataGrid.Columns>
<sdk:DataGridCheckBoxColumn Binding="{Binding myCollection.UserSelected, Mode=TwoWay}" >
<sdk:DataGridCheckBoxColumn.HeaderStyle>
<Style TargetType="sdk:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox x:Name="checkAll" IsThreeState="True"
IsChecked="{Binding myCollection.UserSelected, Mode=TwoWay, Converter={StaticResource threeStateConverter}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</sdk:DataGridCheckBoxColumn.HeaderStyle>
</sdk:DataGridCheckBoxColumn>
<sdk:DataGridTextColumn Binding="{Binding Description}" Header="Chemin" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
I think there's something wrong with the "myCollection.UserSelected" part. ThreeStateConverter is a value converter that would return null when some items are selected, true when they are all selected, etc. but the Convert method is never called (even though the PropertyChanged event is raised when UserSelected is changed).
Any idea on how I can do it? Thank you.
Probably, you've solved the problem already, but nevertheless:
<navigation:Page.Resources>
<model:MyModel x:Key="Model"/>
</navigation:Page.Resources>
...
<data:DataGridTemplateColumn Width="Auto" >
<data:DataGridTemplateColumn.HeaderStyle>
<Style TargetType="datap:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsThreeState="True" Margin="2,0,-13,0" DataContext="{StaticResource Model}" IsChecked="{Binding Path=AllChecked, Mode=TwoWay}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGridTemplateColumn.HeaderStyle>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
MyModel class contains bool? property that implements logic of selection. It is also used as a `DataContext of page.
I must admit that this potentially has problems if we need to change model.
EDIT: I found another way:
<navigation:Page.DataContext>
<model:MyModel />
</navigation:Page.DataContext>
...
<data:DataGridTemplateColumn Width="Auto" >
<data:DataGridTemplateColumn.HeaderStyle>
<Style TargetType="datap:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsThreeState="True" Margin="2,0,-13,0" IsChecked="{Binding Path=DataContext.AllChecked, ElementName=LayoutRoot, Mode=TwoWay}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGridTemplateColumn.HeaderStyle>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Here we bind to DataContext of root layout element (usually called LayoutRoot) which inherits its data context from page by default.

Resources