I'm attempting to enable a TreeView control to support multi select.
The very basic flow works, if you select multiple items in the TreeView while holding down ctrl or shift then it will successfully add those items to a list I have in the view model.
The problem is that when actually clicking on the TreeView items it will only select one visually i.e. only one item is marked as selected. How can I make it highlight/mark multiple items? I don't understand where this is controlled.
The TreeView xaml:
<TreeView x:Name="availableColumnsTreeView"
AutomationProperties.AutomationId="availableColumnsTreeView"
x:Uid="availableColumnsTreeView"
SelectedItemChanged="availableColumnsTreeView_SelectedItemChanged"
ItemsSource="{Binding Path=TreeFieldData, Mode=OneWay, Converter={StaticResource SortingConverter}, ConverterParameter='DisplayName.Text'}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Row="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate x:Uid="HierarchicalDataTemplate_1" ItemsSource="{Binding Path=Children, Mode=OneWay, Converter={StaticResource SortingConverter}, ConverterParameter='DisplayName.Text'}">
<TextBlock x:Uid="TextBlock_1" Text="{Binding DisplayName.Text, Mode=OneWay}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
So "availableColumnsTreeView_SelectedItemChanged" is invoked fine, but I need it to actually highlight the selected items.
EDIT: Please read my question before marking it as a duplicate. I tried to be as specific as possible to what my problem is. I'm not looking for a whole solution for multi select hidden away in some one drive document.
I'm not sure if I 100% follow you. Could provide a small example please?
Sure.
Here is xaml:
<TreeView ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children, Mode=OneWay}">
<CheckBox Content="{Binding Text, Mode=OneWay}" IsChecked="{Binding IsSelected}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<DataTrigger.Setters>
<Setter Property="Foreground" Value="Red" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And here is cs:
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Text { get; set; }
public List<Item> Children { get; set; }
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
}
}
public Item(string text)
{
Text = text;
}
}
public partial class MainWindow : Window
{
public List<Item> Items { get; set; } = new List<Item>
{
new Item("1") { Children = new List<Item>
{
new Item("11"),
new Item("12"),
new Item("13"),
}},
new Item("2") { Children = new List<Item>
{
new Item("11"),
new Item("12"),
new Item("13"),
}},
new Item("3"),
};
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
I am using CheckBox to select item (no idea how you do it). If item is selected it has its foreground changed to red via data trigger.
As you can see selection (disregards how you implement it, really, I am using single selection TreeView) is stored inside items as IsSelected value. You can traverse hierarchical collection to get a list of selected items (this is called flattering).
Note: IPropertyChanged, it's required if you plan to set IsSelected from code-behind (e.g. select all items on button press).
It should be easy to adapt to your case.
How can I make it highlight/mark multiple items? I don't understand where this is controlled.
You define the appearance of a TreeViewItem container using a TreeViewItem style. If you add an "IsSelected" property to your data object that keeps track of whether the item is currently selected, you could use a DataTrigger that binds to this one and provide the highlighting, e.g.:
<TreeView>
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.Resources>
...
</TreeView>
Make sure that the data class where the "IsSelected" property is defined implements the INotifyPropertyChanged interface and that you set this property in your event handler or command.
Related
I have a ListBox, as such:
<ListBox
//other stuff
ItemsSource="{Binding ViewModels, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedThing, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Purple" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
ViewModel.cs:
public SomeType SelectedThing
{
get => selectedThing;
set => //set with INotifyPropertyChanged
}
public ObservableCollection<SomeType> ViewModels
{
get => viewModels;
set => //set with INotifyPropertyChanged
}
It's possible that SelectedThing is defined in the ViewModel when loading the app, so I want it to be styled accordingly.
It works great when I open the app and then click on an item, but how can the style be applied on load?
I tried with:
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource Mode=Self}}" Value="True">
<Setter Property="Background" Value="Purple" />
</DataTrigger>
</Style.Triggers>
But every item of the ListBox is enabled, so it applies it to every item upon load.
EDIT:
After debugging a bit, I found out that when setting SelectedThing on load, SelectedItem remains null.
EDIT:
Here is the OnLoaded method in the ViewModel, where I am setting SelectedThing if the user has selected it while previously using the app. The purpose is to keep the selection after closing and reopening the app.
public IAsyncRelayCommand OnLoadedCommand { get; set; }
In the constructor:
OnLoadedCommand = new AsyncRelayCommand(OnLoaded);
In the View:
<b:Interaction.Triggers>
<b:EventTrigger EventName="Loaded">
<b:InvokeCommandAction Command="{Binding OnLoadedCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
The actual method:
public async Task OnLoaded()
{
//other stuff
if (App.Current.Properties.Contains(nameof(SelectedThing)))
{
var selected = JsonConvert.DeserializeObject<SomeType>(App.Current.Properties[nameof(SelectedThing)].ToString());
SelectedThing = selected;
}
}
The reason why there is no item selected is because you are setting the SelectedThing source property to a value that's not in the ViewModels source collection.
As you have already discovered, this works better:
var selectedViewModel = ViewModels.Where(x => x.SelectedThing == selected.SelectedThing).FirstOrDefault();
SelectedThing = selectedViewModel;
The item to be selected must be present in the source collection.
I have a ComboBox containing four int values, 1, 2, 3, 4. Below that, I have a GroupBox. What I'm trying to do is display unique content in the GroupBox based on the values of the ComboBox; i.e. maybe a TextBlock or TextBox for 1 and a Button for 2.
Currently, I have four separate GroupBoxes all on top of each other and have the Visibility property of the those GroupBoxes bound to unique bool properties in the ViewModel which I set whenever the SelectedItem of the ComboBox changes. But I feel like there's a way where I can condense this and just keep one GroupBox and fill it with the correct content instead of having four separate ones and four separate IsVisible properties.
<ComboBox ItemsSource="{Binding PackageTypes}" SelectedItem="{Binding SelectedType}" />
<GroupBox /><!--Change content of this based on combobox's selection-->
public class MainViewModel : ViewModelBase
{
private PackageModel_selectedPackageModel;
public PackageModel SelectedPackageModel
{
get => _selectedPackageModel;
set
{
_selectedPackageModel = value;
}
}
public ObservableCollection<int> PackageTypes { get; set; }
public int SelectedType { get; set; }
public MainViewModel()
{
PackageTypes = new ObservableCollection<int>() { 1, 2, 3, 4 };
}
}
You may use a Style with triggers, e.g.:
<ComboBox x:Name="cmb" xmlns:s="clr-namespace:System;assembly=mscorlib">
<s:Int32>1</s:Int32>
<s:Int32>2</s:Int32>
<s:Int32>3</s:Int32>
<s:Int32>4</s:Int32>
</ComboBox>
<GroupBox Header="...">
<GroupBox.Style>
<Style TargetType="GroupBox">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=cmb}" Value="1">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="1..." />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedItem, ElementName=cmb}" Value="2">
<Setter Property="Content">
<Setter.Value>
<Button Content="2..." />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</GroupBox.Style>
</GroupBox>
Instead of binding to the SelectedItem property of the ComboBox, you may of course bind to a source property of the view model:
<DataTrigger Binding="{Binding SelectedType}" Value="1">
Make sure that the view model implements the INotifyPropertyChanged interface and raises change notifications.
I have TreeView control and I need to bind property from root (window/usercontrol) DataContext in context menu in that treeview.
<TextBox Text="{Binding Header}"></TextBox>
<TreeView ItemsSource="{Binding Items}" Grid.Row="1">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{{ BINDING TO HEADER PROPERTY FROM WINDOW DATACONTEXT}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
public ObservableCollection<string> Items { get; set; }
public string Header { get { return _header; } set { _header = value; } }
I've tried multiple things: I've added x:Name="WindowRoot" to Window and {Binding Header, ElementName=WindowRoot} but it didn't work, I've tried multiple FindAncestor and RelativeSource but it didn't work.
Can someone help me?
Edit:
This is simplified case, in my normal application I use Unity + Prism, so ViewModel is AutoDiscovered (prism:ViewModelLocator.AutoWireViewModel="True") and it generally works. By "works" I mean: TreeView shows items from my collection, so it is connected, the problem is with context menu binding only.
In this simplified example I have ugly and simple code-behind, because I only want to test this ContextMenu binding:
public partial class MainWindow : Window
{
public ObservableCollection<string> Items { get; set; }
private string _header = "testtest";
public string Header { get { return _header ; } set { _header = value; } }
public MainWindow()
{
Items = new ObservableCollection<string>();
Items.Add("ItemTest");
InitializeComponent();
this.DataContext = this;
}
}
You could bind the Tag property of the TreeViewItem to the parent window or user control using a {RelativeSource} and then bind the Header property of the MenuItem to the Tag property of the PlacementTarget of the ContextMenu:
<TreeView x:Name="tv" ItemsSource="{Binding Items}" Grid.Row="1">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=Window}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{Binding PlacementTarget.Tag.DataContext.Header, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
The reason you cannot bind directly to any property of the window from the MenuItem is that a ContextMenu resides in a different element tree than the parent window or user control.
I want my program to automatically select the most recently created object when it's added to the list. Here's the current code which doesn't work:
public static void CreateResourceTree(TreeView selector,IEnumerable<Resource> resources,Resource onRefresh=null)
{
ActiveResources.Clear();
ActiveResources.AddRange(resources.OrderByDescending(x=>x.FetchMeta("Priority").IntValue)
.ThenBy(x=>x.Label)
.ToList());
selector.Items.Refresh();
selector.SelectedItem=onRefresh;
}
The code is meant to set the value to the value of onRefresh Unfortunately, SelectedItem as ReadOnly, which results in an error.
When I Googled the issue, the only solution I found to programatically selecting an item is to use the "IsSelected" property on a TreeViewItem, but since this is data-bound, it doesn't use or recognize TreeViewItem objects.
Here is the XAML code, in case it's needed:
<TreeView ItemsSource="{Binding ResourceList}" Grid.Column="0" Grid.Row="3" x:Name="ResourcesTree" SelectedItemChanged="ResourcesTree_OnSelectedItemChanged" Margin="0,4,0,0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type xmlclass:Resource}" ItemsSource="{Binding Children}">
<ContentPresenter Content="{Binding Output}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Wrap your Resource in some sort of ViewModel or just create a sub class with an IsSelected property.
e.g.
public class SelectableResource : Resource
{
public bool IsSelected { get; set; }
}
Then create a style to bind the TreeViewItem.IsSelected to your new property:
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.Resources>
You can now set the selected items from the data side, rather than trying to control the view manually.
public static void CreateResourceTree(TreeView selector,IEnumerable<SelectableResource> resources,SelectableResource onRefresh=null)
{
ActiveResources.Clear();
ActiveResources.AddRange(resources.OrderByDescending(x=>x.FetchMeta("Priority").IntValue)
.ThenBy(x=>x.Label)
.ToList());
onRefresh.IsSelected = true;
}
When the visibility of a CarViewControl is set to collapsed, it still shows a placeholder where it used to be (see screenshot below).
Is there any way to completely hide a ListViewItem when it is Collapsed?
XAML Code
<ScrollViewer>
<ListView ItemsSource="{Binding CarVM.UserCars}" ShowsScrollingPlaceholders="False">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<ctrl:CarViewControl Car="{Binding}" Visibility="{Binding HideCar, Converter={ThemeResource InverseVisConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
In the image above, there are three CarViewControls which are collapsed, followed by one which is not. One is highlighted. I want them to be completely invisible when the content is collapsed.
What I've tried:
Setting height of the DataTemplate control to 0 (just to see if it hides the placeholder which had no effect
Setting ShowsScrollingPlaceholders to False based on this documentation: MSDN ListView Placeholders
Reason For Collapse Requirement
Within each CarViewControl, a WebView exists which includes a security token (which maintains that the WebView is logged into a specific web site). If you try to pass the WebView by reference, due to what I can only assume are security measures, you lose that security token and must re-login to the site. That is why adding/removing the control from the ObservableCollection will not work in my case.
I would say your design is flawed, but I can't fix that; so I'll provide a "workaround."
The issue is that your DataTemplate is collapsing, which is great, but clearly the container it is in doesn't collapse. This won't happen inherently because the parent won't inherit from a child. First realization is every item is wrapped in a ListViewItem and you can observe that from setting your ItemContainerStyle. This leaves you with two solutions (workarounds). You can either set up some triggers on your ListViewItem or you can do something easier like I did--and if you don't mind the UI affects.
My full working application is below. The main point is that you have to edit the layout/behavior of the ListViewItem. In my example, the default values aren't BorderThickeness and Padding isn't "0, 0, 0, 0"... Setting those to 0 will get hide your items completely.
MainWindow.xaml
<Window x:Class="CollapsingListViewItemContainers.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CollapsingListViewItemContainers"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
</Window.Resources>
<Grid>
<StackPanel>
<Button Content="Disappear Car 3" Click="Button_Click" />
<ListView ItemsSource="{Binding Cars}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="MinHeight" Value="0" />
<Setter Property="Padding" Value="0 0 0 0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type local:Car}">
<Grid Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisConverter}}">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Title}" />
<TextBlock Text="{Binding Id}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace CollapsingListViewItemContainers
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Car> _cars = new ObservableCollection<Car>
{
new Car("Not yours", "Mine", 1),
new Car("Not mine", "Yours", 2),
new Car("Not ours", "His", 3),
new Car("Not ours", "Hers", 4),
};
public ObservableCollection<Car> Cars
{
get
{
return _cars;
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Cars[2].IsVisible = !Cars[2].IsVisible;
}
}
public class Car : INotifyPropertyChanged
{
private bool _isVisible;
public bool IsVisible
{
get
{
return _isVisible;
}
set
{
_isVisible = value;
NotifyPropertyChanged("IsVisible");
}
}
public string Name
{
get; set;
}
public string Title
{
get; set;
}
public int Id
{
get; set;
}
public Car(string name, string title, int id)
{
Name = name;
Title = title;
Id = id;
IsVisible = true;
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Edit
Let's be honest, the above was a pretty cheap solution and I wasn't satisfied with it after thinking about it for another 3 minutes. The reason I'm dissatisfied is because you can still select the item if you have access to a keyboard. In the example above, click the first item, "hide" the item(s), then use your mouse and the ListView.SelectedItem will still change.
So below is a quick solution (workaround :D ) to actually remove the item from the list and preventing them from getting focus. Replace the ListView.ItemContainerStyle with this one and change the ActualHeight trigger value to match the values you're seeing. This will change based on OS themes I believe--I'll leave it up to you to test. Lastly, remember the ListViewItem.DataContext is going to be that of an item in the ItemsSource. This means the DataTrigger bound to IsVisible is bound to the Car.IsVisible property.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Style.Triggers>
<Trigger Property="ActualHeight" Value="4">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
<DataTrigger Binding="{Binding IsVisible}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
Edit 2 (edited before I even posted the first edit.
Screw it, don't bind visibility of your CarViewControl; You don't need to. You solely need to focus on removing the item itself, and once the item is removed, the containing controls will be removed as well (though you should test this yourself and change IsTabStop and IsFocusable if you can still tab to items in CarViewControl). Also, since using an arbitrary number with the ActualHeight binding isn't very safe, just binding straight to the IsVisible property (or HideCar in your case) and triggering visibility of the ListViewItem should be sufficient.
Finally, here is my final XAML:
<Window x:Class="CollapsingListViewItemContainers.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CollapsingListViewItemContainers"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
</Window.Resources>
<Grid>
<StackPanel>
<Button Content="Disappear Car 3" Click="Button_Click" />
<ListView ItemsSource="{Binding Cars}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding IsVisible}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type local:Car}">
<Grid>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Title}" />
<TextBlock Text="{Binding Id}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
</Window>
Instead of attempting to hide item via the UI, I would remove the item from the View or Collection that the ItemsSource is bound against. It's a cleaner approach, and the UI never has to be concerned about Visibility of an item.
EDIT 1
When a user selects a specific car from the quick select menu, it shows that car and hides the others.
Makes sense; let's call that view "UserCars", and assume the ItemsSource is bound to UserCars.
How about this: change the ItemsSource to be bound to "SelectedCollection". When you want to show UserCars, point SelectedCollection to the UserCars collection.
To limit the set, you'd simply point SelectedCollection to a new SingleCar that you populate with only UserCars.SelectedItem
XAML:
<ListView ItemsSource="{Binding SelectedCollection}" SelectedItem="{Binding SelectedCar}">
ViewModel:
private Car _selectedCar;
public Car SelectedCar {
get { return _selectedCar; }
set { _selectedCar = value; OnPropertyChanged("SelectedCar"); }
}
private ObservableCollection<Car> _selectedCollection = CarVM.UserCars;
private ObservableCollection<Car> SelectedCollection {
get { return _selectedCollection; }
set { _selectedCollection = value; OnPropertyChanged("SelectedCollection"); }
}
private ObservableCollection<Car> _originalCollectionForReferenceKeepingOnly;
// called via RelayCommand
public void UserJustSelectedACar()
{
ObservableCollection<Car> singleItemCollection = new ObservableCollection<Car>();
singleItemCollection.Add(SelectedCar);
_originalCollectionForReferenceKeepingOnly = SelectedCollection;
SelectedCollection = singleItemCollection;
}
public void ReturnToFullUsedCarsList()
{
SelectedCollection = _originalCollectionForReferenceKeepingOnly;
}
EDIT 2
It appears that you are trying to make a ListView pretend to be a "Car Details" view by hiding these other items. This is inherently a bad idea; a different UI element bound to the Listview's Selected Car Item would make for a better solution. Since the new detail panel would just be looking at an already-generated Car instance, you wouldn't incur any data hit. Even if you can make this approach work right now, I'm worried you're going to just cause yourself more grief in the future.