Multi-select Listview - xamarin

This is my list view it should contain the list of activities came from my SQLite database:
<ListView SeparatorVisibility="None" x:Name="lstActivity" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame StyleClass="lstframe" CornerRadius="0" BorderColor="Transparent" HasShadow="False">
<StackLayout StyleClass="lstContainer" VerticalOptions="CenterAndExpand">
<Grid>
<Label StyleClass="lstActivityName" Grid.Row="0" Grid.Column="0" Text="{Binding ActivityDescription}">
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String">
<On Platform="Android" Value="Poppins-Regular.otf#Poppins-Regular"/>
</OnPlatform>
</Label.FontFamily>
</Label>
<Switch Grid.Row="0" Grid.Column="1" IsToggled="{Binding Selected}" />
</Grid>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is how I populate the list view this will return atleast five(5) activities:
public void Get_Activities()
{
try
{
var db = DependencyService.Get<ISQLiteDB>();
var conn = db.GetConnection();
var getActivity = conn.QueryAsync<ActivityTable>("SELECT * FROM tblActivity WHERE Deleted != '1' ORDER BY ActivityDescription");
var resultCount = getActivity.Result.Count;
if (resultCount > 0)
{
var result = getActivity.Result;
lstActivity.ItemsSource = result;
lstActivity.IsVisible = true;
}
else
{
lstActivity.IsVisible = false;
}
}
catch (Exception ex)
{
//Crashes.TrackError(ex);
}
}
Selected item binding:
public class SelectData
{
public bool Selected { get; set; }
}
Get selected Items on click:
private void BtnClose_Clicked(object sender, EventArgs e)
{
foreach (var x in result)
{
if (x.Selected)
{
// do something with the selected items
}
}
}
I posted another question regarding on multi-select list view my problem is I don't know how to proceed when I use the answers given to me. How can I get the the selected values because I will save the selected values to my database?

your Switch is bound to the Selected property of your model. Just iterate (or use LINQ) to get the items that are selected.
// you need to maintain a reference to result
foreach(var x in result)
{
if (x.Selected)
{
// do something with the selected items
}
}
LINQ
var selected = result.Where(x => x.Selected).ToList();
you will also need to have a class level reference to result
// you will need to change this to reflect the actual type of result
List<MyClass> result;
public void Get_Activities()
{
...
result = getActivity.Result;
...

For multi-select Listview, I have wrote a working example in my blog. Hope this helps : https://androidwithashray.blogspot.com/2018/03/multiselect-list-view-using-xamarin.html?view=flipcard

Related

how to refer to item in an enumeration within a ListView

I have a class MRU which contains an ObservableCollection of type string. I would like to display the items within the collection on a PopupPage in a ListView.
[DefaultBindingProperty("Items")]
public class MRU<T> :INotifyPropertyChanged
{
public ObservableCollection<T> Items { get; } = new ObservableCollection<T>();
// This is the maximum number of items that will be saved to persistent storage,
// in use the list can grow beyond this number
private int _MaxItems = 5;
public int MaxItems
{
get { return _MaxItems; }
set {
_MaxItems = value;
TrimItems();
OnPropertyChanged();
}
}
public void Add(T t) {
int idx = Items.IndexOf(t);
if (idx > 0)
{
Items.Move(idx, 0);
}
else if (idx == -1)
{
Items.Insert(0, t);
}
}
public T Pop(int i)
{
if (i > 0 && i < Items.Count)
{
Items.Move(i, 0);
}
return Items[0];
}
private void TrimItems()
{
while (Items.Count > _MaxItems)
{
Items.RemoveAt(Items.Count - 1);
}
}
public void Save()
{
App.Current.Properties["MRU"] = SaveToJSON();
App.Current.SavePropertiesAsync();
}
public string SaveToJSON()
{
TrimItems();
string jsonString = JsonSerializer.Serialize(Items);
return jsonString;
}
public int LoadFromJSON(string jsonString)
{
Items.Clear();
ObservableCollection<T> restore = JsonSerializer.Deserialize<ObservableCollection<T>>(jsonString);
foreach (var t in restore)
{
if (Items.Count == _MaxItems) break;
Items.Add(t);
}
return Items.Count;
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I can display it in my page using the following XAML
<StackLayout
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="0,20">
<Frame BackgroundColor="White" CornerRadius="15">
<StackLayout>
<Label Text="Most Recently Used" FontSize="Large" BackgroundColor="LightBlue" HorizontalOptions="CenterAndExpand" Padding="20,4"/>
<StackLayout >
<ListView ItemsSource="{Binding Items}" SelectionMode="Single">
<!--<ListView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical">
<Label Text="{Binding XXXXXX}" VerticalOptions="Center" HorizontalTextAlignment="End" FontSize="Medium"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate> -->
</ListView>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
Note the commented out ListView.ItemTemplate section. I would like to display the items within an ItemTemplate so that I can add buttons for each object, however I don't know how to declare it . What should XXXXX be? I've tried a zillion unlikely things
In your xaml page, you created a ListView. I found that you forgot to add <ViewCell></ViewCell> tags.
You should use a structure like the following:
<ListView x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you have an ObservableCollection<string>, to access the string in your binding, you want to do:
Text="{Binding}"
or
Text="{Binding .}"
Both say that you want to bind to whole object. Since the "whole object" in this case is a string, that's what you'll get.

Cannot display ListView data when creating PDF using PdfSharp.Xamarin.Forms

I am new to xamarin and I am using PdfSharp.Xamarin.Forms nuget to create a PDF in Xamarin forms for both Android and iOS. Problem is I cannot render ListView. They have mentioned about it, and need to write a renderer for it. But I have no idea how to create and bind it.
This is how I did it.
<Grid x:Name="mainGrid">
<ScrollView>
<StackLayout Padding="4" Orientation="Vertical">
<!--<Image HeightRequest="80" Source="logojpeg.jpg" Margin="0,0,0,5"/>-->
<Label FontSize="18" TextColor="Black" FontFamily="{StaticResource timesNewRomanBold}" HorizontalOptions="CenterAndExpand" Text="Monthly Motor Renew List of Jayasekara (900585) as at January, 2020"/>
<Label FontSize="18" TextColor="Black" FontFamily="{StaticResource timesNewRomanBold}" HorizontalOptions="CenterAndExpand" Text="Report generated on 27 December, 2019" Margin="0,0,0,5"/>
<ListView x:Name="renewListView"
Footer=""
pdf:PdfRendererAttributes.ListRendererDelegate="{StaticResource PDFSampleListRendererDelegate}"
BackgroundColor="White"
SeparatorVisibility="None"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="false">
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ScrollView>
</Grid>
In code behind.
public partial class MotorRenewalFinalPrint : ContentPage
{
public MotorRenewalFinalPrint()
{
InitializeComponent();
}
public MotorRenewalFinalPrint (List<MotorRenewalPrintData> newdd)
{
InitializeComponent ();
Title = "Save as PDF";
renewListView.ItemsSource = newdd;
}
private void pdf_Clicked(object sender, EventArgs e)
{
var pdf = PDFManager.GeneratePDFFromView(mainGrid);
var fileManager = DependencyService.Get<IFileIO>();
string filePath = Path.Combine(fileManager.GetMyDocumentsPath(), "formpdf.pdf");
DependencyService.Get<IPdfSave>().Save(pdf, filePath);
DependencyService.Get<IPDFPreviewProvider>().TriggerPreview(filePath);
}
}
Updated...
MainClass
public partial class MainPage : ContentPage
{
private List<Customer> Cus = new List<Customer>();
public MainPage()
{
InitializeComponent();
Customer ss1 = new Customer { Names = "test1", Ages = "10"};
Customer ss2 = new Customer { Names = "test2", Ages = "30" };
Cus.Add(ss1);
Cus.Add(ss2);
//rListView.ItemsSource = Cus;
}
private void Button_Clicked(object sender, System.EventArgs e)
{
var pdf = PDFManager.GeneratePDFFromView(mainGrid);
var fileManager = DependencyService.Get<IFileIO>();
string filePath = Path.Combine(fileManager.GetMyDocumentsPath(), "testpdf.pdf");
DependencyService.Get<IPdfSave>().Save(pdf, filePath);
DependencyService.Get<IPDFPreviewProvider>().TriggerPreview(filePath);
}
}
view
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TestPDF"
xmlns:pdf="clr-namespace:PdfSharp.Xamarin.Forms;assembly=PdfSharp.Xamarin.Forms"
x:Class="TestPDF.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:PDFSampleListRendererDelegate x:Key="PDFSampleListRendererDelegate" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<Grid x:Name="mainGrid">
<ScrollView>
<StackLayout Margin="0,0,0,5">
<Label Text="Welcome to Xamarin.Forms!" HorizontalOptions="CenterAndExpand" TextColor="Black" FontSize="18" VerticalOptions="Center" />
<ListView pdf:PdfRendererAttributes.ListRendererDelegate="{DynamicResource PDFSampleListRendererDelegate}" HeightRequest="150"/>
<Button Text="click" Clicked="Button_Clicked"/>
</StackLayout>
</ScrollView>
</Grid>
</ContentPage.Content>
</ContentPage>
PDFSampleListRendererDelegate
public class PDFSampleListRendererDelegate : PdfListViewRendererDelegate
{
public override void DrawCell(ListView listView, int section, int row, XGraphics page, XRect bounds, double scaleFactor)
{
XFont font = new XFont("times" ?? GlobalFontSettings.FontResolver.DefaultFontName, 15);
var yourObject = (listView.ItemsSource as List<Customer>).ElementAt(row);
page.DrawString(yourObject.Names, font, XBrushes.Black, bounds,
new XStringFormat
{
LineAlignment = XLineAlignment.Center,
Alignment = XStringAlignment.Center,
});
}
public override void DrawFooter(ListView listView, int section, XGraphics page, XRect bounds, double scaleFactor)
{
base.DrawFooter(listView, section, page, bounds, scaleFactor);
}
public override double GetFooterHeight(ListView listView, int section)
{
return base.GetFooterHeight(listView, section);
}
}
you should override DrawCell method
i.e:
public override void DrawCell(ListView listView, int section, int row, XGraphics page, XRect bounds, double scaleFactor)
{
XFont font = new XFont(yourCustomFont ?? GlobalFontSettings.FontResolver.DefaultFontName, label.FontSize * scaleFactor);
var yourObject = (listView.ItemSource as List<YourObjType>).ElementAt(row);
page.DrawString(yourObject.Text, font, XColors.Black, bounds,
new XStringFormat {
LineAlignment = XLineAlignment.Center,
Alignment = XStringAlignment.Center,
});
}

Is there any way that I can speed up the adding of new elements to a page when using the C# back end?

I have code that works but I notice it's rather slow to create the page elements.
Here's what I have so far. Note that I'm not adding everything at once as I found that when I did the page creation was even slower.
public void CreateSwitchSection(bool? selected)
{
Application.Current.Resources.TryGetValue("FrameBorder", out object frameBorder);
var st = new StackLayout { Orientation = StackOrientation.Vertical, Spacing = 0 };
st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Take(20)));
st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(20).Take(20)));
st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(40).Take(20)));
st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(60).Take(20)));
st.Children.Add(AddSwitchRows(selected, App.cardSetWithWordCount.Skip(80).Take(20)));
var fr = new Frame { Style = (Style)frameBorder };
var fs = new FrameStack { };
var ht = new HeaderTemplate()
{
Text = "CHOOSE CARD SETS FOR THE DECK"
};
fs.Children.Add(ht);
fs.Children.Add(st);
fs.Children.Add(new LineTemplate());
fr.Content = fs;
details.Children.Clear();
details.Children.Add(fr);
}
private StackLayout AddSwitchRows(bool? selected, IEnumerable<CardSetWithWordCount> data)
{
var stack = new StackLayout
{
Orientation = StackOrientation.Vertical,
Spacing = 0
};
foreach (var x in data)
{
var cell = new BadgeGridTemplate
{
BindingContext = x,
Text = x.Name,
State = selected == true ? "E" : "D",
Message = x.TotalWordCount.ToString(),
TapCommand = (Command)vm.SelectCardSetCmd,
RowId = x.Id,
Separator = true
};
stack.Children.Add(cell);
}
return stack;
}
For reference here is the BadgeGridTemplate I coded:
<?xml version="1.0" encoding="UTF-8"?>
<t:BaseGridTemplate xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:t="clr-namespace:Japanese.Templates"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
xmlns:b="clr-namespace:Behaviors;assembly=Behaviors"
xmlns:converters="clr-namespace:Japanese.Converters;assembly=Japanese"
x:Class="Japanese.Templates.BadgeGridTemplate"
x:Name="this"
HeightRequest="{DynamicResource GridHeight}" Margin="0"
Orientation="Vertical" Spacing="0">
<BoxView HeightRequest="1" HorizontalOptions="FillAndExpand" IsVisible="{Binding Separator, Source={x:Reference this}}" BackgroundColor="{DynamicResource LineColor}" Margin="0" />
<Grid Padding="20,0" VerticalOptions="CenterAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding Text, Source={x:Reference this}}" TextColor="{DynamicResource LabelColor}" Style="{StaticResource LabelText}" VerticalTextAlignment="Center" WidthRequest="30" />
<t:Button Grid.Column="1" Meta="GsT" RowId="{Binding RowId, Source={x:Reference this}}" State="{Binding State, Source={x:Reference this}}" TapCommand="{Binding TapCommand, Source={x:Reference this}}" Text="{Binding Message, Source={x:Reference this}}" Theme="{Binding Theme}" WidthRequest="30" />
</Grid>
</t:BaseGridTemplate>
What are we trying to do:
Take a list of data from some source, Put them in a list
What your code is doing:
taking all the data building(all 100 AddSwitchRows ) UI right then.
Those hundreds of UI has to be rendered all at once on the screen. (even if the user is not going to look at them all)
Suggestion: How should we do this:
I highly suggest using a Listview.
Or FlowListview for grids https://github.com/daniel-luberda/DLToolkit.Forms.Controls
Why?
Listview will only try to draw UI for the screen user is looking at.
If there are a thousand more items needed. It will be built only when the user scrolls down to that portion.
If you want to make multiple kinds of cells which depends on the data you receive we should use DataTemplate with ListView
More Information on Microsoft's Official Docs
Example
I have added 1000 items in the listView's ItemSource when clicked on the button in header.
You can add your template in the Listview.datatemplate tag and bind your ViewModel to this view
View
And if you want to change item's view as per a property value. Use DataTemplate selecter property of ListView
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:stackAnswerApp"
x:Class="stackAnswerApp.MainPage"
x:Name="Root">
<ContentPage.BindingContext>
<local:MainPageViewModel />
</ContentPage.BindingContext>
<ListView ItemsSource="{Binding ListItems}" RowHeight="100">
<ListView.Header>
<Button Text="Start Adding" Command="{Binding StartAddingItems}" />
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!-- whatever is your template -->
<StackLayout BackgroundColor="Aqua">
<Label Text="{Binding Text1}" />
<Button BackgroundColor="Blue"
Text="Go" BindingContext="{Binding Source={x:Reference Root},Path=BindingContext}"
Command="{Binding ButtonTapped}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
ViewModel
class AModel
{
public string Text1 { get; set; }
public string Text2 { get; set; }
}
class MainPageViewModel
{
public ObservableCollection<AModel> ListItems { get; set; }
private const int TotalRows = 1000;
public ICommand StartAddingItems
{
get
{
return new Command(async () =>
{
//add too many items in the list
await Task.Run(async () => { await AddItemsToList(); });
});
}
}
private async Task AddItemsToList()
{
for (var i = 0; i < TotalRows; i++)
{
ListItems.Add(new AModel() {Text1 = $"index {i}", Text2 = $"tap {i}"});
}
}
public ICommand ButtonTapped
{
get
{
return new Command((() =>
{
//button in the list was tapped
}));
}
}
public MainPageViewModel()
{
ListItems = new ObservableCollection<AModel>();
}
}
You can try using BatchBegin() on stacks variable before the loop of child adds in your function AddSwitchRows and BatchCommit() after the loop. And if that works, do the same to your parent stack variable st in CreateSwitchSection.
Ie.
stacks.BatchBegin();
foreach var x in data
{
…
}
stacks.BatchCommit();
This may resolve it as many languages displaying forms like to recalculate layout every time (expensive) the collection/list/children are added/removed/updated. Batching allows the layout recalculations to be done once instead of every time the list changes.
If the order matters, I haven't seen another way to do this.
If the order doesn't matter, You could split the StackLayout in to multiple StackLayouts and just add the individual elements inside asynchronous threads using Task.WhenAll.
Task.WhenAll is like having multiple people do work for you at the same time, instead of just 1 person.
I think Problem is in
App.cardSetWithWordCount
It may be a linq query
I think this goes to db every time .Just run it once and save it on variable or property
var globalApp_cardSetWithWordCount = App.cardSetWithWordCount.ToList()
Linq is executed when to list or toarray is called.
List<int> ints = new List<int>();
foreach(var y in query)
ints.Add(y.Count());
return ints.ToArray();
then use same take and skip format

What's the optimal way to show a check mark against four settings with forms?

I have a screen that shows four rows: "English", "Romaji", "Kana" and "Kanji". The user can select one of the four and when selected a check mark will appear next to it. Currently it's implemented like this (just showing the first two):
englishSide.GestureRecognizers.Add(new TapGestureRecognizer
{
NumberOfTapsRequired = 1,
Command = new Command(() =>
{
englishImage.IsVisible = true;
romajiImage.IsVisible = false;
kanaImage.IsVisible = false;
kanjiImage.IsVisible = false;
})
});
romajiSide.GestureRecognizers.Add(new TapGestureRecognizer
{
NumberOfTapsRequired = 1,
Command = new Command(() =>
{
englishImage.IsVisible = false;
romajiImage.IsVisible = true;
kanaImage.IsVisible = false;
kanjiImage.IsVisible = false;
})
});
As the number of choices increases from 4 to more I can see this would get a lot more complicated. Is there a more optimal way to do this?
For reference here's the Xaml:
<TableSection Title="Card Front Side">
<ViewCell>
<StackLayout x:Name="englishSide" Padding="20,0,20,0"
Orientation="Horizontal" VerticalOptions="Center">
<Label Text="English" XAlign="Center"/>
<Image x:Name="englishImage" Source="check.png"
HorizontalOptions="EndAndExpand" IsVisible="false" />
</StackLayout>
</ViewCell>
<ViewCell>
<StackLayout x:Name="romajiSide" Padding="20,0,20,0"
Orientation="Horizontal" VerticalOptions="Center">
<Label Text="Romaji" XAlign="Center"/>
<Image x:Name="romajiImage" Source="check.png"
HorizontalOptions="EndAndExpand" IsVisible="false" />
</StackLayout>
</ViewCell>
What you would need is a proper viewmodel pattern where the ListView.ItemsSource is an IEnumerable<YourViewModel>. I use Prism for Xamarin.Forms to facilitate this.
Read more:
https://msdn.microsoft.com/en-us/library/ff798384.aspx?f=255&MSPPError=-2147217396
https://github.com/PrismLibrary/Prism
YourViewModel will be the child viewmodel containing all the logic and ecapsulating the business object.
public class MyViewModel
{
// INotifyPropertyChanged would be needed
public MyModel Model {get;set;}
public bool IsChecked {get;set;}
public string Image => this.IsChecked? "theImage.jpg" : null;
}
Hope that helps.

How to set the selectedItem of a listpicker using databinding MVVM

I have a listpicker on my Category details page
<toolkit:ListPicker HorizontalAlignment="Left" Name="ListPickerCategoryTypes"
ItemsSource="{Binding CategoryTypes, Mode=TwoWay}" Header="Category Types;"
VerticalAlignment="Top" Width="438" Margin="9,6,0,0" SelectedItem="{Binding CategoryTypeName, Mode=TwoWay}" >
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryTypeName}" Tag="{Binding Id}"></TextBlock>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
The list picker populates correctly but when I navigate to the details page the selectedItem is never set?
I have a category Name textbox that is correctly displaying the category name that was selected so I know it has the data just not sure what I am doing wrong
with the listpicker. I thought maybe it was that I wasnt using the CategoryTypeName I was trying to use the Category Type ID that is on my model.
I am using MVVM so I would like to be able to do this in my view model.
Addtional Code to help
The SettingProduct view lists all the products I have in a listbox.
<Grid x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<ListBox x:Name="TileList" ItemTemplate="{StaticResource TileProductDataTemplate}"
ItemsSource="{Binding DisplayProducts}"
Margin="6,20,6,-8"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}" >
<Custom:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding EditDetailsPageCommand}" />
</i:EventTrigger>
</Custom:Interaction.Triggers>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
on the tap of a product the event command executes...
this.EditDetailsPageCommand = new RelayCommand(this.GotoEditProductDetail, this.CanGotoEditProductDetail);
public void GotoEditProductDetail()
{
//Messenger.Default.Send<NavigateToPageMessage>(new NavigateToPageMessage() { PageName = "SettingsProductDetail", SendObject = DisplayProducts });
// Messenger.Default.Send<NavigateToPageMessage>(new NavigateToPageMessage(){PageName = "SettingsProductDetail", SendObject = SelectedProduct});
Navigator.NavigateTo("SettingsProductDetail", SelectedProduct);
}
It navigates to teh SettingsProductDetail View and in the constructor it errors on this line when setting the DataContext
SettingsProductDetail Xaml
<toolkit:ListPicker HorizontalAlignment="Left" Name="ListPickerCategoryTypes"
ItemsSource="{Binding CategoryTypes}"
Header="Product Types;"
VerticalAlignment="Top" Width="438" Margin="9,6,0,0"
SelectedItem="{Binding SelectedCategoryType, Mode=TwoWay}"
>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryTypeName}" ></TextBlock>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
public SettingsProductDetail()
{
InitializeComponent();
this.DataContext = new ViewModel.SettingsProductDetailViewModel(Navigator.Object);
}
In my viewmodel for the SettingsProductDetail
I have two properties one for teh Itemsource and one for the selectedItem
public ObservableCollection<CategoryType> CategoryTypes
{
get { return _categoryType; }
set
{
if (value != _categoryType)
{
_categoryType = value;
base.RaisePropertyChanged("CategoryType");
}
}
}
public Model.CategoryType SelectedCategoryType
{
get { return _selectedCategoryType; }
set
{
if (value != _selectedCategoryType)
{
_selectedCategoryType = value;
base.RaisePropertyChanged("SelectedCategoryType");
}
}
}
In the construct is where I am populating the SelectedCategoryType from the object passed from the product view.
public SettingsProductDetailViewModel(object sendObject)
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
ProductDetail = sendObject as DisplayProducts;
if (ProductDetail == null)
{
ProductDetail = new DisplayProducts();
}
else
{
SelectedCategoryType = new CategoryType();
SelectedCategoryType.Id = ProductDetail.FkCategoryTypeID;
SelectedCategoryType.CategoryTypeName = ProductDetail.CategoryTypeName;
}
_TheStoreDataContext = new TheStoreDataContext(ConnectionString);
PopulateHelperObjects();
SettingsProductDetailSaveCommand = new RelayCommand<Model.Product>(param => SaveRecord(), param => (ProductDetail != null));
SettingsProductDetailCancelCommand = new RelayCommand(CancelRecord, () => true);
}
}
Your ViewModel needs to have a property called CategoryTypeSelected which will be of the type T, where T is the type of objects that are in the collection CategoryTypes which you used to bind your ItemsSource. This way, the CategoryTypeSelected will always be the item selected from the list. You bind it like this:
<toolkit:ListPicker HorizontalAlignment="Left" Name="ListPickerCategoryTypes"
ItemsSource="{Binding CategoryTypes, Mode=TwoWay}" ......
SelectedItem="{Binding CategoryTypeSelected, Mode=TwoWay}" >
Of course, your ViewModel needs to implement INotifyPropertyChanged.
Ok I now have it working...
//It was my constructor and how I was setting the property. changed the constructor to this... if (ProductDetail == null)
{
ProductDetail = new DisplayProducts();
}
else
{
SelectedCategoryType = new CategoryType {CategoryTypeName = ProductDetail.CategoryTypeName};
//SelectedCategoryType.Id = ProductDetail.FkCategoryTypeID;
}
// and then changed the property setter to this... set
{
if (_categoryType.Contains(value))
{
_selectedCategoryType = value;
base.RaisePropertyChanged("SelectedCategoryType");
}
else
{
_selectedCategoryType = _categoryType.FirstOrDefault((o) => o.CategoryTypeName == value.CategoryTypeName);
base.RaisePropertyChanged("SelectedCategoryType");
}
//if (value != _selectedCategoryType)
//{
// _selectedCategoryType = value;
// base.RaisePropertyChanged("SelectedCategoryType");
//}
}

Resources