wp7 listbox selection - windows-phone-7

I have a listBox with 1000+ items.
in xaml
<ListBox
//some code here
SelectionChanged="OnSelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<controls:MyCustomItem/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In cs
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var item = lb.SelectedItem as MyCustomItem;
var vm = DataContext as ViewModel;
if (vm == null) return;
foreach (var it in vm.ItemsForBinding)
{
it.IsSelected = false;
}
item .IsSelected = true;
}
in MyCustomItem xaml
<UserControl
//Some code here
Style="{Binding Path=IsSelected, Converter = {StaticResource BoolToStyle}}">
<Border
</Border>
</UserControl>
Where IsSelected - one of property of my ViewModel. Converter return one of two style (first if item selected, second - if no)
Always work, but I know - it's very resource-intensive, and the wrong decision.
How to do it right?

This post help me!
Just override default ItemContainerStyle for my ListBox.
I removed:
metod OnSelectionChanged, UserControl MyCustomItem, converter for styles and
<ListBox.ItemTemplate>
<DataTemplate>
<controls:MyCustomItem/>
</DataTemplate>
</ListBox.ItemTemplate>
from xaml.

Related

Multi-select Listview

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

How to change Label Text color of masterPageitem after selection

I'm new to Xamarin framework and want to create an app using Master-Detail Page
I did simple Master-Detail Navigation page demo from xamarin websit
master-detail-page xamarin webise
only difference is I used ViewCell inside DataTemplate.In ViewCell I have Label
instead of Image.
after clicking on MasterPageItems navigation is working fine but now I want to change the label Text color also .
<ListView x:Name="listView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" RowHeight="50" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Title}" TextColor="#1ca7ec" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(ContactsPage)));
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
}
I think you can do in this way:
1- in your model you should have a "TextColor" property and a "Selected" property
public bool Selected { get; set; }
// I think you should not return "Color" type (for strong MVVM) but, for example, a value that you can convert in XAML with a IValueConverter...
public Color TextColor
{
get
{
if (Selected)
return Color.Black;
else
return Color.Green;
}
}
2- In your XAML you should have something like
<ListView SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding List}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}" TextColor="{Binding TextColor}" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
3- and in your ViewModel something like
MyModel _selectedItem { get; set; }
public ObservableCollection<MyModel> List { get; set; } = new ObservableCollection<MyModel>();
public MyModel SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem != null)
_selectedItem.Selected = false;
_selectedItem = value;
if (_selectedItem != null)
_selectedItem.Selected = true;
}
}
When your item in the list is selected , SelectedItem property change and Selected property in your model became True or False, changing the TextColor property (I use PropertyChanged.Fody for INPC).
Hope this help
You can find the repo on GitHub
Instead of use a TextColor Property in your Model, I think you can also use only Selected property and an IValueConverter that convert Selected property to a color

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");
//}
}

WP7 Itemtemplate click event fires on collapsed ExpanderView

I am using the ExpanderView control available in the Silverlight Toolkit with some custom templates. It all works well, but when the ExpanderView is collapsed, and I click on the area below the Header where an item resides when the ExpanderView is expanded. The click event of that item fires.
How can i fix this? Should I somehow remove the tap commands or remove the ItemPanel when the ExpanderView is collapsed and add it again when it's being expanded?
<DataTemplate x:Key="CustomItemTemplate">
<Image delay:LowProfileImageLoader.UriSource="{Binding}" Width="156" Height="95" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding Storage.ImageTapCommand, Source={StaticResource Locator}}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
</DataTemplate>
<toolkit:ExpanderView Grid.Column="1" Header="{Binding}"
Expander="{Binding}" IsExpanded="{Binding IsExpanded, Mode=TwoWay}"
ItemsSource="{Binding Files}" HeaderTemplate="{StaticResource CustomHeaderTemplate}"
ExpanderTemplate="{StaticResource CustomExpanderTemplate}"
ItemTemplate="{StaticResource CustomItemTemplate}" >
<toolkit:ExpanderView.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</toolkit:ExpanderView.ItemsPanel>
</toolkit:ExpanderView>
You can change the IsHitTestVisible property of the root UIElement for each of your expander items every time the ExpanderView is expanded/collapsed, and also just after initially binding the ExpanderView (hooking up to ExpanderView.LayoutUpdated works fine for that purpose). Here's an example that fixed the issue for me:
private void FixExpanderItemsInteractivity(ExpanderView expanderView)
{
foreach (var item in expanderView.Items)
{
ContentPresenter contentPresenter = expanderView.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
if (contentPresenter != null)
{
UIElement expanderItemRootElement = VisualTreeHelper.GetChild(contentPresenter, 0) as UIElement;
if(expanderItemRootElement != null)
{
expanderItemRootElement.IsHitTestVisible = expanderView.IsExpanded;
}
}
}
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
FixExpanderItemsInteractivity(sender as ExpanderView);
}
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
FixExpanderItemsInteractivity(sender as ExpanderView);
}
private void Expander_LayoutUpdated(object sender, EventArgs e)
{
FixExpanderItemsInteractivity(sender as ExpanderView);
}

How to change Foreground color of TextBlock in DataTemplate from code on Windows Phone?

I'd like to change Foreground color of TextBlock (bellow TitleText and DateText) in DataTemplate from code.
<ListBox x:Name="listBox1" ItemsSource="{Binding}" ScrollViewer.ManipulationMode="Control" SelectionChanged="listBox1_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stackPanel1" HorizontalAlignment="Stretch" Orientation="Horizontal">
<TextBlock FontSize="35" x:Name="TitleText" Text="{Binding Title}" Width="386" Foreground="Black" />
<TextBlock FontSize="25" x:Name="DateText" Text="{Binding Date}" Width="78" Foreground="Black" />
<TextBlock x:Name="Id" Text="{Binding Id}" Visibility="Collapsed" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'd like to do like this in code behind. But It seems not be able to access x:Name property in DataTemplate.
this.TitleText.Foreground = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0);
Does anyone know a good solution for this ?
Why don't you do it the Fast way instead of crawling the Visual Tree.
<TextBlock FontSize="35" Text="{Binding Title}" Width="386" Foreground="[Binding Color}" />
Then all you have to do is:
Add a Color Brush Property in your Collection
Change this property to the color you want
Make sure this property implement INotify or is a Dependency Property
Example
XAML
<Grid>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
<TextBlock Text="{Binding Title}" Foreground="{Binding TitleColor}" />
<TextBlock Text="{Binding Date}" Foreground="Black" />
<TextBlock Text="{Binding Id}" Visibility="Collapsed" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
CodeBehind
public partial class MainPage : Page
{
public ObservableCollection<TEST> TestCollection { get; private set; }
public MainWindow()
{
InitializeComponent();
TestCollection = new ObservableCollection<TEST>();
TestCollection.Add(new TEST()
{
TitleColor = Brushes.Black,
ID = 0,
Title = "A",
Date = DateTime.Now,
});
TestCollection.Add(new TEST()
{
TitleColor = Brushes.Red,
ID = 1,
Title = "B",
Date = DateTime.Now.AddDays(1),
});
DataContext = TestCollection;
}
}
public class TEST : INotifyPropertyChanged
{
private Brush _TitleColor;
public Brush TitleColor
{
get
{
return _TitleColor;
}
set
{
_TitleColor = value;
OnPropertyChanged("TitleColor");
}
}
private int _ID;
public int ID
{
get
{
return _ID;
}
set
{
_ID = value;
OnPropertyChanged("ID");
}
}
private string _Title;
public string Title
{
get
{
return _Title;
}
set
{
_Title = value;
OnPropertyChanged("Title");
}
}
private DateTime _Date;
public DateTime Date
{
get
{
return _Date;
}
set
{
_Date = value;
OnPropertyChanged("Date");
}
}
public TEST()
{
}
#region INotifyProperty
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
#endregion
}
You can use FindItem method to find element in visual tree by it name and then change it Foregorund.
((listBox.ItemContainerGenerator.ContainerFromIndex(5) as FrameworkElement).FindName("TitleText") as TextBlock).Foreground = new SolidColorBrush(Color.FromArgb(255, 128, 128, 128));
where 5 is your item index
To Expand on MyKuLLSKI's answer, if your change is based on some value already in your object (eg. an int property that is greater than 5), you could use a ValueConverter (see here for an example) to read the value and return a brush. That is cleaner than adding a colour to your model since it is (arguably) UI related rather than data related.
MyKuLLSKI gave me perfect solution. But I couldn't make it.
I was struggling and I found my problem. I wrote the answer (only for me ) in my blog. Please take a glance it.
Cannot bind two types of data source to one UI target
http://myprogrammingdial.blogspot.com/2012/03/cannot-bind-two-types-of-data-source-to.html

Resources