How to select first list box item by default? - silverlight

I'm using ObservableCollection to bind data into list box. Is there a way to make first list item to be selected right after data binding? Is there any event I can use ?
Thank you

Right after (or any point after) setting the datacontext for the listbox (or parent object - probably the page), just set the selected index to the first item in the list.
listbox.SelectedIndex = 0;
If you've got a handler for when the selected index is changed then be sure to ignore when you first set the index.

Create a property named IsSelected in the object contained within the ObservableCollection. Bind this to the ListBoxItem's IsSelected property via a TwoWay binding.
Then, in the page's OnLoaded callback (or wherever you're binding the collection to the ListBox), do something like this
foreach( var obj in myCollection ) {
obj.IsSelected = false;
}
if( myCollection.Count > 0 ) {
myCollection[0].IsSelected = true;
}
// bind the collection to the listbox

why won't you try something like
var listBoxItem = ItemContainerGenerator.ContainerFromItem(myList.First());
listBoxItem.Focus();
or
listBoxItem.IsSelected = true;

Related

WPF Datagrid ignores SelectedItem changes if the item is not in the grid

I have an ObservableCollection of "things" in my view model, and a couple filtered subsets of that list in additonal ObservableCollections. I have two DataGrids on the screen, and I have bound them each to one of the subset ObservableCollections.
Both DataGrids have their SelectedItem property bound to a SelectedThing property in the view model.
When I change SelectedThing either programatically or by selecting a row in one of the two grids, it will change as expected. If the item now pointed to by SelectedThing exists in a grid, the grid will update it's selected item.
So here is my problem... if SelectedThing does not exist in the grid's ItemSource, the selection acts like nothing happened and remains in whatever state it was in before SelectedThing was changed. Ideally I would like the selected to Clear if the underlying view model property no longer is set to something in the grid's ItemsSource... anyone have any suggestions?
Ok. Got it working. In case it helps someone else in the future, here's what made it work...
In your code behind, register an event handler for the view model's PropertyChanged event, and then use that to check each grid to see if it contains the item being selected. If not, then clear the selected in that grid. I also modified my SelectedThing property to ignore incoming NULL values to avoid a deadlock (and in my app it will never be NULL after initialization)
_vm is a Property that returns my view model.
_vm.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_vm_PropertyChanged);
void _vm_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedThing")
{
CheckSelection(grid1, _vm.SelectedThing);
CheckSelection(grid2, _vm.SelectedThing);
}
}
void CheckSelection(DataGrid grid, object selectedItem)
{
if (grid.ItemsSource != null)
{
bool itemInGrid = false;
foreach (var item in grid.ItemsSource)
{
if (item == selectedItem)
{
itemInGrid = true;
break;
}
}
if (!itemInGrid) // clear selection
{
grid.SelectedItem = null;
// not sure why, but this causes the highlight to clear. Doesn't work otherwise
grid.IsEnabled = false;
grid.IsEnabled = true;
}
}
}

Unable to find container from item in Treeview

I got an ItemsControl (a Treeview or a TreevieItem) wich Item is filled with my own Model.
I wan't to programatically unfold the TreeView. So I try this:
var model = itemsControl_.Items.Select(i => i as MyModel).Where(ftn => ftn != null && ftn.key = searchedKey));
now that i have founded the model a want to unfold. I search for the container to unfold it:
var tvi = itemsControl_.ItemContainerGenerator.ContainerFromItem(model) as TreeViewItem;
if(tvi!=null)
{
if (!tvi.IsExpanded)
{
tvi.IsExpanded = true;
}
}
And sometimes tvi is null !?!
Can someone explain me how that can be possible ?
Based on your description, it seems you want to expand TreeViewItem.
I think this is most likely caused by the Virtualizating. The Virtualizating Attached Property is on by default for TreeView, which means if an item is not in the viewport, it probably didn't get an container.
If you call GetContainerFromItem on a TreeView, the ItemContainerGenerator searches only the direct child objects of the TreeView. So you need to recursively traverse the TreeView and child TreeViewItem objects. A further complication is that if the TreeView virtualizes its items (you enable virtualization by setting the VirtualizingStackPanel.IsVirtualizing property to true), the child items need to be created before you can check its data object.
Hope this will help.
Pls refer to http://blogs.msdn.com/b/wpfsdk/archive/2010/02/23/finding-an-object-treeviewitem.aspx
Regards
Dipak

How to programmatically select a TabItem in WPF TabControl

I would like to know how to select a specific TabItem in a WPF TabControl.
I tried these bellow but nothing work!
MyTabControl.SelectedIndex = x
MyTabControl.SelectedItem = MyTabItem
MyTabControl.SelectedValue = MyTabItem
MyTabItem.IsSelected = True
As #Chris says, any of the first three things should work and as #Phyxx says, it doesn't always really work. The problem is some subtle thing about the order of property changes. To work around it you need to let the WPF invoke your tab-selection code in its own time:
Dispatcher.BeginInvoke((Action)(() => MyTabControl.SelectedIndex = x));
This does just what Phyxx' timer does, but in a slightly less extreme way.
All your examples except the third one are correct and will work. The problem must be at another location. Maybe you reset the item after setting or your code never is called?
Valid
MyTabControl.SelectedIndex = x
MyTabControl.SelectedItem = MyTabItem
MyTabItem.IsSelected = True
Invalid
MyTabControl.SelectedValue = MyTabItem
Loop through the TabItems and for the tab to be selected, set
tabItem.IsSelected = true
If there are any other place due to binding changing you will see problem. Otherwise, the above code should work.
One thing which hasn't been mentioned above:
The main reason something like this won't work is that the tab items do not have the "Name" property set. Each tab item of the tab control which you want to navigate to programmatically must have its name property set for any of the above code to work.
<tabItem Name="tab1"></tabItem>
I have implemented a small MVVM bindings based solution for selecting tab panels pragmatically.
define a property in your view model - Selected int type
bind the property in your view
<TabControl
x:Name="TabsCandidate"
VerticalAlignment="Stretch"
TabStripPlacement="Top"
SelectedIndex="{Binding Selected}"
private int _selected;
public int Selected
{
get { return _selected; }
set
{
_selected = value;
OnPropertyChanged("Selected");
}
}
Set the value to Select property, simply the binding will activate the tab panel.
if you want to navigate from tab panel inside parent tab panels, this solution will simply works, All you need to do is, access the data context of your control and set it
// set the property value of the view model which points the index of the tab controller.
((CandidateViewModel)((System.Windows.FrameworkElement)candidateTab.Content).DataContext).Selected = CandidateLogTabIndex;
Try to set the MyTabControl.SelectedIndex = x in the event handler of DataContextChanged or Loaded of your UI. Hope this will work.
I tried all the methods that should have worked, but like you nothing actually changed the selected tab. In the end I got it to work by putting the tab selection code in a DispatcherTimer tick.
DispatcherTimer switchTabTimer = new DispatcherTimer();
switchTabTimer.Interval = new TimeSpan(0);
switchTabTimer.Tick += (object timerSender, EventArgs timerE) =>
{
myTabControl.SelectedIndex = 0;
switchTabTimer.Stop();
};
switchTabTimer.Start();
if you don't know the index of the tab (hint its not TabIndex) use:
private async Task ChangeTabTo(TabItem wantedTab) {
int index = 0;
for (var i = 0; i < TabControl.Items.Count; i++) {
var tab = TabControl.Items[i];
var t = tab as TabItem;
if (t == null) continue;
if (t == wantedTab) {
index = i;
break;
}
}
await Dispatcher.BeginInvoke((Action)(() => TabControl.SelectedIndex = index));
}
or modify it to search by name if you don't want to keep a reference to the tab
I'm throwing my 2 cents on the topic, since it might help someone out. I'm using WPF with Prims framework.
I was unable to select a tab by binding to SelectedItem or SelectedIndex - it didn't work. I was also unable to set TabItem.Name value from within TabControl.ItemTemplate or TabControl.ContentTemplate.
Instead I implemented event-based solution:
Add Name value for my TabControl.
Create an event - in Prism that means define a class that derives from PubSubEvent<T> (T is the type of parameter - in my case that was the ViewModel object bound to the TabItem>.
Publish that event whenever I want to a tab to be selected.
Subscribe to the event within my View.cs class and set the TabControl.SelectedItem programmatically using FindName.

WPF ContextMenu with bound items: Items.Count == 0 in ContextMenuOpening event

I have a ContextMenu with the ItemsSource bound to the selected item of a list view, like this:
<ContextMenu ItemsSource="{Binding Path=PlacementTarget.SelectedItem,
RelativeSource={RelativeSource Self}, Converter={StaticResource possibleConverter}}"/>
The possibleConverter enumerates all possible values for a property of the the selected item, which are shown in the context menu. In the Opened event of the context menu, I select the current value like this:
var cm = e.OriginalSource as ContextMenu;
if (cm != null) {
var lv = cm.PlacementTarget as ListView;
var field = lv.SelectedItem as Field;
var item = cm.ItemContainerGenerator.ContainerFromItem(cm.Items.OfType<object>().Where(o => o.ToString().Equals(field.StringValue)).FirstOrDefault()) as MenuItem;
if (item != null) {
item.IsChecked = true;
}
}
Not particularly elegant, but it works. With the debugger I verified that the ContextMenu.Items.Count property has a non-zero value when expected (i.e. cm.Items.Count is non-zero in the if).
So far, so good. There are, however, items in the listview where the context menu will have no items. In this case, an empty menu is shown. I tried to suppress this in the ContextMenuOpening event in the list view, like this:
var lv = sender as ListView;
if (lv != null) {
var cm = lv.ContextMenu;
if ((cm != null) && (cm.Items.Count > 0)) {
// Here we want to check the current item, which is currently done in the Opened event.
} else {
e.Handled = true;
}
}
Seems like it should work. However, cm.Items.Count is always zero. This is true even if ListView.SelectedItem did not change: For an item with menu entries, the menu is shown correctly after the first click, so the data binding has already happend. It is shown correct the second time as well, but in any case, Items.Count is zero in the ContextMenuOpening event.
What am I missing? How can I suppress empty context menus? Why is the count zero in the ContextMenuOpening handler, which is in Windows Forms (ContextMenuStrip.Opening) the canonical point where to do these things?
EDIT: Upon further investigating, it turns out that in the ContextMenuOpening handler, any binding to the listview fails, which is why ItemsSource is null. I tried to bind via ElementName, via a FindAncestor relationship, all to no avail. The PlacementTarget is null during that event. An ugly hack worked though: In the ContextMenuOpening event, I assign the list view to the ContextMenu.Tag property, while the ItemsSource binding now binds to Tag.SelectedItem. This updates the binding, so Items.Count is what it should be. It's still strange. How can you do meaningful things in ContextMenuOpening other than replacing the menu or something, if the binding fails because somehow the context menu is out of context during the event? Was it only tested with static pre-defined menu items?

Programmatically selecting Items/Indexes in a ListBox

In WPF, I'd like to set the selected indexes of a System.Windows.Controls.ListBox
I best way I've found so far is to remove all the items from the control, insert the selected, call SelectAll(), then insert the rest, but this solution neither works in my situation nor is very efficient.
So, how do you set items in a Listbox to be selected, programmatically?
You can set multiple items as selected by using the SelectedItems collection. This isn't by index, but by what you have bound:
foreach (var boundObject in objectsBoundToListBox)
{
ListBox.SelectedItems.Add(boundObject);
}
One way you can do this is to add a Selected field to your data object. Then you need to overide the default listboxitem style and bind the isselected property to the Selected property in your object. Then you just need to go through your data items and update the Selected value.
If you don't implement that Selected property as a dependency property, you need your class to implented the INotifyPropertyChanged interface and raise the propertychanged event when you set the value.
You have to do this:
ListBoxObject.SelectedItem = ListBoxObject.Items.GetItemAt(itemIndex);
Where itemIndex would be the item you want to select.
If you want to select multiple items, you need to use the ListBox.SelectedIndexCollection property.
You can do this for multiple sections:
ListBoxObject.SelectedItems.Add(ListBoxObject.Items.GetItemAt(i));
Where i is the item index.
Thanks to mdm20.
My case was actually checking a CheckBox within the ListBox, and this Dependency Property worked like a charm.
I had to inherit my custom class from DependencyObject and implement the property
public class ProjectListItem : DependencyObject{
public Boolean IsChecked
{
get { return (Boolean)this.GetValue(CheckedProperty); }
set { this.SetValue(CheckedProperty, value); }
}
public static readonly DependencyProperty CheckedProperty =
DependencyProperty.Register("IsChecked", typeof(Boolean), typeof(ProjectListItem),
new PropertyMetadata(false));
}
how to programmatically select multiple items in listbox in wpf
foreach (var boundObject in objectsBoundToListBox)
{
ListBox.SelectedItems.Add(boundObject);
}

Resources