ComboBoxItem Will Not Collapse - wpf

I wrote a basic visibility converter such that when the property "Active" is true the ComboBoxItem should be Visible, Collapsed otherwise. It currently displays the Active ones properly, the inactive ones Text are invisible but the item can still be seen.
http://snag.gy/Mh2Xq.jpg
May I ask how do I get the ComboBoxItem to collapse the comboboxitem that are inactive properly please.
<ComboBox Grid.Row="1" Grid.Column="2" SelectedItem="{Binding Product, Mode=TwoWay}" ItemsSource="{Binding Products}" VerticalContentAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="{Binding Active, Converter={StaticResource VisibilityConverter }}"></Setter>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Apply the visibility converter to the parent stackpanel instead. Like so:
<StackPanel Orientation="Horizontal" Visibility="{Binding Active, Converter={StaticResource VisibilityConverter}}">
...
</StackPanel>

<ComboBox.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="Visibility" Value="{Binding Active, Converter={StaticResource VisibilityConverter}}" />
</Style>
</ComboBox.Resources>

You should filter the bound list on basis of IsActive
Try to Bind comboBox to
Products.Where(t => t.IsActive)

Related

ComboBox of CheckBoxes not checking item when row clicked

I've got a combo box of checkboxes that allow the user to select multiple temperature bands. When the users clicks on the checkbox, or the text directly next to the checkbox, the boxes are checked correctly, and my combobox text updates to add/remove the selected temperature. However if I check in the area of my combobox drop down to the right of the text, the checkbox isn't toggled, and instead I get namespace text in my combo box.
Here's my XAML
<ComboBox x:Name="cbDegrees" ItemsSource="{Binding m_DegreeValues}" Text="Degrees" IsEditable="True" IsReadOnly="True" Grid.Row="0" Grid.Column="0" Margin="5" Background="Gray" >
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="chkDegrees" Content="{Binding Name}" Checked="Degrees_CheckBox_Click" Unchecked="Degrees_CheckBox_Click" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
</CheckBox>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I would like to have the checkbox toggled when I click anywhere in the line item for the temperature.
The default ComboBoxItem that is generated is not set to stretch and fill all available space. You can see this if you wrap your ComboBox in something like a DockPanel with it's Background property set.
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel Background="CornflowerBlue">
<CheckBox Name="chkDegrees" Content="{Binding Name}" .. />
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
To fix it, set your ComboBoxItem style so it sets the HorizontalContentAlignment to Stretch
<ComboBox.Resources>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ComboBox.Resources>
You could define an ItemContainerStyle that makes the CheckBox stretch horizontally:
<ComboBox x:Name="cbDegrees" ItemsSource="{Binding m_DegreeValues}" Text="Degrees" IsEditable="True" IsReadOnly="True" Grid.Row="0" Grid.Column="0" Margin="5" Background="Gray" >
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="chkDegrees" Content="{Binding Name}" Checked="Degrees_CheckBox_Click" Unchecked="Degrees_CheckBox_Click" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
</CheckBox>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

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?

Something Like For Loop in XAML

I have a Resource Dictionary in which I want to have a common DataTemplate for ComboBox.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate DataType="{x:Type ComboBox}">
<StackPanel Orientation="Horizontal">
<!--Here I need to use something like For Loop-->
<TextBlock Text=""></TextBlock>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
Now I have created a dependency property of type integer named NoOfColumns. While declaring the comboBox I need to set the NoOfColumns property to automatically generate that number of columns. I want them to databind.
Update as requested by Joe
<ComboBox x:Name="cbUnder" ItemsSource="{Binding GroupsAndCorrespondingEffects}"
IsEditable="True" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}"
Text="{Binding InputValue, UpdateSourceTrigger=PropertyChanged}" TextSearch.TextPath="GroupName"
Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="3">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type vm:GroupAndCorrespondingEffect}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" Width="250">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="{Binding CorrespondingEffect}" />
</StackPanel>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
There's nothing like for in XAML, but ItemsControl is very much like foreach. Instead of setting an int property, make an ObservableCollection<T> and add that many objects to it, and then bind the ItemsControl to your collection property.
This has the added benefit that each collection item can expose properties to be bound, e.g. if you wanted to display different text in each TextBlock, you could put a property on your collection item and bind the TextBlock to that property.

Change DataTemplate style in ListBoxItem if the item is selected

I have a listbox with an expander inside the ItemTemplate. I managed to bind the expander's IsExpanded property to the ListBoxItem's IsSelected property ok. Now I want to apply a style to the ListBoxItem's content also bound to the IsSelected property.
<ListBox.ItemTemplate>
<DataTemplate>
<Border Name="myBorder">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Description}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Date:"/>
<TextBlock Text="{Binding Date}"/>
</StackPanel>
<dx:DXExpander Name="expanderDetails"
IsExpanded="{Binding Mode=TwoWay, Path=IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Count:"/>
<TextBlock Text="{Binding Count}"/>
</StackPanel>
</dx:DXExpander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
What I want to do is somehow set the style of the "myBorder" Border to "NotSelectedBorderStyle" for unselected ListBoxItems, and to "SelectedBorderStyle" for the SelectedItem (ListBox with single selection).
FYI, the styles define background, border and that kind of stuff, just to make the clear which item is selected, nothing fancy in these styles.
I tried the accepted answer here but if I completely switch styles I loose the nice expanding animation my DXExpander has.
I guess there must be some solution using triggers, but I can't just hit the right spot.
Finally I got it, I'm posting it here in the hope that this will save someone else time and pain :-P
This code does some additional things: the EventSetter and the corresponding Handler method are there to capture clicks to the elements inside the DataTemplate, in order to select the ListBoxItem which contains the element (if you don't you might type text inside an item, while a different one is selected).
The inner Border ("myBorder") is just a container for the stackpanels, I had to wrap everything inside another border ("backgroundBorder") which gets the style changed when the ListBoxItem gets selected.
<Style x:Key="FocusedContainer" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="LightGray"/>
<EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="backgroundBorder" Width="Auto" Style="{StaticResource NotSelectedBorderStyle}">
<ContentPresenter Content="{TemplateBinding Content}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Border Name="myBorder">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Description}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Date:"/>
<TextBlock Text="{Binding Date}"/>
</StackPanel>
<dx:DXExpander Name="expanderDetails"
IsExpanded="{Binding Mode=TwoWay, Path=IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Count:"/>
<TextBlock Text="{Binding Count}"/>
</StackPanel>
</dx:DXExpander>
</StackPanel>
</Border>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="backgroundBorder" Property="Style" Value="{StaticResource SelectedBorderStyle}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then I set the ItemContainerStyle in my ListBox to the above style:
<ListBox Background="#7FFFFFFF" HorizontalContentAlignment="Stretch"
ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True"
ItemContainerStyle="{StaticResource FocusedContainer}"/>
To finish, the code behind for the GotKeyBoardFocus handler:
private void OnListBoxItemContainerFocused(object sender, RoutedEventArgs e)
{
(sender as ListBoxItem).IsSelected = true;
}
A mess in code, but pretty neat in the UI. Hope this helps someone!

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