I am writing a UWP app with a listview. The listviewitems contain a textblock and a checkbox. When the listviewitem is selected, I would like the checkbox to check/uncheck. I would also like to remove the "selected" animation, where the listviewitem turns blue when it is selected.
I have found different solutions, but they all seem to rely on the use of Triggers, which Visual Studio tells me is not available in UWP.
How can I solve this, without triggers in UWP?
My listview:
<ListView Name="ListViewItems" Grid.Row="2">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0,0,0,1"></Setter>
<Setter Property="Background" Value="White"></Setter>
<Setter Property="Padding" Value="5"></Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Padding="5,0">
<CheckBox HorizontalAlignment="Right" VerticalAlignment="Center" Name="CheckBoxItem"></CheckBox>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Name="TextblockItem" Text="{Binding}"></TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When the listviewitem is selected, I would like the checkbox to check/uncheck.
You can directly binding the IsChecked property to the CheckBox to the ListViewItem IsSelected property:
<CheckBox
HorizontalAlignment="Right"
Margin="10"
VerticalAlignment="Center"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}}"
Name="CheckBoxItem">
</CheckBox>
Whenever the IsSelected Property of ListViewItem change, the CheckBox will be checked/unchecked.
I would also like to remove the "selected" animation, where the
listviewitem turns blue when it is selected.
The code below can help you achieve this, BUT, it overrides the Template of the Item, which means that you have to write your own template.
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If your ItemSource of the ListView is bound to a collection on a ViewModel, I would suggest you add a bool to the item class of the collection.
So for example if you are showing Persons, add a bool IsSelected field to the Person class.
You can then use this field to bind it to the IsChecked prop of the checkbox in your view, only thing needed to do is be sure to set the Mode=TwoWay
Last thing left to do is toggle this IsSelected field, you do this by binding the SelectedItem of the ListView to a Person property on your ViewModel and each time this is being called ( setter of the property ), toggle the given IsSelected field.
To override the change in colour when an item is selected of the ListView, you need to get a copy of the ListView template and delete the colour setting in the selected state
Related
I have a Style in Window.Resources applied to all the ComboBox in the application. However, I have come across a situation where I need the styles in the Window.Resources applied to the ComboBox, but I need to disable one feature I have specifically set in the styles.
I want to be able to enter text into one ComboBox in the application.
Being that the ComboBox text editable part is actually a TextBox I set disabled the TextBox altogether: This style is specifically for the ComboBox style I have,
<Style x:Key="ComboBoxTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled" Value="False" />
<....More setters etc
I have also set in the ComboBox styles IsEditable to false. I had to apply this style to both the TextBox part of the ComboBox to the style work correctly. I have also included the code pointing to the static resource to x:Key="ComboBoxTextBoxStyle" shown above:
<Style TargetType="{x:Type ComboBox}">
<Setter Property="IsEditable" Value="False" />
<...More setter's
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<TextBox Style="{StaticResource ComboBoxTextBoxStyle}"
<...More styles applied here
So now I have a ComboBox that I want to be able to enter text into, so I set the XAML to this,
<ComboBox x:Name="Cmb" IsEditable="True"
ItemsSource="{Binding Source={x:Static viewModels:ShiftManagerViewModel.MyBindingList}}"
SelectedItem="{Binding UpdateSourceTrigger=PropertyChanged, Path=MySeletcProperty}" />
But, the ComboBox is not able to be able to enter text even though I have set the actuall ComboBox to IsEditable="True".
How do I override the styles to allow this one ComboBox enter text?
You would have to create a new Style which will inherit from that Style that already exists and apply it to that particular combox box, style would be like:
A better apporach is that your TextBox element in Style Template for ComboBox IsEnabled property should have binding to ComboBox IsEditable property :
<Style x:Key="ComboBoxTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled"
Value="{Binding Path=IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" />
and then in xaml consuming side you can specify this style :
<ComboBox x:Name="Cmb" IsEditable="True"
ItemsSource="{Binding Source={x:Static viewModels:ShiftManagerViewModel.MyBindingList}}"
SelectedItem="{Binding UpdateSourceTrigger=PropertyChanged, Path=MySeletcProperty}" />
I have a ListView and each item has a toggle button. I want the to be able to toggle the button when the item is selected from the list, and untoggle when the item is deselected. It has to follow mvvm, so no code behind.
Here is my setup:
<ListView x:Name="stampList"
ItemsSource="{Binding AllStampImages}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="0,2,0,0">
<ToggleButton Width="72"
Height="72"
Command="{Binding StampSelectedCommand}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My problem is, when I hit the toggle button the item is not selected. Likewise, when I hit outside the toggle button (still within the boundaries of the listView item), the item is selected but the button is not toggled.
How do I tie the two together?
You might have to bind the Selector.IsSelected property to you custome property. In this case to property of you toggle button.
Try something like:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Selector.IsSelected" Value="{Binding [Value of your toggle button], Mode=TwoWay}" />
</Style>
</ListView.ItemContainerStyle>
The cleanest way to do that is to have a collection of items provided by ViewModel or Model layer.
Each item should have a IsSelected boolean property :
class LineItem
{
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; }
}
private String label;
public String Label
{
get { return label; }
set { label = value; }
}
}
There should be some binding to the model IsSelected property :
2.1 On the ListView.ItemTemplate
<ListView x:Name="list1" SelectionMode="Multiple" ItemContainerStyle="{DynamicResource ListViewItemStyle1}" Margin="0,0,334,0">
<ListView.ItemTemplate>
<DataTemplate>
<ToggleButton Margin="5" Content="{Binding Label}" IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListView.ItemTemplate>
2.2 On the ListViewItem Template
Create a Template with right clicking on the listview,
In the menu : "Edit additional templates/Edit generated Item container (Empty).
Fill with following code :"
<Style x:Key="ListViewItemStyle1" TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid x:Name="Bd">
<ContentPresenter />
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="#FF204080"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The trigger is based on Model IsSelected property.
The Grid is named Bd in order to change its background in the trigger.
Link to a full working demo : http://1drv.ms/1iyvPgt
Note
I trie to answer closely to the question .
But in my humble opinion, I would not use a ListView May be you have good reasons I gnore.
I 'd prefer to use an ItemsControl that is a listbox that doesn't allow to make a selection.
I'd put Toggle button, with custom Template to put a blue background if needed.
It would remove all the trigger stuff
, ...
Best coding
I implemented a column in a data grid that containes comboboxes. In order to display a text box in stead of a combobox when a list containes only one value, I used the solution from this post:
How to hide combobox toggle button if there is only one item?
However, when that one value in the list is changed, it is not updated in the text box. I have, of course, implemented INotifyPropertyChanged and it works as long as I have more than one item in the list (in other words, when the combobox is shown) but the value in the TextBlock is never updated.
Edit:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="CList" ItemsSource="{Binding Values, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="0" BorderBrush="Transparent"
Background="Transparent">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}" >
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Items.Count, ElementName=CList}" Value="1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="{Binding Items[0], ElementName=CList}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
I can see you are binding to the item itself, instead of any property in it
so perhaps you many need to bind to the respective property of your data item
eg
<TextBlock Text="{Binding Items[0].MyProperty, ElementName=CList}" />
assuming your intended property is MyProperty
Note if there is no underlying property then you'll have to remove the item and add new one again to list in order to update the text block, in this scenario INotifyPropertyChanged will also not work
I am currently working in C# WPF 4.5 and am attempting to create an interactive grid. Given the grid cell square size (width = height), number of columns and number of rows, I'd like to dynamically generate the grid on the screen.
For the generated grid, I'd like the grid lines to be visible. I'm thinking this could be emulated by just drawing a border within each cell if nothing else. On top of all this, I need an event to be able to determine what cell the user clicks on (i.e., on click return gird column and row) with the ability to select one or more cell at a time.
Any help or ideas on how I could create something like this or is there a better way to go about it? Thanks in advance!
Edit:
So here's some progress I've made:
Created a custom checkbox:
<Style x:Key="styleCustomCheckBox" TargetType="{x:Type CheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid x:Name="Background" Background="Transparent">
<Rectangle x:Name="fillCheckBox"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="fillCheckBox" Property="Fill" Value="Transparent"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="fillCheckBox" Property="Fill" Value="Red"/>
<Setter TargetName="fillCheckBox" Property="Opacity" Value=".3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And then just populate a grid with the custom checkboxes of that style:
<CheckBox Grid.Column="1" Grid.Row="1" Style="{StaticResource styleCustomCheckBox}" />
So now I'd like to map each of checkboxes within the grid where I can gather whether the checkbox is checked or not and what grid row and column it is in, how would I go about this?
I would use an ItemsControl with its ItemsPanelTemplate set to a UniformGrid, and it's ItemTemplate set to your CheckBox
It would be easiest if you could bind your ItemsControl.ItemsSource to a collection of data objects in your code behind. For example:
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="5" Columns="5" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ItemTemplate -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<CheckBox Style="{StaticResource styleCustomCheckBox}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you needed to specify the Row or Column index of the checked item, you could add that to your data item and use a regular Grid instead of a UniformGrid, and apply an ItemContainerStyle like this:
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
To determine the item clicked on, that depends on how you structure your data object and what you actually want to do in your click.
If you wanted the data item behind the square, you could just use the DataContext, such as
void MyCheckBox_Clicked(object sender, EventArgs e)
{
var selectedItem = ((CheckBox)sender).DataContext;
}
Or if you had something like a data item with an IsChecked property like this:
<CheckBox IsChecked="{Binding IsChecked}"
Style="{StaticResource styleCustomCheckBox}" />
You could easily attach an event to the PropertyChanged event for the data item's IsChecked property, and run some code there.
As a third alternative you could use a Button for your ItemTemplate and pass the selected item as the CommandParameter
<Border BorderBrush="Red" BorderThickness="1">
<Button CommandParameter="{Binding }" ... />
</Border>
I can't give you an definite answer because I don't know your code base and what you're trying to do, but I hope this gives you enough information to give you a rough start and point you in the right direction for your situation.
I have the following XAML/Control Template for a ListViewItem:
<Window.Resources>
<ControlTemplate x:Key="ItemTemplate" TargetType="ListViewItem">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="0,5,0,5" BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
<Style TargetType="ListViewItem">
<Setter Property="Template" Value="{StaticResource ItemTemplate}" />
</Style>
<DataTemplate x:Key="ItemDataTemplate">
<RadioButton
x:Name="radiobutton" GroupName="LeGroup"
Content="{Binding Path=Name}" Checked="radiobutton_Checked" />
</DataTemplate>
</Window.Resources>
AND when I try to set the SelectionChanged to fire, it ONLY fires when a radio button is checked with a Mouse RIGHT Click. I need it to fire when it is a NORMAL Left Click.
<ListView x:Name="radioListView" SelectionMode="Single" ItemsSource="{Binding}" ItemTemplate="{StaticResource ItemDataTemplate}" SelectionChanged="radioListView_SelectionChanged" Loaded="radioListView_Loaded"></ListView>
Can't seem to find anything referring to forcing a selectionchanged event on a right click. Standard is a left click.
Thank you in advance :)
EDIT:
So I noticed that the SelectionChanged event fires when I Click OUTSIDE of the radio button (and its associated text) on Left Click. It's as if the radio button is working independently from the listview. The SelectionChanged event fires on a Right Click INSIDE the radio button element. Weird, still trying to figure it out.
I'm not sure why the Left mouse button doesn't work, but here is the style I use for displaying a ListBox with RadioButtons. It works fine with both Right and Left mouse clicks
<Style x:Key="ListBoxAsRadioButtonsStyle" TargetType="{x:Type ListBox}">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Cycle" />
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2, 2, 2, 0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent">
<RadioButton IsHitTestVisible="False" Focusable="false"
Content="{TemplateBinding ContentPresenter.Content}"
IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
It is used like
<ListBox ItemsSource="{Binding MyItems}"
Style="{StaticResource ListBoxAsRadioButtonsStyle}" />
If I had to guess at your problem, I would guess that the regular ListView Selection behavior is marking the LeftMouseClick event as Handled when you click on an item
Well thats strange! Selection takes place on Right click???
All I can suggest you is to take an eventless approach ...
You could use the MVVM pattern by binding SelectedItem / SelectedValue properties etc.
Also you could harness single selection mode of ListView to automatically "group" radio buttons so that ONLY one is selected at time
e.g. radiobutton's IsChecked bound to ancestor ListViewItem's IsSelected property, two way. This way when a radio button is checked it would automatically select that list view row and SelectedItem\Value binding would fire automatically. And being in single selection mode of ListView, next radio button click would automatically de-select previous row.