How do I use a Trigger to synchronize ListBox selections? - c#

I have two list boxes which are bound to same observable collection. Basically one list box is a document tray where I drag & drop documents and other is a regular list box where I give the option to edit the file name. I want to highlight the items in both the listboxes. For example when the user selects a document in one listbox (tray) I want the list item with textbox in the other to be highlighted and likewise when I click on the text box in the other list box I want the item in the tray to be selected. I have my code as below.
In the Document Tray list box I have code as below.
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</Style>
</ListBox.ItemContainerStyle>
Similarly in the other list box I have the style like this
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="IsSelected" Value="True"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
When I select an item in the document tray the item is getting selected in the other list box. When I click on the text box or select an item in other list box the item is not getting selected in the tray list box. Also I noticed if I comment the focus trigger the items are getting selected appropriately. I want the selection to be propagated on focus also.

I am assuming you are binding your ListBox to data in code-behind (view model or otherwise) via a property, e.g. YourItems:
<ListBox ItemsSource="{Binding YourItems}">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
</ListBox>
Create another property of the type that's within the collection in your code-behind, also (e.g. called YourSelectedItem. Then just add the following line inside BOTH of your Listbox definitions:
<ListBox ItemsSource="{Binding YourItems}"
SelectedItem="{Binding YourSelectedItem, Mode=TwoWay}">
As long as both are bound two-way, then they should stay in-sync with each other.
I would not use setters in the style to set IsSelected (discard the lines <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />). The ListBoxes themselves have properties to handle that if you can bind to a common item in code-behind as shown above. I'd need to try it, but then hopefully your trigger would work also since you won't have competing IsSelected triggers/styling.
I think the key thing here is that I've found that sometimes you cannot set something via trigger IF you've also set the same property via a style, so use SelectedItem on the ListBox parents, instead of an IsSelected style on each item.

Related

TreeView - Refresh the treeView and expanding the last selected - WPF

I have a treeview that need to refresh due to data changes. Is it
possible to refresh and select the last node that they were viewing when the tree view was been populated once again?
Thanks for any help.
For auto refresh you can create some ViewModel for treeView. Something like in my answer Binding a WPF TreeView to multiple Lists . To update selected item and expand it you should define in ViewModel IsSelected and IsExpanded properties (like Name property, but bool). And you should define ItemContainerStyle in your TreeView like this.
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</TreeView.ItemContainerStyle>

how to write ParentListBox.SelectedIndex in xaml?

I have a ListBox having ComboBoxes as ListItems. The ComboBox in each ListItem is created using ListBox's ItemTemplate.
Now, suppose I have 5 ListItems. i.e. 5 ComboBoxes.
For the 1st ListItem i.e. 1st ComboBox I would like to have all the Items from database as ItemsSource.
For the 2nd ListItem i.e. 1st ComboBox I would like to have all the Items from database as ItemsSource.
For the 3rd ListItem i.e. 1st ComboBox I would like to have only the selected Items from above Comboboxes as ItemsSource.
For the 4th ListItem i.e. 1st ComboBox I would like to have only the selected Items from above Comboboxes as ItemsSource.
For the 5th ListItem i.e. 1st ComboBox I would like to have only the selected Items from above Comboboxes as ItemsSource.
So, I think I have to use different DataSource for different ComboBoxes.
And for that to happen I have the following starting code:
<ComboBox....>
<ComboBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="ParentListBox.SelectedIndex" Value="0">
<Setter Property="DataSource" Value="{Binding Path=ListCorrespondingToValue1}"/>
</DataTrigger>
<DataTrigger Binding="ParentListBox.SelectedIndex" Value="Non-0">
<Setter Property="DataSource" Value="{Binding Path=ListCorrespondingToValue2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ComboBox.Style>
</ComboBox>
Now, my question is what should I use insead of ParentListBox.SelectedIndex in the above code and what should I replace Non-0 with?
You don't use a DataTrigger for the non zero part... you just set it as a Setter in the Style, so that becomes the default value and then the DataTrigger changes the DataSource property only when the value is 0. Try this:
<ComboBox....>
<ComboBox.Style>
<Style>
<Setter Property="DataSource" Value="{Binding
Path=ListCorrespondingToValue2}"/>
<Style.Triggers>
<DataTrigger Binding="ParentListBox.SelectedIndex" Value="0">
<Setter Property="DataSource" Value="{Binding
Path=ListCorrespondingToValue1}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ComboBox.Style>
</ComboBox>

Disable particular item in the ListBox

I bind list of objects to my ListBox in WindowsPhone application. This business object has one boolean property. Based on the Boolean property i need to set the IsEnabled property in the ListBoxItem.
Is there any way to achieve this in WindowsPhone ListBox ?
There're few ways to achieve this:
You can add the ListBoxItem by C# code and set the property properly.
In Xaml, inside the list box, place the following:
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="IsEnabled" Value="{Binding Content.IsEnabled, RelativeSource={RelativeSource Self}}"/>
</Style>
</ListBox.Resources>
Here I assume that the property name in your model object is IsEnabled, change it according to its real name.

Data-bound WPF ComboBox not displaying selected value

I have a ComboBox, bound to a DataTable. The ComboBox displays a list of values, pulled from the "wellId" column of the DataTable. The ComboBox is also styled so that I can insert a custom item into the list simply by adding a dummy row to the DataTable with the wellId field set to "(settings)".
<ComboBox IsEditable="True" Name="comboWell" ItemsSource="{Binding}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Content" Value="{Binding wellId}" />
<Style.Triggers>
<DataTrigger Binding="{Binding wellId}" Value="(settings)">
<Setter Property="Content" Value="Customize..." />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
For the most part, this works great. It shows the list, and all items (including the dummy items) are selectable in the drop-down list.
However, after selecting an item from the list, whether it is a real item or a dummy item, the ComboBox doesn't show the selected item properly. Rather than showing the same value displayed in the drop-down list (the "wellId" column from the DataTable), it instead just displays the string "System.Data.DataRowView". No matter what I select, it always displays the same thing.
If I specifically set the DisplayMemberPath on the ComboBox to "wellId", then it displays the selected item properly. However, this messes up all of the other styling I have applied, resulting in the drop-down list being filled with blank entries.
How do I get the ComboBox to display the selected item properly?
Change your ComboBox to set the ItemTemplate instead of the ItemContainerStyle, and remove IsEditable=True. If IsEditable=True then the SelectedItem will get displayed in a TextBox, and if a TextBox.Text is bound to an item, it will display the .ToString() of that item
<ComboBox Name="comboWell" ItemsSource="{Binding }">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Content" Value="{Binding wellId}" />
<Style.Triggers>
<DataTrigger Binding="{Binding wellId}" Value="(settings)">
<Setter Property="Content" Value="Customize..." />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

WPF Listview empty text

How to show in the WPF Listview using the GridView an empty text (like in ASP.net), e.g. "please select a person" or "0 items founded"?
This XAML will do something similar, it has a visible ListView showing a list and a hidden message and switches visibility when the list is empty using a trigger.
The code below will work with any IList or ICollection but the same technique can be used with any data source, like always, if you want the display to update when you add or remove items you need to use an ObservableCollection or similar.
The ContentPresenter is there because you can only use triggers inside a template or a style, so we put our controls inside a DataTemplate and use the ContentPresenter to show it.
If you want the message to appear inside the ListView than all you have to do is remove the Setter that hides the ListView and add some margin to the TextBlock to position it where the first item in the ListVIew should be.
<ContentPresenter Content="{Binding}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Grid>
<ListView Name="list" ItemsSource="{Binding MyList}"/>
<TextBlock Name="empty" Text="No items found" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding MyList.Count}" Value="0">
<Setter TargetName="list" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="empty" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
Bind it to a DataSource + Property that returns the text you want ?
Slot in a dummy object whose String representation is the text you want..

Categories