Bind CheckBox to SelectedItem in ListBox - c#

I have created a ListBox in WPF, which does the following:
It binds to a DataSource in my ViewModel. The ListBox contains Items and next to each item is a CheckBox. If the user clicks on a CheckBox, the Item is being selected and the Command will be executed. It works fine and the code for WPF looks like this:
<ListBox ItemsSource="{Binding OCAntwort}" SelectedItem="{Binding SelectedAntwort}" >
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--ElementName=Window is the Name of the XAML (in root definition)-->
<CheckBox DataContext="{Binding DataContext, ElementName=Window}" Command="{Binding CheckAnswer}" CommandParameter="{Binding ElementName=cbox, Path=IsChecked}" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" x:Name="cbox" HorizontalAlignment="Right"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
Now I'm trying the same on Windows Store App, but there's no AncestorType... If I'm doing it like this:
<Style TargetType="ListBoxItem" x:Name="lbitem">
<CheckBox DataContext="{Binding DataContext, ElementName=Window}" Command="{Binding CheckAnswer}" CommandParameter="{Binding ElementName=cbox, Path=IsChecked}" IsChecked="{Binding ElementName=lbitem, Path=IsSelected}" x:Name="cbox" HorizontalAlignment="Left"/>
The Checkbox disappears and it'll just display the ItemsSource of OCAntwort. Clicking on an item won't execute the Command of the CheckBox (since there is no CheckBox).
Doing it like this:
IsChecked="{Binding ElementName=lbitem, Path=IsSelected,RelativeSource={RelativeSource Mode=Self}}"
would display the CheckBox and execute the Command, but it's not being binded to the SelectedItem.
How can I bind the CheckBox (which is being displayed next to the ListBoxItem for each ListBoxItem) to the SelectedItem of a ListBox? Working code is above... I want that the code should work in my Windows Store App (and later in my Windows Phone App).
Example:
You can download a sample here: http://weblogs.asp.net/marianor/archive/2008/02/04/multiselect-listview-with-checkboxes-in-wpf.aspx
This shows what I want to achieve in Windows Store App.
Edit: Doing it like this displays the ListBox with a content and a CheckBox, but I still need to bind the CheckBox by the SelectedItem. Means when the user clicks on a CheckBox, the ListBoxItem has to be selected or if the user clicks on a ListBoxItem, the CheckBox has to be selected.
<ListBox ItemsSource="{Binding OCAntwort}" SelectedItem="{Binding SelectedAntwort}" Grid.Row="2" Grid.Column="1" x:Name="listBox" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Text}" Width="150" VerticalAlignment="Center"/>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Not sure if you've found the solution yet but I have used the following code to establish two way binding between the IsChecked property of a CheckBox and the IsSelected property of a parent ListBoxItem or ListViewItem.
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"/>

You should use TemplatedParent as you are writing inside the ListBoxItem.
IsChecked="{Binding ElementName=lbitem,
Path=IsSelected,
RelativeSource={RelativeSource Mode=TemplatedParent}}"
I think this may resolve your problem.

Related

ListBox.ItemTemplate with long text does not match ListBox width

I'm trying to put a StackPanel inside a ListBox and this inside a Grid that is a part of a UserControl.
So far I have come up with the following XAML but I cannot fix the width. When you take a look at the screenshot you'll see that the StackPanel is few pixels too wide. I tried HorizontalAlignment=Stretch on every element but on the Width={Binding...} manages to limt the width, although not perfectly. What do I miss?
<UserControl x:Class="Reusable.Apps.ExceptionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Reusable.Apps"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Width="{Binding (Window.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<UserControl.Resources>
<local:ExceptionVisualizerData x:Key="DesignViewModel" />
<Style TargetType="TextBlock" x:Key="NameStyle">
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontFamily" Value="Consolas"/>
<Setter Property="Foreground" Value="DarkMagenta"/>
</Style>
<Style TargetType="TextBlock" x:Key="MessageStyle">
<Setter Property="FontSize" Value="16"></Setter>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</UserControl.Resources>
<Grid Width="{Binding (UserControl.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListBox ItemsSource="{Binding Exceptions}" d:DataContext="{Binding Source={StaticResource DesignViewModel}}" Width="{Binding (Grid.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="{Binding (ListBox.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}">
<TextBlock Text="{Binding Name}" Style="{StaticResource NameStyle}" />
<TextBlock Text="{Binding Message}" Style="{StaticResource MessageStyle}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
It sounds like you should get rid of all the width bindings and just put a margin around your StackPanel, e.g:
<StackPanel Margin="10" >
--- UPDATED --
Frustrating is the right word. Okay, here's the solution.
(1) Replace StackPanel with Grid.
(2) In the ListBox, set ScrollViewer.HorizontalScrollBarVisibility to "Disabled"
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<Grid Margin="10" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Title" Style="{StaticResource NameStyle}" />
<TextBlock Grid.Row="1" TextWrapping="Wrap" Text="lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text lots of text " Style="{StaticResource MessageStyle}" />
</Grid>
</ListBox>
This should do the trick.
I finally got it! I found a similar question and adopted its answer to my needs:
<Grid >
<ListBox ItemsSource="{Binding Exceptions}" d:DataContext="{Binding Source={StaticResource DesignViewModel}}" Margin="30" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Width" Value="{Binding (Grid.ActualWidth), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" Style="{StaticResource NameStyle}" />
<TextBlock Text="{Binding Message}" Style="{StaticResource MessageStyle}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
The trick was to bind the Width of the ListBoxItem to the parent. Holy cow! This was tricky. Now it also nicely scales when resizing the window.
Instead of referring to ListBox, refer width of ListBoxItem
<StackPanel Width="{Binding (ListBoxItem.Width), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
The reason for the overlap is due to the default template for a ListBoxItem, which has Padding set to "4,1" and a BorderThickness of "1" on both itself and the inner Border object. You can use a tool such as WPF Snoop to see this.
The solution is to bind to the ActualWidth of the next level of container, in this case the ContentPresenter.
<ListBox ItemsSource="{Binding DataItems}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Yellow"
Width="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContentPresenter}},
Path=ActualWidth}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Id}" />
<TextBlock Grid.Row="1" Text="{Binding Description}" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note the properties set on the ListBox, which prevent the content growing beyond the width of the ListBox, and forcing the content to resize as the ListBox width is increased.

WPF - Tooltip content binded to a combobox selected item

I have a datagrid which has, in the head of every column a combobox. I want a tooltip to appear when hovering the combobox, to show the selected item value
<DataGrid HeadersVisibility="Column" Name="griglia" Grid.Row="2" ItemsSource="{Binding Path=Test}" AutoGenerateColumns="True" IsReadOnly="True" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate DataType="DataGridColumnHeader">
<ComboBox ItemContainerStyle="{StaticResource SingleSelectionComboBoxItem}" DisplayMemberPath="Oggetto" Width="100" Height="20" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},Path=DataContext.Selezione, UpdateSourceTrigger=LostFocus}" SelectionChanged="SingleSelectionComboBox_SelectionChanged"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Content = "what should i put here????"/>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
What should i put inside the content property of the tooltip? I already tried
Content = "{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridColumnHeader}},Path=DataContext.Tot.Oggetto}"
where Tot.Oggetto is a string containing the displayed item in the combobox but it doesn't work
EDIT: I tried already to set tooltip property fo the combobox like
ToolTip="{Binding Path=SelectedItem.ToolTip, RelativeSource={RelativeSource Self}}"
but it does not show any tooltip
Your first try is setting the Tooltip of the DataGridColumnHeader, but it seems you want to set the Tooltip of the ComboBox, right?
You can set the Tooltip of a ComboBox to show its selected value as follows:
<ComboBox ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=SelectedValue}" ...>
RelativeSource={RelativeSource Self} is binding to the ComboBox itself. Then Path=SelectedValue specifies that you want to bind to the property SelectedValue of this ComboBox, instead of binding directly to itself.
So your code would then be: (I erased the code that was setting the Tooltip of the DataGridColumnHeader and modified the code for the ComboBox inside the DataTemplate)
<DataGrid HeadersVisibility="Column" Name="griglia" Grid.Row="2" ItemsSource="{Binding Path=Test}" AutoGenerateColumns="True" IsReadOnly="True" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate DataType="DataGridColumnHeader">
<ComboBox ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=SelectedValue}" ItemContainerStyle="{StaticResource SingleSelectionComboBoxItem}" DisplayMemberPath="Oggetto" Width="100" Height="20" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},Path=DataContext.Selezione, UpdateSourceTrigger=LostFocus}" SelectionChanged="SingleSelectionComboBox_SelectionChanged"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>

Not able to display checkboxes for ListViewItems in WPF

I want to display checkboxes alongside text in ListView. I have multiple ListViews in my WPF window, hence I want to specify this behavior as a style in resources. But the checkboxes are not displayed, only the text is. So please, find below my code and suggest edits.
<Window.Resources>
<Style TargetType="ListView" x:Key="ListViewTemplate">
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="BorderBrush" Value="Transparent"></Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}" Content=""></CheckBox>
<Separator Width="5"></Separator>
<TextBlock Text="{Binding Text}"></TextBlock>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListView Grid.Column="1" Grid.Row="3" Grid.ColumnSpan="8" Grid.RowSpan="5" x:Name="listViewDocTypes" Style="{DynamicResource ListViewTemplate}" >
</ListView>
</Grid>
Provide a Text or Width to the Checkbox. Without setting the Text property the length will automatically be "0" if you do not specify its Width.
<CheckBox Content="xyz" Width="1234"/>
The problem was that I was binding data the wrong way.I was using the following code.
ListViewItem item = new ListViewItem();
item.Tag = docType;
item.Text = docType.Text;
listViewDocTypes.Items.Add(item);
The correct way to bind is
listViewDocTypes.ItemsSource = docTypes;

(WPF) How to change button image background in listBox with buttons in each line ?

i have listbox, and the data template is button :
<ListBox x:Name="lbname" ItemsSource="{Binding myCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button x:Name="btnname" Content="{Binding name}" Click="btnname_Click">
<Button.Background>
<ImageBrush ImageSource="/myApplication;component/images/buttons/normal.png"/>
</Button.Background>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
i have two image backgroud for this listBox image (normal.png for normal mode, click.png for selected item in listBox)
The listBox view items in list of buttons, the button image background is normally normal.png
my question is how to change the button image background to click.png for selected one and the old selected button retrieve to normal.png
How to change image background for selected item in listBox with buttons in each line ?
hope this clear, please i spend about one day about this issue
can any one help
Thanks
I haven't tested it, but you need some code that looks like this:
<ListBox x:Name="lbname" ItemsSource="{Binding myCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button x:Name="btnname" Click="btnname_Click">
<Grid>
<Image>
<Image.Style>
<Style>
<Setter Property="Image.Source"
Value="/myApplication;component/images/buttons/normal.png" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"
Value="True">
<Setter Property="Image.Source"
Value="/myApplication;component/images/buttons/click.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Text="{Binding name}" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The idea is to bind directly to the IsSelected property of the ListBoxItem object. This is done using a RelativeSource binding. However, I'm guessing that this code doesn't do what you want... I suggest that you might want to use a ToggleButton instead... something like this:
<ListBox x:Name="lbname" ItemsSource="{Binding myCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton x:Name="btnname" Click="btnname_Click" IsChecked="{Binding
IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type
ListBoxItem}}}">
<Grid>
<Image>
<Image.Style>
<Style>
<Setter Property="Image.Source"
Value="/myApplication;component/images/buttons/normal.png" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"
Value="True">
<Setter Property="Image.Source"
Value="/myApplication;component/images/buttons/click.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Text="{Binding name}" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
if you are using MVVM then make a property in viewModel and bind this to ImageBrush as ImageSource,
when you are selecting value in ListView then get selected record and change image path of this property.
Try using Togglebutton instead of button.Use a trigger to check when the IsChecked property of the toggleButton changes. And based on that change your image.

ToggleButton Checked Event Handling

I have ToggleButtons that are dynamically created based on my datasource. I would like to have only one togglebutton checked at a time when a user clicks one. How can I accomplish this?
<UserControl.Resources>
<ItemsPanelTemplate x:Key="HorizontalMiniDrawerList">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
<DataTemplate x:Key="MiniDrawerRowTemplate">
<ToggleButton x:Name="_MiniDrawerButton" Width="60" Height="85" Style="{DynamicResource MiniDrawerButtonWhite}" Checked="_MiniDrawerButton_Checked" >
</ToggleButton>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Width}" Value="3">
<Setter TargetName="_MiniDrawerButton" Property="Width" Value="185"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="MiniDrawerListItemTemplate">
<ListBox SelectionMode="Multiple" Background="#00000000" BorderThickness="0" Width="500"
ItemsPanel="{StaticResource HorizontalMiniDrawerList}"
ItemTemplate="{StaticResource MiniDrawerRowTemplate}"
ItemsSource="{Binding Row}" >
</ListBox>
</DataTemplate>
</UserControl.Resources>
<Grid Background="{DynamicResource ListBackgroundColor}" >
<ListBox x:Name="_MiniDrawerRows" BorderThickness="0" Background="Transparent" Margin="107,84,225,217" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Path=MiniDrawerRows, diagnostics:PresentationTraceSources.TraceLevel=High}"
ItemTemplate="{StaticResource MiniDrawerListItemTemplate}" >
</ListBox>
</Grid>
Update: Instead of using a togglebutton I used a radiobutton and changed the style of the radio button to look like a togglebutton.
<Style x:Key="MiniDrawerButtonWhiteRadioToToggleButton" BasedOn="{StaticResource {x:Type ToggleButton}}" TargetType="{x:Type RadioButton}">
I assume you mean "only one" instead of "only when". In that case you can use RadioButton (which is derived from ToggleButton) instead and set a GroupName on _MiniDrawerButton in your ItemTemplate. It looks like you are probably already using a custom ControlTemplate so you can use the same one for RadioButton by just changing the Style and ControlTemplate TargetTypes.

Categories