Always select first item from listbox when binding is changed - wpf

There is a ListBox that its ItemsSource is bind to a collection,
we need always selected first item when binding is changed, for example at first ListBox has 3 items, second item is selected from ListBox by user, after that binding is changed and ListBox has 1 items, but second item is selected yet.( and also second item is emty but is not hidden)
<ListBoxItem x:Name="item1">
<Border Margin="0" >
<Image Source="{Binding Selected.List[2].Image,NotifyOnTargetUpdated=True}" ></Image>
</Border>
</ListBoxItem>
<ListBoxItem x:Name="item2">
<Border Margin="0">
<Image Source="{Binding Selected.List[1].Image,NotifyOnTargetUpdated=True}"></Image>
</Border>
</ListBoxItem>
<ListBoxItem x:Name="item3">
<Border Margin="0">
<Image Source="{Binding Selected.List[0].Image,NotifyOnTargetUpdated=True}"></Image>
</Border>
</ListBoxItem>
Can anyone help that how can resolve this issue?

You are adding items inside the xaml statically. Please create a collection property in your view model, bind it to the items source and bind the SelectedItem property of the listbox in the view model like this:
<ListBox ItemsSource="{Binding ItemCollection}"
SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- bind to item properties here -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Whenever the ItemCollection property is set, the view model could also set its SelectedItem property:
public class Item { ... }
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private IEnumerable<Item> itemCollection;
public IEnumerable<Item> ItemCollection
{
get { return itemCollection; }
set
{
itemCollection = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(ItemCollection)));
SelectedItem = itemCollection.FirstOrDefault(); // here
}
}
private Item selectedItem;
public Item SelectedItem
{
get { return selectedItem; }
set
{
selectedItem = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}
}

first make an event for change binding and set the index in the change_event funciton
private void AddEventHandler()
{
myListBox.BindingContextChanged += new EventHandler(BindingContext_Changed);
}
private void BindingContext_Changed(object sender, EventArgs e)
{
myListBox.SelectedIndex = 0;
}

Firstly do you have any specific reason to add ListBoxItem in xaml itself.
If not please use observable collection to binding to Itemsource property of your listbox.
Also bind the selected item property of your ListBox to the property from your viewmodel.
Once your collection is changed set binded selected item property of your viewmodel to first item.
Also set,
IsSynchronizedWithCurrentItem="True" on your listbox in xaml.
This should work !

Related

Expander inside ListBox not showing content when expanded the first time from binding

I have a dialog window that contains a ListBox whose ItemTemplate contains an expander.
Its IsExpanded is bound to a property in the item view model. The ListBoxItem's IsSelected property is also bound to the IsExpanded property in the item view model object. And finally the SelectedItem property of the ListBox is bound to a property with the same name in the view model.
The problem here is that when setting up the view model before showing the dialog and setting it to the DataContext of the dialog, the item in the listbox gets selected as it should, the expander arrow shows that it is in the expanded state, but the content of the expander is not displayed.
If I set up the view model after showing the dialog, eg. in the Loaded handler of the dialog things work as expected. What is going on here, and what would be the best way to fix it?
The dialog window is defined as:
<Window x:Class="WpfApplication1.Dialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication1"
Title="Dialog" Height="300" Width="300">
<Grid>
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="Expander" x:Name="MyExpander" IsExpanded="{Binding IsExpanded, Mode=TwoWay}">
<Rectangle Width="100" Height="20" Fill="Red" />
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
And the ViewModel (implementation not included for the sake of brevity):
public interface IMyViewModel : INotifyPropertyChanged
{
object SelectedItem { get; set; }
ObservableCollection<IMyItemViewModel> Items { get; }
}
public interface IMyItemViewModel : INotifyPropertyChanged
{
bool IsExpanded { get; set; }
}
Then I have a simple main window with a button, and its Click handler is defined as:
private void Button_Click(object sender, RoutedEventArgs e)
{
MyViewModel vm = new MyViewModel();
MyItemViewModel item = new MyItemViewModel();
vm.Items.Add(item);
vm.SelectedItem = item;
Dialog dialog = new Dialog();
dialog.DataContext = vm;
dialog.ShowDialog();
}
When I run the application and click the button, the dialog shows up, the expander arrow indicates that it is in the expanded state, but its content is not displayed. Clicking on the expander collapses it, and clicking it again expands it, this time showing the content.
Putting the same code directly in the Main Window instead of a dialog however works as it is supposed to.
If I just do a Dispatcher.BeginInvoke(new Action(() => vm.SelectedItem = item); instead of setting it directly things also seem to work, but this feels a bit shaky.
What can be done to fix this problem?
Sounds like the content of the expander is not measured again after it is loaded, if the IsExpanded property is already set to true. Or to put it another way, the content is measured when it has still no actual size.
I suppose the easiest solution would be to just set the SelectedItem once the dialog has been loaded:
dialog.Loaded += (s, x) => vm.SelectedItem = item;

How to bind Dictionary object to checkedlistbox using WPF

I have a generic dictionary collection Dictionary. I need to bind the displaymember path key to the content of the checkbox and checkbox Ischecked property to the value member of the Dictionary
private Dictionary<string, bool> _columnHeaderList;
public Dictionary<string, bool> ColumnHeaderList
{
get { return _columnHeaderList; }
set { _columnHeaderList = value; RaisePropertyChanged("ColumnHeaderList"); }
}
private Dictionary<string, bool> GetColumnList()
{
Dictionary<string, bool> dictColumns = new Dictionary<string, bool>();
Array columns = Enum.GetValues(typeof(ColumnHeaders));
int arrayIndex=0;
for(int i=0;i<columns.Length;i++)
{
dictColumns.Add(columns.GetValue(arrayIndex).ToString(), true);
}
return dictColumns;
}
My XAML looks like
<ListBox Grid.Column="0" Grid.Row="1" Height="200"
ItemsSource="{Binding ColumnHeaderList}"
VerticalAlignment="Top">
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox Content="{Binding key}" IsChecked="{Binding Path=Value}"></CheckBox>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You will need to use OneWay binding if you bind to Dictionary because the KeyValuePair has read-only properties.
<CheckBox Content="{Binding Key, Mode=OneWay}" IsChecked="{Binding Path=Value, Mode=OneWay}" Width="100" /></CheckBox>
make sure you have set the DataContext. Note this will not update the dictionary values when the user presses on the checkboxes.
Since Value property is readonly and OneWay binding will not allow you track the changes if user checks or unchecks the checkboxes. It is recommended to bind them with an array new class ListItem:
class ListItem
{
public string Text { get; set; }
public bool IsChecked { get; set; }
}
private ListItem[] GetColumnList()
{
return Enum.GetValues(typeof(ColumnHeaders))
.Select(h => new ListItem{ Text = h.ToString(),IsChecked = true})
.ToArray();
}
Yeah its possible and it should work too, although you need to bind with Value with Binding Mode as OneWay since the dictionary value can't be set since its readOnly. If you wish to change the value you can hook the Command(if following MVVVM) or can handle in code behind on Checked event.
Also Binding for Key is not correct, replace your keywith Key. Your final xaml should be like this -
<ListBox Grid.Column="0" Grid.Row="1" Height="200"
ItemsSource="{Binding ColumnHeaderList}"
VerticalAlignment="Top">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Key}"
IsChecked="{Binding Path=Value, Mode=OneWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note i have changed the HierarchicalDataTemplate with DataTemplate since i could not see any hierarchy in the template.

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

Binding a listbox SelectedItem to an Observable Collection?

I have a Listbox in WPF with the SelectionMode set to Multiple, and can multiselect the items in the Listbox. However, the SelectedItem is not updating the Observable Collection it is bound to.
Is there a way to bind the multiple selected items from a ListBox to an Observable Collection?
i dont know mvvm way of doing this,
i have a working solution comibined of mvvm & codebehind.
CodeBehind
private void lstbox_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
var listBox = sender as ListBox;
if (listBox == null) return;
var viewModel = listBox.DataContext as Window1ViewModel;
if (viewModel == null) return;
viewModel.ListOfSelectedItems.Clear();
foreach (Window1ViewModel.States item in listBox.SelectedItems)
{
viewModel.ListOfSelectedItems.Add(item);
}
}
ViewModel
private ObservableCollection<States> _listofselecteditems;
public ObservableCollection<States> ListOfSelectedItems
{
get
{
return _listofselecteditems;
}
set
{
_listofselecteditems = value;
RaisePropertyChanged(() => ListOfSelectedItems);
}
}
Xaml
<ListBox x:Name="lstbox"
SelectionChanged="lstbox_SelectionChanged_1"
ItemsSource="{Binding StatesList,Mode=TwoWay}"
SelectionMode="Multiple" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox
IsChecked="{Binding Path=IsSelected,Mode=TwoWay}"
Content="{Binding StateName}" />
<TextBox Margin="8,0,0,0" Text="{Binding SOmeProperty}" IsEnabled="{Binding Path=IsSelected}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>

Is it possible to bind an Event in a Silverlight DataTemplate?

Is it possible to bind an Event in a Silverlight DataTemplate? If so, what is the best way to do it?
For example, say you've created a DataTemplate that has a Button in it, like this:
<UserControl.Resources>
<DataTemplate x:Key="MyDataTemplate" >
<Grid>
<Button Content="{Binding ButtonText}" Margin="4" />
</Grid>
</DataTemplate>
</UserControl.Resources>
Then, you apply it to a ListBox ItemTemplate, like this:
<Grid x:Name="LayoutRoot" Background="White">
<ListBox x:Name="lbListBox" ItemTemplate="{StaticResource MyDataTemplate}" />
</Grid>
If you set the ListBox's ItemSource to a list of objects of the class:
public class MyDataClass
{
public string ButtonText{ get; set; }
}
How then do you catch the button click from each button from the DataTemplate in the list? Can you use binding to bind the Click event to a method in "MyButtonClass", like this:
<UserControl.Resources>
<DataTemplate x:Key="MyDataTemplate" >
<Grid>
<Button Click="{Binding OnItemButtonClick}" Content="{Binding ButtonText}" Margin="4" />
</Grid>
</DataTemplate>
</UserControl.Resources>
Would this work? If so, what should I put in the "MyDataClass" to catch the event?
Thanks,
Jeff
There are a couple of options.
One. make a custom control that is bound the data object for that row. On that custom control add the handler for the bound object.
I dont think your binding on the click will work. Sans the Binding Statment and just declare your click to a string.
Add the handler on the page where the control is housed.
Keep in mind that if you bind this way you will only be able to work with the sender of that item (button) and it's properties. If you need to get at specific attributes on an object you maybe better off pursuing the first option.
Small Example demonstrating the functionality by adding 10 buttons to a list box with click events. HTH
DataTemplate XAML
<UserControl.Resources>
<DataTemplate x:Name="MyDataTemplate">
<Grid>
<Button Click="Button_Click" Content="{Binding ItemText}"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
ListBox XAML
<ListBox x:Name="ListBoxThingee" ItemTemplate="{StaticResource MyDataTemplate}"/>
Code Behind (I just plugged this all into the page.xaml file
public class MyClass
{
public string ItemText { get; set; }
}
public partial class Page : UserControl
{
ObservableCollection<MyClass> _Items;
public Page()
{
InitializeComponent();
_Items = new ObservableCollection<MyClass>();
for (int i = 0; i < 10; i++)
{
_Items.Add(new MyClass() {ItemText= string.Format("Item - {0}", i)});
}
this.ListBoxThingee.ItemsSource = _Items;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Button _b = sender as Button;
if (_b != null)
{
string _s = _b.Content as string;
MessageBox.Show(_s);
}
}
}
What I would do is create a button that uses the command pattern for click handling. In the .NET 4 framework you can bind commands to those that exist on the view model.

Resources