I would like to save which items have been checked in a multiselectlist so that when the page is navigated from and then back to the checked items may be shown in the list. Currently when I navigate away after checking items and then go back to the Multiselectlist item page, the checked states are not saved. So far what I have is as follows
Multiselectlist.xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ScrollViewer>
<!--<toolkit:MultiselectList x:Name="ColorList" ItemsSource="{Binding}" Height="88" HorizontalAlignment="Left" VerticalAlignment="Top" >-->
<toolkit:MultiselectList x:Name="ColorList" HorizontalAlignment="Left" VerticalAlignment="Top" Tap="ColorList_Tap">
<toolkit:MultiselectList.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="12,0,0,0" Grid.ColumnSpan="2">
<!--<Rectangle Fill="{Binding Brush}" Width="50" Height="50"/>-->
<CheckBox Background="{Binding Brush}"/>
<TextBlock Text="{Binding Name}" Margin="12,10,0,0"/>
</StackPanel>
</DataTemplate>
</toolkit:MultiselectList.ItemTemplate>
</toolkit:MultiselectList>
</ScrollViewer>
Multiselectlist.xaml.cs
public ColorListPage()
{
InitializeComponent();
solidColorBrushList = new List<ColorItem>()
{
//Color Color = (Color)ColorConverter.ConvertFromString("#FFF0F8FF");
new ColorItem { Brush = ColorHelper.ToSolidColorBrush("#FFF0F8FF"), Name = "alice blue" },
new ColorItem { Brush = ColorHelper.ToSolidColorBrush("#FFFAEBD7"), Name = "antique white" },
new ColorItem { Brush = ColorHelper.ToSolidColorBrush("#FF00FFFF"), Name = "aqua" },
new ColorItem { Brush = ColorHelper.ToSolidColorBrush("#FF7FFFD4"), Name = "aquamarine" },
new ColorItem { Brush = ColorHelper.ToSolidColorBrush("#FFF0FFFF"), Name = "azure" }, //dont translate!?
};
this.ColorList.ItemsSource = solidColorBrushList;
this.Loaded += new RoutedEventHandler(ColorListPage_Loaded);
}
void ColorListPage_Loaded(object sender, RoutedEventArgs e)
{
//show checkboxes when page is loaded
this.ColorList.IsSelectionEnabled = true;
}
private void ColorList_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
DependencyObject tappedElement = e.OriginalSource as UIElement;
MultiselectItem tappedItem = this.FindParentOfType<MultiselectItem>(tappedElement);
ColorItem selectedItem = null;
if (tappedItem != null)
{
// DataContext contains reference to data item
selectedItem = tappedItem.DataContext as ColorItem;
}
if (selectedItem != null)
{
MessageBox.Show(selectedItem.Name + " Tapped");
}
}
private T FindParentOfType<T>(DependencyObject element) where T : DependencyObject
{
T result = null;
DependencyObject currentElement = element;
while (currentElement != null)
{
result = currentElement as T;
if (result != null)
{
break;
}
currentElement = VisualTreeHelper.GetParent(currentElement);
}
return result;
}
So how would I save the checked state of the items in the Multiselectlist so that if a user navigates away and then back to the page, those checked items are shown?
You can use the multiselectlist's SelectedItems property to select items. If you use the SelectionChanged event on the list you can update a list in your viewmodel to save when navigating away from the page. Using the blend sdk and its Interactivity dll, you can bind the event to a command in your viewmodel as well.
<phone:PhoneApplicationPage
<!-- the usual xmlns attributes -->
xmlns:vm="clr-namespace:MyApplication.Presentation.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<phone:PhoneApplicationPage.DataContext>
<vm:MyPageViewModel />
</phone:PhoneApplicationPage.DataContext>
<toolkit:MultiselectList x:Name="selectionlist" ItemsSource="{Binding Items}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction
Command="{Binding UpdateSelectedCommand}"
CommandParameter="{Binding ElementName=selectionlist, Path=SelectedItems}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction
Command="{Binding LoadCommand}"
CommandParameter="{Binding ElementName='selectionlist'}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<toolkit:MultiselectList.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</toolkit:MultiselectList.ItemTemplate>
</toolkit:MultiselectList>
</phone:PhoneApplicationPage>
Then, in your viewmodel you can create the appropriate commands and methods like this.
namespace MyApplication.Presentation.ViewModels {
public sealed class MyPageViewModel : DependencyObject {
private readonly ObservableCollection<ItemViewModel> items;
private readonly RoutedCommand load;
private readonly RoutedCommand saveCommand;
private readonly RoutedCommand updateSelectedCommand;
public MyPageViewModel() {
items = new ObservableCollection<ItemViewModel>();
load = new RoutedCommand<MultiselectList>(
m => {
IEnumerable<Item> store = loadItems();
IEnumerable<Item> selected = loadSelectedItems();
populateSelectionList(m, store, selected);
});
updateSelectedCommand = new RoutedCommand<IList>(setSelected);
// use the savecommand on a button or a BindableApplicationBarButton
// or execute the command when you're navigating away from the page
saveCommand = new RoutedCommand<object>(o => storeItems(items));
}
public ICommand LoadCommand {
get { return load; }
}
public ICommand UpdateSelectedCommand {
get { return updateSelectedCommand; }
}
public ICommand SaveCommand {
get { return saveCommand; }
}
private void populateSelectionList(MultiselectList list, IEnumerable<Item> storage, IEnumerable<Item> selected) {
foreach (Item item in selected) {
ItemViewModel viewModel = new ItemViewModel(item);
list.SelectedItems.Add(viewModel);
items.Add(viewModel);
}
foreach (string item in storage) {
bool found = false;
foreach (Item select in selected) {
if (select == item) {
found = true;
break;
}
}
if (!found) {
ItemViewModel viewModel = new ItemViewModel(item);
items.Add(viewModel);
}
}
}
private void setSelected(IList list) {
// triggered when user selects or deselects an item in GUI
foreach (ItemViewModel viewModel in items) {
viewModel.IsSelected = false;
}
foreach (object item in list) {
ItemViewModel item = (ItemViewModel)item;
foreach (ItemViewModel tvm in items) {
if (tvm == item) {
tvm.IsSelected = true;
break;
}
}
}
}
private static void storeItems(IEnumerable<ItemViewModel> enumerable) {
// get all selected items
List<ItemViewModel> selected = new List<ItemViewModel>();
foreach (ItemViewModel viewModel in enumerable) {
if (viewModel.IsSelected) {
selected.Add(viewModel);
}
}
// save selected items in storage
saveSelectedItems(selected);
// save enumerable (i.e. all items) in storage
saveItems(enumerable);
}
private static void saveItems(IEnumerable<ItemViewModel> items) {
// todo: save enumerable to storage
foreach (ItemViewModel item in items) {
//saveItem(item.Model);
}
}
private static IEnumerable<Item> loadItems() {
// todo: load from storage
}
private static void saveSelectedItems(IEnumerable<Item> items) {
// todo: save selected to storage
}
private static IEnumerable<Item> loadSelectedItems() {
// todo: load from storage
}
}
}
Notice the todo items. You need to store and load the items from some storage, for instance a file. Take a look at IsolatedStorage in case you want to store it in a file.
You'll find the RoutedCommand class if you search on google for instance, it's inserted below for simplicity.
The Item and ItemViewModel classes are very basic, they just expose the fields necessary for displaying your item.
public class ItemViewModel : INotifyPropertyChanged {
private Item model;
public ItemViewModel(Item item) {
model = item;
item.PropertyChanged += (o,e) => onPropertyChanged(e.Property);
}
public bool IsSelected { get; set; }
public string Name {
get { return model.Name; }
set { model.Name = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void onPropertyChanged(string property) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(property));
}
}
}
public sealed class Item : INotifyPropertyChanged {
private string name;
public string Name {
get { return name; }
set {
if (name != value) {
name = value;
onPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void onPropertyChanged(string property) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(property));
}
}
}
You can put the RoutedCommand class in a separate namespace if you'd like, and include it in your xaml file and in your viewmodels using statements.
public class RoutedCommand<TArg> : ICommand {
private readonly Action<TArg> execute;
private readonly Func<TArg, bool> canexecute;
public RoutedCommand(Action<TArg> execute) : this(execute, o => true) { }
public RoutedCommand(Action<TArg> execute, Func<TArg, bool> canexecute) {
this.execute = execute;
this.canexecute = canexecute;
}
public bool CanExecute(object parameter) {
return canexecute((TArg)parameter);
}
public void Execute(object parameter) {
if (CanExecute(parameter)) {
execute((TArg)parameter);
}
}
public event EventHandler CanExecuteChanged;
}
Related
I'm trying to bind checkbox ischecked value. but whenever I click multiple checkbox, only last item is get selected.
This is my xaml.
<ListBox x:Name="EntryBox" ItemsSource="{Binding SourceCollection}"
SelectedItem="{Binding SelectedCheckbox}" SelectionMode="Multiple"
Grid.Column="0" Grid.Row="0" >
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsSelected="{Binding Selected}">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Selected}"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My viewmodel
public class MytViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Item> _socketEntryCollection;
public MyViewModel()
{
_socketEntryCollection = new ObservableCollection<Item>();
}
private ListCollectionView _sourceCollection;
public ListCollectionView SourceCollection
{
get { return _sourceCollection; }
set
{
_sourceCollection = value;
NotifyPropertyChanged(nameof(SourceCollection));
}
}
private Item _selectedCheckbox;
public Item SelectedCheckbox
{
get
{
return _selectedCheckbox;
}
set
{
_selectedCheckbox = value;
NotifyPropertyChanged(nameof(SelectedCheckbox));
}
}
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _selected;
public Item()
{
_selected = false;
}
public bool Selected
{
get { return _selected; }
set
{
if(_selected != value)
{
_selected = value;
NotifyPropertyChanged(nameof(Selected));
}
}
}
private void NotifyPropertyChanged(string Obj)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(Obj));
}
}
In my code behind,
private MyViewModel _BVM;
public BulkTest()
{
InitializeComponent();
_BVM = new MyViewModel();
}
foreach (MyViewModel.Item i in _BVM.SourceCollection)
{
if (i.Selected)
{
}
}
When I try to retrieve selected items, it only gives me only the last item I checked even though I checked multiple.
I would like to get every item that I checked.
How can I do that?
I've stumbled upon the well-known problem with Listbox and focus. I'm setting ItemsSource from the viewmodel and at some point I need to reload them and set selection and focus to a specific item, say:
private readonly ObservableCollection<ItemViewModel> items;
private ItemViewModel selectedItem;
private void Process()
{
items.Clear();
for (int i = 0; i < 100; i++)
{
items.Add(new ItemViewModel(i));
}
var item = items.FirstOrDefault(i => i.Value == 25);
SelectedItem = item;
}
public ObservableCollection<ItemViewModel> Items { /* usual stuff */ }
public ItemViewModel SelectedItem { /* usual stuff */ }
Binding may look like:
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" />
After calling the method item gets selected, but does not receive focus.
I've read a lot on the Internet and on StackOverflow, but all answers I found involve manual filling of the listbox, not via binding from viewmodel. So the question is: how can I properly focus newly selected item in the presented scenario?
To add some context, I'm implementing a sidebar file browser:
I need keyboard navigation on the listbox below treeview.
Here is a solution that might work for you:
The control:
class FocusableListBox : ListBox
{
#region Dependency Proeprty
public static readonly DependencyProperty IsFocusedControlProperty = DependencyProperty.Register("IsFocusedControl", typeof(Boolean), typeof(FocusableListBox), new UIPropertyMetadata(false, OnIsFocusedChanged));
public Boolean IsFocusedControl
{
get { return (Boolean)GetValue(IsFocusedControlProperty); }
set { SetValue(IsFocusedControlProperty, value); }
}
public static void OnIsFocusedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
ListBox listBox = dependencyObject as ListBox;
listBox.Focus();
}
#endregion Dependency Proeprty
}
The ViewModel:
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Boolean _IsFocused;
private String selectedItem;
public ObservableCollection<String> Items { get; private set; }
public String SelectedItem
{
get
{
return selectedItem;
}
set
{
selectedItem = value;
RaisePropertyChanged("SelectedItem");
}
}
public Boolean IsFocused
{
get { return _IsFocused; }
set
{
_IsFocused = value;
RaisePropertyChanged("IsFocused");
}
}
public ViewModel()
{
Items = new ObservableCollection<string>();
Process();
}
private void Process()
{
Items.Clear();
for (int i = 0; i < 100; i++)
{
Items.Add(i.ToString());
}
ChangeFocusedElement("2");
}
public void ChangeFocusedElement(string newElement)
{
var item = Items.FirstOrDefault(i => i == newElement);
IsFocused = false;
SelectedItem = item;
IsFocused = true;
}
private void RaisePropertyChanged(String propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
The XAML:
<local:FocusableListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Width="200"
IsFocusedControl="{Binding IsFocused, Mode=TwoWay}"/>
The update call:
_viewModel.ChangeFocusedElement("10");
I ended up with the following code in control's codebehind:
public void FixListboxFocus()
{
if (lbFiles.SelectedItem != null)
{
lbFiles.ScrollIntoView(lbFiles.SelectedItem);
lbFiles.UpdateLayout();
var item = lbFiles.ItemContainerGenerator.ContainerFromItem(viewModel.SelectedFile);
if (item != null && item is ListBoxItem listBoxItem && !listBoxItem.IsFocused)
listBoxItem.Focus();
}
}
This method is available for calling from within viewModel, which calls it every time it sets the selection:
var file = files.FirstOrDefault(f => f.Path.Equals(subfolderName, StringComparison.OrdinalIgnoreCase));
if (file != null)
SelectedFile = file;
else
SelectedFile = files.FirstOrDefault();
access.FixListboxFocus();
The access is view passed to ViewModel via interface (to keep separation between presentation and logic). The relevant XAML part looks like following:
<ListBox x:Name="lbFiles" ItemsSource="{Binding Files}" SelectedItem="{Binding SelectedFile}" />
Help me please!!!
I have 3 UserControls
I select user on List Users UC listbox
Then send message from SendMessage UC to Database
when i send message to Db it must refresh my chat listBox in Correspondence UC, but problem is in my ChatWrapper.
PropertyChanged in ChatWrapper is always null, and I can't refresh my ListBox in Correspondence UC with new message
List Users:
public IEnumerable<EmployeesDb> getListNames
{
get { return Db.Instance.EmployeesDbs.ToList(); }
}
static EmployeesDb m_selectedUser;
public static EmployeesDb selectedUser
{
get { return m_selectedUser; }
set
{
if (value != null)
m_selectedUser = value;
Correspondence correspondence = new Correspondence();
correspondence.CorrespondenceChat();
}
}
}
Send Message ( I try to refresh -> SendInfo.FirstOrDefault().RefreshGUI();)
public static DependencyProperty SendInfoProperty =
DependencyProperty.Register(
"SendInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(SendMessage));
public IEnumerable<ChatWrapper> SendInfo
{
get { return GetValue(SendInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(SendInfoProperty, value); }
}
void SendMessageCommandExecute()
{
//...
SendInfo.FirstOrDefault().RefreshGUI();
//...
}
ChatWrapper
public class ChatWrapper : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void FirePropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
public void RefreshGUI()
{
FirePropertyChanged("message");
}
public ChatDb chatDb { get; set; }
public string message
{
get
{
return (chatDb != null) ? string.Format("{0} {1}.{2} / {3} / {4}\n{5}",
chatDb.FromEmployeesDb.surname,
chatDb.FromEmployeesDb.name[0],
chatDb.FromEmployeesDb.middleName[0],
chatDb.messageDateTime,
chatDb.computerName,
chatDb.message) : null;
}
}
Correspondence
//...
public partial class Correspondence : UserControl, INotifyPropertyChanged
{
public static DependencyProperty GetCorrespondenceInfoProperty =
DependencyProperty.Register(
"GetCorrespondenceInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(Correspondence),
new PropertyMetadata(OnChanged));
public IEnumerable<ChatWrapper> GetCorrespondenceInfo
{
get { return GetValue(GetCorrespondenceInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(GetCorrespondenceInfoProperty, value); }
}
static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var me = d as Correspondence;
me.chat = me.GetCorrespondenceInfo;
}
ICollectionView m_CollectionView;
public static IEnumerable<ChatWrapper> m_chat;
public IEnumerable<ChatWrapper> chat
{
get { return m_chat; }
set
{
m_chat = value;
if (ListUsers.selectedUser != null)
CorrespondenceChat();
FirePropertyChanged("chat");
}
}
public void CorrespondenceChat()
{
if (m_chat == null)
return;
m_CollectionView = CollectionViewSource.GetDefaultView(m_chat);
//...
FirePropertyChanged("chat");
}
XAML of Correspondence (refresh
<Grid>
<ListBox x:Name="correspondenceListBox" ItemsSource="{Binding chat, RelativeSource={RelativeSource AncestorType={x:Type local:Correspondence}}}"
Height="auto" Grid.Row="0" Grid.Column="1" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding message}" TextWrapping="Wrap" Width="auto"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I tried to write
public event PropertyChangedEventHandler PropertyChanged = delegate { };
PropertyChanged is no longer null, but it's still not updated
Overview:
I've set up a property with INPC that invokes a page navigation in the view code behind from the MainViewModel. This property is bound to the SelectedItem property of a list view in the bound view.
The INPC implementation is inherited from the ViewModelBase class which is implemented as follows, https://gist.github.com/BrianJVarley/4a0890b678e037296aba
Issue:
When I select an item from the list view, the property SelectedCouncilItem setter doesn't trigger. This property is bound to the SelectedItem property of the list view.
Debugging Steps:
Checked binding names for SelectedItem in list view property, which was the same as the property name in the MainViewModel.
Ran the solution and checked for any binding errors in the output window, which there were none.
Placed a break point on the SelectedCouncilItem which doesn't get triggered when I select from the list view.
Checked the data context setup for the view which verified that the view is set to the data context of the MainViewModel.
Question:
Does anyone know what other steps I can take in debugging the issue, or what the issue might be?
Code:
MainPage - (List View)
<Grid x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<phone:LongListSelector x:Name="MainLongListSelector"
Margin="0,0,-12,0"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedCouncilItem}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Style="{StaticResource PhoneTextExtraLargeStyle}"
Text="{Binding CouncilAcronym}"
TextWrapping="Wrap" />
<TextBlock Margin="12,-6,12,0"
Style="{StaticResource PhoneTextSubtleStyle}"
Text="{Binding CouncilFullName}"
TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
MainViewModel - (summary)
namespace ParkingTagPicker.ViewModels
{
public class MainViewModel : ViewModelBase
{
//Dependency Injection private instances
private INavigationCallback _navCallBack = null;
public MainViewModel()
{
this.Items = new ObservableCollection<ItemViewModel>();
}
/// <summary>
/// Creates and adds a few ItemViewModel objects into the Items collection.
/// </summary>
public void LoadCouncilNamesData()
{
this.Items.Add(new ItemViewModel() { ID = "6", CouncilAcronym = "WTC", CouncilFullName = "Wicklow Town Council"});
this.Items.Add(new ItemViewModel() { ID = "7", CouncilAcronym = "TS", CouncilFullName = "Tallaght Stadium" });
this.Items.Add(new ItemViewModel() { ID = "8", CouncilAcronym = "GS", CouncilFullName = "Greystones" });
this.IsDataLoaded = true;
}
public ObservableCollection<ItemViewModel> Items { get; private set; }
public bool IsDataLoaded { get; private set; }
private ItemViewModel _selectedCouncilItem;
public ItemViewModel SelectedCouncilItem
{
get
{
return this._selectedCouncilItem;
}
set
{
this.SetProperty(ref this._selectedCouncilItem, value, () => this._selectedCouncilItem);
if (_selectedCouncilItem != null)
{
_navCallBack.NavigateTo(_selectedCouncilItem.ID);
}
}
}
public INavigationCallback NavigationCallback
{
get { return _navCallBack; }
set { _navCallBack = value; }
}
}
}
ViewModelBase - (detailing INPC implementation)
namespace ParkingTagPicker.ViewModels
{
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected bool SetProperty<T>(ref T backingField, T Value, Expression<Func<T>> propertyExpression)
{
var changed = !EqualityComparer<T>.Default.Equals(backingField, Value);
if (changed)
{
backingField = Value;
this.RaisePropertyChanged(ExtractPropertyName(propertyExpression));
}
return changed;
}
private static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
var memberExp = propertyExpression.Body as MemberExpression;
if (memberExp == null)
{
throw new ArgumentException("Expression must be a MemberExpression.", "propertyExpression");
}
return memberExp.Member.Name;
}
}
}
There is an issue with the control. Please try using custom LongListSeletor
public class ExtendedLongListSelector : Microsoft.Phone.Controls.LongListSelector
{
public ExtendedLongListSelector()
{
SelectionChanged += LongListSelector_SelectionChanged;
}
void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedItem = base.SelectedItem;
}
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(LongListSelector),
new PropertyMetadata(null, OnSelectedItemChanged));
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = (LongListSelector)d;
selector.SelectedItem = e.NewValue;
}
public new object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
}
and implement in replace it in XAML with the existing List.
xmlns:controls="clr-namespace:ProjectName.FolderName"
<controls:ExtendedLongListSelector x:Name="MainLongListSelector"
Margin="0,0,-12,0"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedCouncilItem}">
</controls:ExtendedLongListSelector>
I'm building application using the MVVM pattern. After clicking on one of the elements I want to see this element's details. I wrote this:
XAML
<phone:LongListSelector ItemsSource="{Binding Data}"
Margin="0,0,0,158"
SelectedItem="{Binding SelectedItem}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button>
<!-- Command="{Binding ShowDetailsAction}"-->
<Button.Template>
<ControlTemplate>
<TextBlock Text="{Binding Text}"></TextBlock>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
ViewModel:
public IEnumerable SelectedItem
{
get { return _itemsControl; }
set
{
if (_itemsControl == value)
return;
_itemsControl = value;
// Test
_mss.ErrorNotification("fd");
}
}
I tried also using a command, which didn't work, too.
This was the command part:
public ICommand ShowDetailsCommand { get; private set; }
public ViewModel()
{
_loadDataCommand = new DelegateCommand(LoadDataAction);
SaveChangesCommand = new DelegateCommand(SaveChangesAction);
ShowDetailsCommand = new DelegateCommand(ShowDetailsAction);
}
private void ShowDetailsAction(object p)
{
_mss.ErrorNotification("bla bla");
}
EDIT
ViewModel
private IEnumerable _itemsControl;
public IEnumerable Data
{
get
{
return _itemsControl;
}
set
{
_itemsControl = value;
RaisePropertyChanged("Data");
}
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Model
public string Text { get; set; }
public DateTimeOffset Data { get; set; }
EDIT2
private MobileServiceCollection<ModelAzure, ModelAzure> _items;
private readonly IMobileServiceTable<ModelAzure> _todoTable = App.MobileService.GetTable<ModelAzure>();
private async void RefreshTodoItems()
{
try
{
_items = await _todoTable.ToCollectionAsync();
}
catch (MobileServiceInvalidOperationException e)
{
_mss.ErrorNotification(e.ToString());
}
Data = _items;
}
Your Data property looks like
private MobileServiceCollection<ModelAzure, ModelAzure> _itemsControl;
public MobileServiceCollection<ModelAzure, ModelAzure> Data
{
get
{
return _itemsControl;
}
set
{
_itemsControl = value;
RaisePropertyChanged("Data");
}
}
Edited
It seems the SelectedItem property from LongListSelector cannot be bound in WP8.
What you can do is either :
Use the derived and fixed custom LongListSelector provided in the link above instead of the default one, which looks like :
public class LongListSelector : Microsoft.Phone.Controls.LongListSelector
{
public LongListSelector()
{
SelectionChanged += LongListSelector_SelectionChanged;
}
void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedItem = base.SelectedItem;
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
"SelectedItem",
typeof(object),
typeof(LongListSelector),
new PropertyMetadata(null, OnSelectedItemChanged)
);
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = (LongListSelector)d;
selector.SelectedItem = e.NewValue;
}
public new object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
}
Register the SelectionChanged event from LongListSelector and call your ViewModel by yourself inside the associated handler/callback :
in your view :
<phone:LongListSelector x:Name="YourLongListSelectorName"
ItemsSource="{Binding Data}"
Margin="0,0,0,158"
SelectionChanged="OnSelectedItemChanged">
in your code behind :
private void OnSelectedItemChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs e)
{
((YourViewModel)this.DataContext).NewSelectedItemMethodOrWhateverYouWant((ModelAzure)this.YourLongListSelectorName.SelectedItem);
//or
((YourViewModel)this.DataContext).SelectedItem = (ModelAzure)this.YourLongListSelectorName.SelectedItem;
}
Finally your Button command wasn't properly working, because when you use a DataTemplate, the ambiant DataContext is the item itself. Which means that it was looking for your Command into your Model instance, not into your ViewModel instance.
Hope this helps
In your ViewModel, you have:
public IEnumerable SelectedItem
{
get { return _itemsControl; }
set
{
if (_itemsControl == value)
return;
_itemsControl = value;
// Test
_mss.ErrorNotification("fd");
}
}
Why is your SelectItem an IEnumerable? Should it not be of type "Model"? Your list is bound to "Data" which should be ObservableList, not IEnumerable. It will provide it's own change notification, so you don't need to.
The list will set the SelectedItem when it gets selected, but if the type is wrong, it won't get set.
Greg