I came across one problem where the selected item should be visible in a listbox using MVVM. To make selected item visible is very much UI specific code and it should not be a part of ViewModel class. Here is a piece of code that make the selected item visible in Listbox using reflection (taken from here).
"_itemsHost", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic, null, list, null);
double scrollHeight = vsp.ScrollOwner.ScrollableHeight;
double offset = scrollHeight * itemIndex / list.Items.Count;
vsp.SetVerticalOffset(offset);
Then where should we put this code. One approach is to introduce an event in my view mode class and set it from view. Here is our starting point.
{
public event EventHandler SetSelectedItem;
// other stuff
}
Then we should call this event handler from our command method. It would be something like this.
{
string selected = param as string;
for (int index = 0; index < Data.Count; index++)
{
if (Data[index] == selected)
{
EventHandler handler = this.SetSelectedItem;
if (handler != null)
{
handler(this, new MyEventArgs(index));
}
}
}
}
And in View class we are handling this event and make the selected item visible. It would be something like this.
vm.SetSelectedItem += (sender, e) =>
{
MyEventArgs myArgs = e as MyEventArgs;
int itemIndex = myArgs.SelectedItemIndex;
VirtualizingStackPanel vsp = (VirtualizingStackPanel)typeof(ItemsControl).InvokeMember(
"_itemsHost", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic, null, list, null);
double scrollHeight = vsp.ScrollOwner.ScrollableHeight;
double offset = scrollHeight * itemIndex / list.Items.Count;
vsp.SetVerticalOffset(offset);
};
DataContext = vm;
We are passing the parameter to the command that is actually bind with text in my text box. Here is out XAML code for this.
CommandParameter="{Binding ElementName=text, Path=Text}">
Search
</Button>
This is complete XAML for our program.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Selected Item" Height="300" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Name="list" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="5"
ItemsSource="{Binding Data}"/>
<TextBox Name="text" Grid.Column="0" Grid.Row="1" Margin="5"/>
<Button Grid.Column="1" Grid.Row="1" Margin="5" Command="{Binding SearchItem}"
CommandParameter="{Binding ElementName=text, Path=Text}">
Search
</Button>
</Grid>
</Window>
Here is complete C# code of our program
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfListBoxSelection
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyViewModel vm = new MyViewModel();
vm.SetSelectedItem += (sender, e) =>
{
MyEventArgs myArgs = e as MyEventArgs;
int itemIndex = myArgs.SelectedItemIndex;
VirtualizingStackPanel vsp = (VirtualizingStackPanel)typeof(ItemsControl).InvokeMember(
"_itemsHost", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic, null, list, null);
double scrollHeight = vsp.ScrollOwner.ScrollableHeight;
double offset = scrollHeight * itemIndex / list.Items.Count;
vsp.SetVerticalOffset(offset);
};
DataContext = vm;
}
}
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event EventHandler RequestClose;
public ICommand ExitCommand
{ get; set; }
public void Close()
{
EventHandler handler = this.RequestClose;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MyEventArgs : EventArgs
{
public MyEventArgs(int selectedItemIndex)
{
this.SelectedItemIndex = selectedItemIndex;
}
public int SelectedItemIndex
{ get; set; }
}
public class MyViewModel : ViewModelBase
{
public event EventHandler SetSelectedItem;
public MyViewModel()
{
Data = new ObservableCollection<string>();
for (int i = 0; i < 100; i++)
{
Data.Add(i.ToString());
}
SearchItem = new MyCommand<object, object>(Search, (param) => true);
}
public ObservableCollection<string> Data
{ get; set; }
public ICommand SearchItem
{ get; set; }
void Search(object param)
{
string selected = param as string;
for (int index = 0; index < Data.Count; index++)
{
if (Data[index] == selected)
{
EventHandler handler = this.SetSelectedItem;
if (handler != null)
{
handler(this, new MyEventArgs(index));
}
}
}
}
}
public class MyCommand<T1, T2> : ICommand
{
private Action<T1> _Function;
private Func<T2, bool> _Predicate;
public MyCommand(Action<T1> function)
{
_Function = function;
}
public MyCommand(Action<T1> function, Func<T2, bool> predicate)
{
_Function = function;
_Predicate = predicate;
}
public bool CanExecute(object parameter)
{
if (_Predicate != null)
{
return _Predicate((T2)parameter);
}
return true;
}
public void Execute(object parameter)
{
if (_Function != null)
{
_Function((T1)parameter);
}
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
This is the output of the program when we run it.
And this is an output when we typed something in textbox and press the search button.


[...] using Reflection We just saw how we how to call a member function using the reflection here. With this method we can also call private member function of the class even static function. [...]
By: Calling private methods using Reflection « Zeeshan Amjad's WPF Blog on October 12, 2011
at 10:16 pm
Hi,
Actually i am populating items from a list.Once the mouse over an item it shows one treeview and mouse over the treeview it shows another treeview.
At this point, i wanna highlight the selected item in the first listbox.
Thanks ,
Raja
By: Raja on November 25, 2011
at 1:43 am
Hi
You can try Property Data Trigger. Take a look at the following example
http://zamjad.wordpress.com/2010/02/05/apply-animation-with-property-trigger/
It might help you.
Regards
Zeeshan Amjad
By: Zeeshan Amjad on December 12, 2011
at 11:23 pm