CollectionViewSource unselect selectedItem when clicking a group name - wpf

I have a listbox that has its itemSource bound to a collectionViewSource that is grouped and has 2 levels of groupings over the actual items:
<ListBox ItemsSource="{Binding Source={StaticResource myCVS}}" ItemTemplate="{StaticResource myItemsTemplate}" ItemContainerStyle="{StaticResource myItemsStyle}" SelectedItem="{Binding SelectedListItem}" >
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource HeaderStyle}" />
<GroupStyle ContainerStyle="{StaticResource SubHeaderStyle}" />
</ListBox.GroupStyle>
</ListBox>
With a CollectionViewSource bound to an ObservabeleCollection:
<CollectionViewSource x:Key="myCVS" Source="{Binding Path=myItemsToGroup}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="HeaderName" />
<PropertyGroupDescription PropertyName="SubHeaderName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
the items in the ObservalbleCollection look like:
public class Items
{
public string GroupName;
public string SubGroupName;
public string ItemName;
}
This all works great i end up with:
Header1
|_SubHeader1
|_item1
|_item2
Header2
|_SubHeader2
|_item1
|_item2
The problem is if i click an item it becomes selected, and stays selected if I click on a header or subheader. If a header is clicked I would like to set the SelectedItem to null. I am using a command to remove the SelectedItem from the UI, but i don't want the command to execute if a header or subheader is being clicked only when a item is being clicked.

GroupStyles are not selectable, so of course your view model won't see a selection change happen.
To work around this, you can use some code behind. What you'll notice is if you click on the items in the ListBox, then ListBoxItem will set the MouseUp event's Handled property to true. If you click anywhere else on the ListBox, nothing handles the event. With that being said, you can set your selected item based on the state of Handled.
XAML:
<ListBox ItemsSource="{Binding Source={StaticResource myCVS}}"
ItemTemplate="{StaticResource myItemsTemplate}"
ItemContainerStyle="{StaticResource myItemsStyle}"
SelectedItem="{Binding SelectedListItem}"
MouseLeftButtonUp="ListBox_MouseLeftButtonUp">
Code-behind:
private void ListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if(!e.Handled)
{
var lb = sender as ListBox;
lb.SelectedItem = null;
}
}
Addendum:
Clicking on an already selected item will set the SelectedItem to null. To prevent that, do this: instead of using MouseLeftButtonUp use MouseDown:
<ListBox ItemsSource="{Binding Source={StaticResource myCVS}}"
SelectedItem="{Binding SelectedListItem}"
MouseDown="ListBox_MouseLeftButtonUp">
Here is the state of my current application (GroupStyle's) don't get drawn properly, but the implementation is what's important here. If this doesn't do it for you, I would implement a pure MVVM approach.

Related

how to remove custome items from observablecollection in silverlight

i binded one observable cllection to one listbox in silverlight.when i click one item in listbox and click delete button ,how to remove that particular item remove from the listbox without linq using mvvm.i passed commandparameter of the button is listbox itemid.
<ListBox ItemsSource="{Binding School1,Mode=TwoWay}" DisplayMemberPath="SchoolName" Name="listBox1" >
<Button Content="Delete" Command="{Binding deletecommand}" CommandParameter="{Binding Path=SelectedItem.ID,ElementName=listBox1}" Name="button2" />
so what is the code for remove particular item from observable collection
public void delete(object parameter)
{
School1.Remove(...)
}
Bind the ListBox's SelectedItem to a property and use that in your Remove():
<ListBox ItemsSource="{Binding School1, Mode=TwoWay}"
DisplayMemberPath="SchoolName"
SelectedItem={Binding SelectedSchool}
Name="listBox1"
/>
public void delete(object parameter)
{
if (SelectedSchool != null)
School1.Remove(SelectedSchool);
}
Also note that your question is somewhat of a duplicate: Clearing selecteditem of listbox (which is bound to collection of objects) with MVVM

WPF DataTemplate and Binding

I am DataTemplating a listbox's itemsource to display a label and combobox. In the, datatemplate I am assigning a new itemssource to the combobox but cant get it to work.
Or Ideally, how can I bind the combobox in a datatemplate to a different source.
Thanks. Mani
UserControl:
<DockPanel>
<ListBox x:Name="lstBox" ItemsSource="{Binding FilterControls}" />
</DockPanel>
<!--DataTemplate For SearchElement Type-->
<DataTemplate DataType="{x:Type CustomTypes:FilterElement}">
<Label> Text </Label>
*<ComboBox ItemsSource="{Binding Employees}"DisplayMemberPath="Sex" />*
</DataTemplate>
ViewModel:
List<FilterElement> FilterControls;
List<Employee> Employees
Class FilterElement
{
string Caption;
}
class Employee
{
string Sex;
}
In your combobox, you are binding to Employees in the current data context, which would be a FilterElement object - no Employees property to bind to.
In your binding, you probably want to set Source= to something else, which overrides your datacontext
There are lots of ways to set this one easy way to do this (easy to put here, anyway) would be to add a collectionViewSource to the resources of your window/control (I put Whatever.Resources - it can go in nearly any containing element)
<Whatever.Resources>
<CollectionViewSource x:Key="employeeSource" Source="{Binding Employees}">
</Whatever.Resources>
Then in your datatemplate
<ComboBox ItemsSource={Binding Source={StaticResource employeeSource}}" ... />
Note that using a CollectionViewSource will allow you to do sort/group in xaml as well.

WPF: ComboBoxes in ListBox and concurrency

I have code like this:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>Some Other Stuff Here</TextBlock>
<ComboBox ItemsSource="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is, every time the outside ListBox.SelectedItem gets changed, the ComboBoxes inside it would change their SelectedIndex to -1. Which means if I click "Some Other Stuff Here" (unless the ListBoxItem it is in is selected), all the comboboxes' selection get cleared.
How do I overcome this? Thx!
Presumably your combobox is bound to something like an ObservableCollection - try exposing an instance of ICollectionView instead:
class DataSource
{
// ...
public ObservableCollection<string> MyData { get; private set; }
public ICollectionView MyDataView
{
get
{
return CollectionViewSource.GetDefaultView(this.MyData);
}
}
}
You can then bind your combobox with:
<ComboBox ItemsSource="{Binding MyDataView}" IsSynchronizedWithCurrentItem="True" />
This means that the 'selected item' for each data source is stored in the ICollectionView object instead of within the combobox, which should mean that it is persisted when the ListBox SelectedItem changes

How to bind a TextBox to plain string collection

As a part of large data model I need to display/edit a string collection defined like ObservableCollection<String>. In prototype app we use a list view to display entire of collection and a text box to edit selected element. The text box should be bound to the current element of the collection. Because GUI is subject to change I can't bind directly using <TextBox Text="{Binding SelectedItem,ElementName=listView}" />.
I tried to use
<TextBox Text="{Binding Path=/, UpdateSourceTrigger=PropertyChanged}"/>
but it works only in one direction, changing listview current item causes updating a text box but not otherwise.
How can I bind a text box directly to sting instance of current element in string collection?
Ok, so here's your ListView. I'm going to add a name to it so I can reference it elsewhere in the XAML:
<ListView
x:Name=stringList
ItemsSource="{Binding}"
SelectionMode="Single"
IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn
Header="Data Item"
Width="80"
DisplayMemberBinding="{Binding}"/>
</GridView>
</ListView.View>
</ListView>
Now in your TextBox over on the right you can bind directly to the ListView:
<TextBox Text="{Binding SelectedItem,ElementName=stringList}" />
Since your ListView is bound directly to a list of strings, SelectedItem will be the string the currently-selected ListViewItem points to.
Update
Since you're not allowed to use ElementBinding, your best bet is to introduce a ViewModel class to sit between your list and your window. Define it like this:
public class StringListViewModel : INotifyPropertyChanged
{
// you'll have to implement INotifyPropertyChanged - I won't
// do that here - do a quick search to learn how it works.
public ObservableCollection<String> List { get; set; }
private object _si;
public object SelectedItem
{
get { return _si; }
set
{
_si = value;
OnPropertyChanged("SelectedItem");
}
}
}
Now set your window's DataContext to an instance of your ViewModel class instead of pointing it directly to the string list. Bind your ListView's ItemsSource and SelectedItem to it like this:
<ListView ItemsSource="{Binding List}" SelectedItem="{Binding SelectedItem}" ... />
Now bind your TextBox to the SelectedItem of your ViewModel:
<TextBox Text="{Binding SelectedItem}" />
Now your list sets the SelectedItem on the ViewModel whenever it changes, and thus your TextBox reflects that value. Hope that makes sense.

WPF: TreeView inside a ComboBox

I'm trying to put a TreeView inside a ComboBox in WPF so that when the combo box is dropped, instead of a flat list the user gets a hierarchical list and whatever node they select becomes the selected value of the ComboBox.
I've searched quite a bit for how to accomplish this but the best I could find was only peices of potential soltuions that, because I'm ridiculously new to WPF, I couldn't make work.
I have enough knowledge of WPF and databinding that I can get my data into the treeview and I can even get the treeview inside of the combo box, however what I've been able to accomplish doesn't behave properly at all. I've attached a screenshot to show what I mean. In the screenshot the combo box is "open", so the treeview on the bottom is where I can select a node and the treeview "on top" is being drawn on top of the combobox where I want the text/value of the selected node in the tree to be displayed.
Basically what I don't know how to do is how do I get the treeview's currrently selected node to return its value back up to the combobox which then uses it as its selected value?
Here is the xaml code I'm currently using:
<ComboBox Grid.Row="0" Grid.Column="1" VerticalAlignment="Top">
<ComboBoxItem>
<TreeView ItemsSource="{Binding Children}" x:Name="TheTree">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type Core:LookupGroupItem}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Display}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</ComboBoxItem>
</ComboBox>
Screenshot:
For those who still need this control, I've implemented a WPF version of my Silverlight control. It works only with view models and requires these view models to implement a special interface, but apart of this it's not difficult to use.
In WPF it looks like this:
You can download source code and sample application from here: WpfComboboxTreeview.zip
I had the same issue.
The easiest way to implement the behavior of a treeview in a combobox is to create a TextBox and stylize it to look like a combobox. Add an image next to it. The trick is to put the treeview in a popup control. Then, when the user clicks the textbox or the dropdown image you chose, the popup is displayed directly under the textbox.
Then, when the treeview item is selected, close the popup and place the text of the selected now in the textbox.
Here's an unstylized example:
XAML:
<Window x:Class="ComboBoxTreeView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" MouseEnter="Window_MouseEnter">
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" x:Name="header" Width="300" Height="30" PreviewMouseDown="header_PreviewMouseDown" HorizontalAlignment="Left" />
<Popup Grid.Row="1" x:Name="PopupTest" AllowsTransparency="True" IsOpen="False">
<TreeView x:Name="Tree1" Initialized="Tree1_Initialized" SelectedItemChanged="Tree1_SelectedItemChanged">
<TreeViewItem Header="Test1" x:Name="Tree1Item1">
<TreeViewItem Header="1test1" />
<TreeViewItem Header="2test2" />
</TreeViewItem>
<TreeViewItem Header="Test2" />
</TreeView>
</Popup>
</Grid>
</Window>
And here is the Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ComboBoxTreeView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_MouseEnter(object sender, MouseEventArgs e)
{
}
private void Tree1_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var trv = sender as TreeView;
var trvItem = trv.SelectedItem as TreeViewItem;
if (trvItem.Items.Count != 0) return;
header.Text = trvItem.Header.ToString();
PopupTest.IsOpen = false;
}
private void Tree1_Initialized(object sender, EventArgs e)
{
var trv = sender as TreeView;
var trvItem = new TreeViewItem() { Header="Initialized item"};
var trvItemSel = trv.Items[1] as TreeViewItem;
trvItemSel.Items.Add(trvItem);
}
private void header_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
PopupTest.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;
PopupTest.VerticalOffset = header.Height;
PopupTest.StaysOpen = true;
PopupTest.Height = Tree1.Height;
PopupTest.Width = header.Width;
PopupTest.IsOpen = true;
}
}
}
You might be able to use an event handler on the tree view to set the SelectedItem on the comboBox.
In order to do this you would need to set the Tag porperty of the tree view like so:
<TreeView Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}" MouseDoubleClick="treeview_MouseDoubleClick" ItemsSource="{Binding Children}" x:Name="TheTree">
Now in the DoubleClick event you can get at the ComboBox:
private void treeview_MouseDoubleClick(object sender, RoutedEventArgs e)
{
try
{
TreeView tv = sender as TreeView;
if(tv == null)
return;
var cB = tv.Tag as ComboBox;
cB.SelectedItem = tv.SelectedItem;
}
catch (Exception e)
{
}
}
You will also need to override the way the comboBox Item is selecte, otherwise the whole TreeView will be selected as soon as you click on it.
This question is actually closely related to that one
So you would probably find this implementation helpful. This is a combobox with checkboxes inside, but you can get the idea on how to decouple the text in the box from the popup content with your tree.
It also demonstrates the idea that the IsSelected property should be on your model entities and then it is bound back to the checkbox Text property through the model. In other words, what you show in the combobox collapsed might be completely unrelated to the content... Well, maybe not completely, but in my app when a user selects several checkboxes in that combo I can show comma-separated in the top textbox, or I can show "Several options selected", or whatever.
HTH =)
It's an old topic but it can be useful to somebody.
Trying to do something similar with a combobox, I tried to use popup instead and it's working.
To turn it into a nice feature it needs a lot of tweaking.
<Expander Header="TestCS">
<Popup IsOpen="{Binding IsExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}}">
<TreeView ItemsSource="{Binding CSTree.CSChildren}">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding CSChildren}" DataType="{x:Type ViewModel:ObservableCS}">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="16" Text="{Binding CSName}"></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Popup>
</Expander>
I think you can foreach treeViewItems then add into combo 1by1.
and in each treeviewitem expand event, append its children into combobox.
however, set expandable item's height to looks like in one row, such as Height = 18d.
// == Append Item into combobox =================
TreeViewItem root = new TreeViewItem();
root.Header = "item 1";
TreeViewItem t1 = new TreeViewItem();
t1.Header = "Expanding...";
root.Items.Add(t1);
// ==============================================
// == root expandind event ==============================
root.Height = 18.00d;
TreeViewItem[] items = GetRootChildren(root.Tag);
foreach(TreeViewItem item in items)
{
combox1.Items.Add(item);
}
// ======================================================
There are two problems that you are having. The first is that when you select your one-and-only ComboBoxItem (the whole TreeView), that is what is returned into the ContentPresenter of the ComboBox's base ToggleButton. Simply making your ComboBox IsEditable will stop the whole TreeView from being put into the Content of the ComboBox but it still isn't selecting the item you chose in the TreeView. You'll have to use the SelectedItemChanged event in the TreeView to capture the selected item and then convert that into your 'SelectedItem'. Once you've selected the item and passed it to the ComboBox, set IsDropDownOpen to false.

Resources