WPF: Ordering and Displaying ListView SelectedItems in MVVM - c#

I have a ListView with SelectionMode=Multiple and two TextBoxes. One should display the top-most selected item, one should display the bottom most selected item. I am also working using the MVVM design pattern.
The issues are as follows:
"SelectedItems" is indexed from the first selection point - so the SelectedItems[0] can be the bottom-most selected item, which is undesirable. I want the top-most item to display in the top box and the bottom-most item to display in the bottom box.
I can't seem to reference SelectedItems[ SelectedItems.Count - 1] from the XAML in order to display the last selected item.
Here's a look at my current XAML:
<ListView x:Name="myListView" ItemsSource="{Binding MyList}"
SelectionMode="Multiple">
<TextBox x:Name="topTextBox" Grid.Column="1" Grid.Row="2"
Text="{Binding ElementName=myListView, Path=SelectedItems[0].ID}" />
<TextBox x:Name="bottomTextBox" Grid.Column="1" Grid.Row="3"
Text="{Binding ElementName=myListView, Path=SelectedItems[SelectedItems.Count-1].ID}" />
I'm not sure what the best approach to take is.

The solution I settled on is as follows:
I used the interactive namespace (Don't forget to reference the DLL) and the following XAML:
xmlns:interact="http://schemas.microsoft.com/expression/2010/interactivity"
<TextBox x:Name="startTextBox" Grid.Column="1" Grid.Row="2"
Text="{Binding LatestSelectedItem}" />
<ListView x:Name="myListView" ItemsSource="{Binding MyList}"
SelectionMode="Extended" >
<interact:Interaction.Triggers>
<interact:EventTrigger EventName="MouseUp"> <!-- Alternatively, OnSelectionChanged -->
<interact:InvokeCommandAction Command="{Binding MyListViewSelectionChangedCommand}"
CommandParameter="{Binding ElementName=myListView, Path=SelectedItems}" />
</interact:EventTrigger>
</interact:Interaction.Triggers>
</ListView>
Then I provided appropriate Properties in my ViewModel, along with the following command:
private void MyListView_SelectionChanged(object param)
{
IList selectedItems = (IList)param;
List<MyViewModel> myList = selectedItems.OfType<MyViewModel>().ToList();
if (myList.Count > 0)
{
myList.Sort(); // Implement comparator on MyViewModel
LatestSelectedItem = myList[myList.Count-1];
}
}
Works fine.

Related

Auto-scroll Listbox based on TextBox text

In WPF and MVVM pattern, I have a TextBox and a ListBox. The ListBox is bound to a collection of items using a DataTemplate. The default count for this collection is about 50 or so.
What I wanted to achieve is a filter-like action. If it's possible, I wouldn't like to change the list view or anything like that, but simply to scroll to the item corresponding to the TextBox match, preferably without selecting it.
I've seen some examples and solutions using CollectionView and Filter (couldn't get it to work btw), and some using auto-scroll to the end of the list or to a new added item, but none specific to my case.
My Listbox is structured as follows:
<ListBox IsTextSearchEnabled="True" HorizontalAlignment="Stretch" Margin="6,49,0,0" Name="lbObjectA" VerticalAlignment="Stretch" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type src:Dto}">
<StackPanel Height="20" Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Margin="0,3,0,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Select only one item in two ListViews

I have two ListView, each bound to a specific collection in my ViewModel and I want to be able to select only one item globally.
Each ListView has its SelectedItem property bound to the same property in the ViewModel.
My probleme is as follow: when I select an item in a ListView, and then select another item in the other ListView, the first item stays selected.
I could achieve this with some code-behind, but I want to know if a pure XAML solution exists.
XAML:
<ListView SelectionMode="Single" ItemsSource="{Binding Path=MyList1}" SelectedItem="{Binding Path=MySelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView SelectionMode="Single" ItemsSource="{Binding Path=MyList2}" SelectedItem="{Binding Path=MySelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I finally got it working, by just replacing SelectedItem by SelectedValue.
In this use case, both properties have the same behaviour, but the latter one handles correctly the unselection if the bound selected item is not in the list.
You need to use Mode=TwoWay in your SelectedItem. It should work.

Binding Datagrid Item to Textboxes

I try to bind a selected item in my datagrid to some textboxes.
Sadly the textboxes wont be updated on change.
If you need more Informations fell free to ask.
In my view i try to bind the the data of the selected Item of the Datagrid to the Textboxes.
in the Textboxes there can be a new employee you want to add or one of the Datagrid that you want to edit.
<TextBox Name="TxtName" Text="{Binding Employee.LastName}" Grid.Column="1" Grid.Row="0"></TextBox>
<TextBox Name="TxtFirstName" Text="{Binding Employee.FirstName}" Grid.Column="3" Grid.Row="0"></TextBox>
<TextBox Name="TxtDateOfBirth" Text="{Binding Employee.DateOfBirth, StringFormat=d}" Grid.Column="1" Grid.Row="1"></TextBox>
<ComboBox Name="CmbGender" SelectedItem="{Binding Employee.Gender}" ItemsSource="{Binding Genders}" DisplayMemberPath="Short" Grid.Column="3" Grid.Row="1"/>
<DataGrid Name="GrdAllEmployees" ItemsSource="{Binding Employees}" SelectedItem="{Binding SelectedEmployee}" Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="4" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
In my ViewModel i set the Value of the Selected Employee to the Employee that is shown in the Textboxes and raise the Events for both.
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
NotifyPropertyChanged("SelectedEmployee");
_employee = _selectedEmployee;
NotifyPropertyChanged("Employee");
}
}
After this the values are correct on debugging. but the view will not be updated.
There is a much simpler way of displaying values from the selected item of the DataGrid, or in fact any collection control in WPF. That is to use the Selector.IsSynchronizedWithCurrentItem property. When you set this to true, then you can reference the selected item from that collection using the / notation, which means the current item. Try something like this:
<StackPanel>
<DataGrid ItemsSource="{Binding Employees}" IsSynchronizedWithCurrentItem="True" />
<TextBlock Text="{Binding Employees/Name}" />
</StackPanel>
This would display the Name property value from the currently selected item in the DataGrid. Further information... from the Binding.Path Property page on MSDN:
When the source is a collection view, the current item can be specified with a slash (/). For example, the clause Path=/ sets the binding to the current item in the view. When the source is a collection, this syntax specifies the current item of the default collection view.
Property names and slashes can be combined to traverse properties that are collections. For example, Path=/Offices/ManagerName specifies the current item of the source collection, which contains an Offices property that is also a collection. Its current item is an object that contains a ManagerName property.
It worked for me after i replaced my own classes for the RelayCommand and ViewModelBase with the ones of MVVMlight, that i downloaded from nuget.
https://mvvmlight.codeplex.com/
I just had to change the NotifyPropertyChanged with RaisePropertyChanged.
But also thanks to #Sheridan for his advice.

Problem when setting up command bindings in XAML

I am trying to create a menu bar for my application. I have created a collection in xaml that I will contain menu items that my menu will bind to.
In xaml I have created an array that I use as my static resource for binding.
<coll:ArrayList x:Key="MenuOptionsList">
<model:DashboardMenuBarItem
Icon="the location of an image in my images folder"
DisplayName="The test that will appear under my button"
CommandName="someCommandInMyViewModel"/>
</coll:ArrayList>
I am using a listbox with a data template to show these items as follows.
<ListBox x:Name="lstNavigateTo" MinWidth="400" DockPanel.Dock="Top"
ItemsSource="{Binding Source={StaticResource MenuOptionsList}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource horizontalListTemplate}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<Button Height="60" Width="60"
Command="{Binding Mode=OneWay, Path=CommandName}">
<Button.Content>
<Image Source="{Binding Path=Icon}" Grid.Row="0" />
</Button.Content>
</Button>
<TextBlock Text="{Binding Path=DisplayName}"
Width="100" TextAlignment="Center" Grid.Row="1" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My problem is that I am using the MVVM design pattern and cannot get the command bindings to work on the button click. Previously I would have managed the button click like this.
Command="{Binding someCommandInMyViewModel}"
That would work fine but when I try to bind a command to a property of an item in my collection the command will not fire.
Does anyone know how I can achieve this.
The CommandName property in your collection is of type String, whereas the Command property on Button is of type ICommand. In what way are you expecting WPF to resolve an ICommand from a String? You'll need to help it: either create a converter and use it in your binding, or change your CommandName property so that it contains an actual ICommand rather than a String.

How to Set focus on the 1st Item of Nested Listbox in Silverlight?

I have a List box which contains another lisbox Inside it.
<ListBox x:Name="listBoxParent">
<ListBox.ItemTemplate>
<DataTemplate>
<Image x:Name="thumbNailImagePath" Source="/this.jpg" Style="{StaticResource ThumbNailPreview}" />
<TextBlock Margin="5" Text="{Binding Smthing}" Style="{StaticResource TitleBlock_Artist}" />
</StackPanel>
<StackPanel Style="{StaticResource someStyle}" >
<ListBox x:Name="listBoxChild" Loaded="listBoxChild_Loaded" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="5" Text="{Binding myText}" Width="300"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
</ListBox.ItemsPanel>
</ListBox>
Now while I try to focus to the 1st item of the child List Box It focus repeteadly(as it get repeated inside parent Listbox) on 1st all the 1st items of parentlistbox, wish I could have provide a Screen shot for Better Understanding. But Can't
public void listBoxChild_Loaded(object sender, RoutedEventArgs e)
{
var myListBox = (ListBox)sender;
myListBox .ItemsSource = PageVariables.eOUTData;//listboxSongsData;
myListBox .SelectedIndex = 0;
myListBox .Focus();
}
Thanks,
Subhen
It's difficult to determine what you're trying to accomplish in your code.
One idea:
Have you considered changing the inner ListBox to an ItemsControl? If you needed it to scroll as well, you could layer it in a ScrollViewer.
Second idea (possibly an additional idea):
Every item that created in the item template is calling the Loaded event. Don't you want that to happen only once? Remove the event from the inner ListBox and set focus once the outer list has been loaded.

Categories