When I change selection in combo box debugger will hit TestSelectedItem {set} in User class, but I want to hit debugger, TestSelectedItem {set} in MainWindow class. Please suggests me correct solution.
public class User
{
public string Name { get; set; }
public ObservableCollection<string> TypeCollection { get; set; }
private string _testSelectedItem;
public string TestSelectedItem
{
get { return _testSelectedItem; }
set { _testSelectedItem = value; }
}
public User()
{
TypeCollection = new ObservableCollection<string>();
TypeCollection.Add("A");
TypeCollection.Add("B");
TypeCollection.Add("C");
}
}
public partial class MainWindow : Window
{
public List<User> Users { get; set; }
private string _testSelectedItem;
public string TestSelectedItem
{
get { return _testSelectedItem; }
set { _testSelectedItem = value; }
}
public MainWindow()
{
InitializeComponent();
Users = new List<User>();
Users.Add(new User() { Name = "User 1" });
Users.Add(new User() { Name = "User 2" });
Users.Add(new User() { Name = "User 3" });
myGrid.DataContext = this;
}
}
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication11"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid Name ="myGrid">
<StackPanel>
<DataGrid Name="MyDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding Users}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"
Header="Name"
Width="90"
CanUserResize="False"
CanUserSort="False"
CanUserReorder="False"
IsReadOnly="True" />
<DataGridComboBoxColumn Header="Product Type"
DisplayMemberPath="ProductType"
SelectedValuePath="ProductTypeId"
SelectedValueBinding="{Binding SelectedValuePath, UpdateSourceTrigger=PropertyChanged}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding TypeCollection}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding TypeCollection}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridTemplateColumn Header="Bug">
<DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<ComboBox Height="25" Width="200"
x:Name ="ppCombo"
ItemsSource="{Binding TypeCollection }"
SelectedItem="{Binding TestSelectedItem ,Mode=TwoWay ,UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
</Window>
I think I just found a solution but not sure is that the correct one
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Path=TestSelectedItem}">
Related
I am working on providing wishlist feature for my app by tapping wishlist icon on each product in list through MVVM. Once tapped, an API call is made to update database(add/remove from wishlist table). Based on result from api call, I updated the specific product's respective property to either 'True' or 'False'. Once property updated, I want to change the icon image source of corresponding product. I am using trigger on wishlist icon to differentiate non-wishlist and wiahlist products while binding the list itself.
My code is below,
MODEL
public class PublisherProducts
{
public long ProductId { get; set; }
public string ProductName { get; set; }
public string ImageURL { get; set; }
public decimal Price { get; set; }
public bool IsWishlistProduct { get; set; }
}
VIEWMODEL
public class OnlineStoreViewModel : BaseViewModel
{
private ObservableCollection<PublisherProducts> publisherProducts;
public Command<long> WishlistTapCommand { get; }
public OnlineStoreViewModel()
{
publisherProducts = new ObservableCollection<PublisherProducts>();
WishlistTapCommand = new Command<long>(OnWishlistSelected);
}
public ObservableCollection<PublisherProducts> PublisherProducts
{
get { return publisherProducts; }
set
{
publisherProducts = value;
OnPropertyChanged();
}
}
public async Task GetProducts(long selectedCategoryId)
{
try
{
...
PublisherProducts = new ObservableCollection<PublisherProducts>(apiresponse.ProductList);
...
}
catch (Exception ex) { ... }
finally { ... }
}
async void OnWishlistSelected(long tappedProductId)
{
if (tappedProductId <= 0)
return;
else
await UpdateWishlist(tappedProductId);
}
public async Task UpdateWishlist(long productId)
{
try
{
var wishlistResponse = // api call
var item = PublisherProducts.Where(p => p.ProductId == productId).FirstOrDefault();
item.IsWishlistProduct = !item.IsWishlistProduct;
PublisherProducts = publisherProducts; *Stuck here to toggle wishlist icon*
await App.Current.MainPage.DisplayAlert("", wishlistResponse.Message, "Ok");
}
catch (Exception ex) { ... }
finally { ... }
}
}
XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" ... >
<ContentPage.Content>
<ScrollView>
<StackLayout Padding="15,0,15,10">
<FlexLayout x:Name="flxLayout" BindableLayout.ItemsSource="{Binding PublisherProducts}" ...>
<BindableLayout.ItemTemplate>
<DataTemplate>
<AbsoluteLayout Margin="6" WidthRequest="150">
<Frame Padding="0" WidthRequest="150" CornerRadius="10" HasShadow="True">
<StackLayout Orientation="Vertical" Padding="10" HorizontalOptions="FillAndExpand">
<Image Source="{Binding ImageURL}" WidthRequest="130" HeightRequest="130" HorizontalOptions="Center"/>
<Label Text="{Binding ProductName}" Style="{StaticResource ProductNameStyle}"></Label>
...
<StackLayout ...>
...
<Frame x:Name="wlistFrame" Padding="0" WidthRequest="30" HeightRequest="30" CornerRadius="10" BorderColor="#02457A">
<StackLayout Orientation="Horizontal" VerticalOptions="Center" HorizontalOptions="Center">
<Image x:Name="wlImage" WidthRequest="13" HeightRequest="12" HorizontalOptions="Center" VerticalOptions="Center" Source="ic_wishlist_open">
<Image.Triggers>
<DataTrigger TargetType="Image" Binding="{Binding IsWishlistProduct}" Value="true">
<Setter Property="Source" Value="ic_wishlist_close" />
</DataTrigger>
</Image.Triggers>
</Image>
</StackLayout>
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type local:OnlineStoreViewModel}}, Path=WishlistTapCommand}" CommandParameter="{Binding ProductId}" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
</Frame>
</StackLayout>
</StackLayout>
</Frame>
</AbsoluteLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
I am stuck at this place to change wishlist icon, when 'IsWishlistProduct' property value is changed in UpdateWishlist().
Guessing through your code, the BaseViewModel contains code similar to the following:
public class BaseViewModel : INotifyPropertyChanged
{
...
public event PropertyChangedEventHandler PropertyChanged;
...
public void OnPropertyChanged(string name)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
...
}
And your viewmodel should be like this:
...
public ObservableCollection<PublisherProducts> PublisherProducts
{
get { return publisherProducts; }
set
{
publisherProducts = value;
OnPropertyChanged(nameof(PublisherProducts));
}
}
...
As Jason mentioned, If there is a change in data in the ViewModel, it is reflected in the UI when it is notified to the View through NotifyPropertyChanged. You already implemented "OnPropertyChanged" function in your BaseViewModel but it seems you don't pass the object name.
I'm trying to setup a gallery of images, that also features the ability to click on the image to show/hide a grid with image description, using the MVVM process.
However, i think my model, view model and view is not correctly setup because i am only able to display the images in the stacklayout if i use BindableLayout.ItemsSource="{Binding GalleryList.Gallery}" in the top stacklayout, but not able to access the other parts of the class, i.e. only the Gallery list.
So essentially, I cant access InfoGridVisible binding, since the it can't find/reach it? (i'm not sure to be honest).
Here is a snippet of my work so far:
View - testpage.xaml:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="testproject.Pages.testpage"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:styles="clr-namespace:testproject.Styles"
xmlns:behaviours="clr-namespace:testproject.ViewModels"
mc:Ignorable="d"
xmlns:pancakeview="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
BackgroundColor="Orange"
x:Name="Root">
<ContentPage.Content>
<ScrollView>
<Grid
Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!--PROBLEM STARTS HERE -->
<StackLayout x:Name="GalleryStk" Grid.Row="1" Orientation="Vertical" BindableLayout.ItemsSource="{Binding GalleryList.Gallery}" Margin="10,0,10,0">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="5" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="AUTO"/>
<RowDefinition Height="AUTO"/>
</Grid.RowDefinitions>
<Frame x:Name="ImageFrame" CornerRadius="20" Padding="0" IsClippedToBounds="True" Grid.Row="0">
<Image Source="{Binding Image}" Aspect="AspectFill" Grid.RowSpan="2" HorizontalOptions="Center" VerticalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}" Command="{Binding TapCommand}"/>
</Image.GestureRecognizers>
</Image>
</Frame>
<!--<Image Source="ShadowOverlay" Grid.RowSpan="2" Aspect="Fill" VerticalOptions="End" HorizontalOptions="Fill" />-->
<Grid x:Name="InfoGrid" RowSpacing="10" ColumnSpacing="10" Grid.Row="0" IsEnabled="False" IsVisible="{Binding GalleryList.InfoGridVisible, Source={x:Reference InfoGrid}}">
<Grid.RowDefinitions>
<RowDefinition Height="AUTO"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="AUTO"/>
</Grid.ColumnDefinitions>
<BoxView Color="Black" Opacity="0.5" CornerRadius="20" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Grid.Row="0" Grid.RowSpan="5" Grid.ColumnSpan="3"/>
<Label Text="{Binding Title}" Padding="10,10,0,0" Grid.Row="0" Grid.ColumnSpan="3" Style="{StaticResource TitleLabel}" LineBreakMode="NoWrap"/>
<!--<controls:CircleImage Source="{Binding ProfileImage}" Aspect="AspectFill" Grid.Row="1" Grid.Column="0" WidthRequest="25" HeightRequest="25" />-->
<Label Text="{Binding Description}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Padding="10,0,10,0" Grid.Row="1" Grid.RowSpan="4" Grid.ColumnSpan="3" Style="{StaticResource HandleLabel}"/>
<!--<Button Text="See More" x:Name="ExpandContractButton" Clicked="ExpandContractButton_Clicked" Padding="10,0,10,0" Grid.Row="2" Grid.RowSpan="3" Grid.ColumnSpan="3"/>-->
<!--<StackLayout Orientation="Horizontal" VerticalOptions="Center" Grid.Column="2" Grid.Row="1" Spacing="5">
<Image Source="Eye"/>
<Label Text="{Binding ViewCount, StringFormat='{0:N0}'}" Style="{StaticResource HandleLabel}" />
<Label Text="views" Style="{StaticResource BodyLabel}"/>
</StackLayout>-->
</Grid>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</Grid>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Code behind for view - testpage.xaml.cs
public partial class testpage : ContentPage
{
private Image _galleryImage;
public Grid GalleryInfoGrid;
private static testpage _instance;
public UserProfileViewModel vm { get; }
public static testpage Instance
{
get
{
if (_instance == null)
_instance = new testpage();
return _instance;
}
}
public testpage()
{
InitializeComponent();
vm = new UserProfileViewModel();
BindingContext = new UserProfileViewModel();
//var _galleryInfoGrid = (Grid)Root.FindByName("InfoGrid");
//_galleryInfoGrid.IsEnabled = true;
//_galleryInfoGrid.IsVisible = true;
}
Model:
{
public List<GalleryImage> Gallery { get; set; }
public string InfoGridVisible;
public string InfoGridEnabled;
}
public class GalleryImage
{
public string Title { get; set; }
public string Image { get; set; }
public string Description { get; set; }
}
Services (data):
public class ProfileService
{
private static ProfileService _instance;
public static ProfileService Instance
{
get
{
if (_instance == null)
_instance = new ProfileService();
return _instance;
}
}
public GalleryList GetGallery()
{
return new GalleryList
{
InfoGridEnabled = "False",
InfoGridVisible = "False",
Gallery = new List<GalleryImage>
{
new GalleryImage { Title="sevilla01.jpg", Image = "sevilla01.jpg", Description="Description1Description1Description1Description1 Description1Description1Description1 Description1Description1" },
new GalleryImage { Title="sevilla02.jpg", Image = "sevilla02.jpg", Description="Description2 Description1Description1Description1Description1Descript ion1Description1" },
new GalleryImage {Title="sevilla03.jpg", Image = "sevilla03.jpg", Description="Description3Description1Description1Description1" },
new GalleryImage {Title="sevilla04.jpg", Image = "sevilla04.jpg", Description="Description4Description1Description1" },
new GalleryImage {Title="sevilla05.jpg", Image = "sevilla05.jpg", Description="Description5Description1" },
new GalleryImage {Title="sevilla06.jpg", Image = "sevilla06.jpg", Description="Description6" },
new GalleryImage {Title="sevilla07.jpg", Image = "sevilla07.jpg", Description="Description7Description1Description1Description1Description1Description1Description1Description1Description1 Description1" }
}
};
}
}
View Model - UserProfileViewModel.cs:
public class UserProfileViewModel : BindableObject
{
//Models
private Profile _profile;
private GalleryList _galleryList;
int taps = 0;
ICommand tapCommand;
public UserProfileViewModel()
{
Profile = ProfileService.Instance.GetProfile();
GalleryList = ProfileService.Instance.GetGallery();
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
//var info1Grid = testpage.Instance.GalleryInfoGrid;
//info1Grid.IsVisible = true;
//info1Grid.IsEnabled = true;
//GalleryList.InfoGridEnabled = "False";
//GalleryList.InfoGridVisible = "False";
Console.WriteLine("parameter: " + taps + " " + GalleryList.InfoGridVisible);
OnPropertyChanged();
}
public Profile Profile
{
get { return _profile; }
set
{
_profile = value;
OnPropertyChanged();
}
}
public GalleryList GalleryList
{
get { return _galleryList; }
set
{
_galleryList = value;
OnPropertyChanged();
}
}
}
Apologies for the code dump, any help would be appreciated as i have been stuck on this for a few days.
If there is a better method of implementing this, i would love to hear it.
What I want you try is something like this:
public class GalleryList : BindableObject {
private string _InfoGridVisible { get; set; }
private string _InfoGridEnabled { get; set; }
public List<GalleryImage> Gallery { get; set; }
public string InfoGridVisible
{
get { return _InfoGridVisible; }
set
{
_InfoGridVisible = value;
OnPropertyChanged();
}
}
public string InfoGridEnabled
{
get { return _InfoGridEnabled; }
set
{
_InfoGridEnabled = value;
OnPropertyChanged();
}
}
}
public class GalleryImage : BindableObject {
private string _Title { get; set; }
private string _Image { get; set; }
private string _Description { get; set; }
public string Title
{
get { return _Title; }
set
{
_Title = value;
OnPropertyChanged();
}
}
public string Image
{
get { return _Image; }
set
{
_Image = value;
OnPropertyChanged();
}
}
public string Description
{
get { return _Description; }
set
{
_Description = value;
OnPropertyChanged();
}
}
}
InfoGridVisible is not a property and hence binding when performs a lookup will never find it, the way to solve this is :
public class GalleryList
{
public List<GalleryImage> Gallery { get; set; }
public string InfoGridVisible { get; set; }
public string InfoGridEnabled { get; set; }
}
In the case of how to be able to get the Object within a Command you need to tell your TapGesture how to access to that command through this 2 options:
<TapGestureRecognizer Command ="{Binding Path=BindingContext.CommandToCall, Source={x:Reference Name=ParentPage}}" CommandParameter="{Binding .}"/>
Or:
<TapGestureRecognizer Command ="{Binding Source={RelativeSource AncestorType={x:Type vm:vmWhereCommandIs}}, Path=CommandToCall}" CommandParameter="{Binding .}"/>
Just in case you need more info here there are some resources:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/relative-bindings
https://www.xamarinexpert.it/how-to-correctly-use-databinding-with-listview-and-collectionview/
i have a collection
<CollectionView SelectionMode="Single"
ItemsSource="{Binding Cities}"
ItemsLayout="HorizontalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5">
<Image Source="{Binding Image}" />
<Label TextColor="Black" text="Text"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
How can i painting with diferents colors the image and label when i select a item?
According to your description, you have Image and Label in CollectionView DataTemplate, you want to set Image and Label different background color when you select one item, am I right?
If yes, I suggest you can use binding Background color to do this, please take a look my code:
<ContentPage.Resources>
<Style TargetType="StackLayout">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<CollectionView
ItemsSource="{Binding images}"
SelectionChanged="CollectionView_SelectionChanged"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5">
<Frame BackgroundColor="{Binding imagecolor}" CornerRadius="5">
<Image Source="{Binding Image}" />
</Frame>
<Label
BackgroundColor="{Binding labelcolor}"
Text="{Binding cityname}"
TextColor="Black" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
public partial class Page3 : ContentPage
{
public ObservableCollection<imagemodel> images { get; set; }
public Page3()
{
InitializeComponent();
images = new ObservableCollection<imagemodel>()
{
new imagemodel(){Image="a5.jpg",cityname="beijing",imagecolor=Color.White,labelcolor=Color.White},
new imagemodel(){Image="a6.jpg",cityname="shanghai",imagecolor=Color.White,labelcolor=Color.White},
new imagemodel(){Image="a7.jpg",cityname="shenzhen",imagecolor=Color.White,labelcolor=Color.White},
new imagemodel(){Image="a8.jpg",cityname="xiamen",imagecolor=Color.White,labelcolor=Color.White}
};
this.BindingContext = this;
}
private void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
imagemodel previous = (e.PreviousSelection.FirstOrDefault() as imagemodel);
imagemodel current = (e.CurrentSelection.FirstOrDefault() as imagemodel);
//Set the current to the color you want
current.imagecolor = Color.Pink;
current.labelcolor = Color.Green;
if (previous != null)
{
//Reset the previous to defaulr color
previous.imagecolor = Color.White;
previous.labelcolor = Color.White;
}
}
}
public class imagemodel:ViewModelBase
{
public string Image { get; set; }
public string cityname { get; set; }
private Color _imagecolor;
public Color imagecolor
{
get { return _imagecolor; }
set
{
_imagecolor = value;
RaisePropertyChanged("imagecolor");
}
}
private Color _labelcolor;
public Color labelcolor
{
get { return _labelcolor; }
set
{
_labelcolor = value;
RaisePropertyChanged("labelcolor");
}
}
}
The ViewModelbase implement INotifyPropertyChanged interface:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Update:
If you want to use mvvm mode to do this, and put CollectionView_SelectionChanged method in my viewModel, I suggest you can bind SelectionChangedCommand to do this, please take a look my code:
The ContentPage:
<ContentPage.Resources>
<Style TargetType="StackLayout">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<CollectionView
ItemsSource="{Binding images}"
SelectedItem="{Binding CurrentSelection}"
SelectionChangedCommand="{Binding selectioncommand}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5">
<Frame BackgroundColor="{Binding imagecolor}" CornerRadius="5">
<Image Source="{Binding Image}" />
</Frame>
<Label
BackgroundColor="{Binding labelcolor}"
Text="{Binding cityname}"
TextColor="Black" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
public partial class Page3 : ContentPage
{
public Page3()
{
InitializeComponent();
this.BindingContext = new imageviewmodel();
}
}
The imageviewmodel.cs:
public class imageviewmodel:ViewModelBase
{
public ObservableCollection<imagemodel> images { get; set; }
private imagemodel _PreviousSelection;
public imagemodel PreviousSelection
{
get { return _PreviousSelection; }
set
{
_PreviousSelection = value;
RaisePropertyChanged("PreviousSelection");
}
}
private imagemodel _CurrentSelection;
public imagemodel CurrentSelection
{
get { return _CurrentSelection; }
set
{
if(CurrentSelection!=value)
{
PreviousSelection = CurrentSelection;
_CurrentSelection = value;
RaisePropertyChanged("CurrentSelection");
}
}
}
public Command selectioncommand { get; set; }
public imageviewmodel()
{
images = new ObservableCollection<imagemodel>()
{
new imagemodel(){Image="a5.jpg",cityname="beijing",imagecolor=Color.White,labelcolor=Color.White},
new imagemodel(){Image="a6.jpg",cityname="shanghai",imagecolor=Color.White,labelcolor=Color.White},
new imagemodel(){Image="a7.jpg",cityname="shenzhen",imagecolor=Color.White,labelcolor=Color.White},
new imagemodel(){Image="a8.jpg",cityname="xiamen",imagecolor=Color.White,labelcolor=Color.White}
};
selectioncommand = new Command(changecolor);
}
private void changecolor()
{
foreach(imagemodel model in images)
{
if(model.cityname==CurrentSelection.cityname)
{
model.imagecolor = Color.Pink;
model.labelcolor = Color.Green;
}
else if(PreviousSelection != null && model.cityname==PreviousSelection.cityname)
{
model.imagecolor = Color.White;
model.labelcolor = Color.White;
}
}
}
}
The screenshot is same.
The screenshot:
I've Xamarin Forms app and I have Listview which looks like this :
<ListView x:Name="CalendarList" VerticalOptions="FillAndExpand" VerticalScrollBarVisibility="Never" RowHeight="100"
Grid.Row="0" SeparatorVisibility="None" ItemTapped="CalendarList_OnItemSelected" BackgroundColor="Transparent" >
<ListView.ItemTemplate>
<DataTemplate>
<local:MyCell>
<pcv:PancakeView **x:Name="YearsContainer"** Margin = "0,10,0,10" Style="{StaticResource cell_years}" IsClippedToBounds="true" >
<StackLayout HorizontalOptions = "StartAndExpand" Orientation="Horizontal">
<Label Style = "{DynamicResource bold_label}" Text="{Binding Year}" VerticalOptions="Center" />
</StackLayout>
</pcv:PancakeView>
</local:MyCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
private async void CalendarList_OnItemSelected(object sender, EventArgs e)
{
await Task.Delay(350);
var selectedItem = ((ListView)sender).SelectedItem;
var item = ((YearsList)selectedItem);
((ListView)sender).SelectedItem = null;
vm.ViewDetailCommand.Execute(item); // goto details page
}
public class MyCell: ViewCell
{
protected async override void OnTapped()
{
base.OnTapped();
await Task.Run(async () => await AnimationHelper.AnimateClick(this.View));
}
}
//IOS Renderer
[assembly: ExportRenderer(typeof(MyCell), typeof(MyCellRenderer))]
namespace CountDown.iOS.Renderers
{
public class MyCellRenderer : ViewCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var cell = base.GetCell(item, reusableCell, tv);
if (cell != null)
{
cell.SelectionStyle = UITableViewCellSelectionStyle.None;
}
return cell;
}
}
}
Now the question is :
Is it possible to change the style for PCV: PancakeView to different style names once a cell is selected?
I'm using Xamarin Forms ver 4.3.0 btw.
I've been able to change the color of the whole cell but I am not sure how to change the style.
According to your description, I guess that you want to change PancakeView style in ListView ItemTapped event?
If yes, I do one sample that you can take a look. I use Label control instead of PancakeView, it is the same.
<ContentPage.Resources>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="TextColor" Value="Color.Black" />
<Setter Property="FontAttributes" Value="None" />
</Style>
<Style x:Key="LabelChangedStyle" TargetType="Label">
<Setter Property="TextColor" Value="Color.Red" />
<Setter Property="FontAttributes" Value="Bold" />
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView ItemTapped="ListView_ItemTapped" ItemsSource="{Binding model3s}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Style="{Binding LabelStyle}" Text="{Binding str}">
<Label.Triggers>
<DataTrigger
Binding="{Binding istap}"
TargetType="Label"
Value="true">
<Setter Property="Style" Value="{StaticResource LabelChangedStyle}" />
</DataTrigger>
</Label.Triggers>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public partial class Page12 : ContentPage
{
public ObservableCollection<model3> model3s { get; set; }
public model3 model;
public Page12 ()
{
InitializeComponent ();
model3s = new ObservableCollection<model3>()
{
new model3(){str="this is test!",istap=false },
new model3(){str="this is test!",istap=false},
new model3(){str="this is test!",istap=false},
new model3(){str="this is test!",istap=false},
new model3(){str="this is test!",istap=false}
};
this.BindingContext = this;
}
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
if(model!=null)
{
model.istap = false;
}
model3 m = e.Item as model3;
m.istap = true;
model = m;
}
}
public class model3:ViewModelBase
{
public string str { get; set; }
private bool _istap;
public bool istap
{
get { return _istap; }
set
{
_istap = value;
RaisePropertyChanged("istap");
}
}
}
The ViewModelBase is the class that implement INotifyPropertyChanged
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
When you tap one item in ListView, the style will change.
I try to bind an pivot to the DataContext in an Windows Universal app.
Everything works fine except it seems that I am unable to mix binding and "static" PivotItems.
I need to create 0 to n PivotItems based on a list and on static PivotItem containing settings.
This is what I tried. If I remove the HeaderTemplate and ItemTemplate Element the PivotItem-Element is shown. If I let the Template Elements on there place the bound data is shown but not the extra PivotItem.
Is it even possible to mix?
<Pivot Name="PivotMain" Title="Title" ItemsSource="{Binding Path=Parts}">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="viewmodel:DetailModel">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate x:DataType="viewmodel:DetailModel">
<TextBlock Text="TestTest"/>
</DataTemplate>
</Pivot.ItemTemplate>
<PivotItem Name="Settings" Header="Settings">
<ScrollViewer VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
<ListView ItemsSource="{Binding Path=Settings}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodel:SettingModel">
<RelativePanel>
<ToggleSwitch Name="OnOff"
OffContent="{Binding OffContent}" OnContent="{Binding OnContent}" IsOn="{Binding IsMonitored, Mode=TwoWay}"
RelativePanel.AlignLeftWithPanel="True" />
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</PivotItem>
</Pivot>
Is it even possible to mix?
As far as I known, it is impossible. But you can use other ways to meet your requirements. What you actually want to do is the first one PivotItem has different content with others. You should be able use a DataTemplateSelector that you can select a different DataTemplate for the first item (which header is settings) with others. For example, code as follows:
XAML
<Page.Resources>
<DataTemplate x:Key="itemstemplate" x:DataType="local:DetailModel">
<TextBlock Text="TestTest"/>
</DataTemplate>
<DataTemplate x:Key="settingtemplate" x:DataType="local:DetailModel">
<ScrollViewer VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
<ListView ItemsSource="{Binding Path=Settingss}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:SettingModel">
<RelativePanel>
<ToggleSwitch Name="OnOff" OffContent="{Binding OffContent}" OnContent="{Binding OnContent}" IsOn="{Binding IsMonitored, Mode=TwoWay}" RelativePanel.AlignLeftWithPanel="True" />
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</DataTemplate>
<local:PivotTemplateSelector
itemstemplate="{StaticResource itemstemplate}"
settingtemplate="{StaticResource settingtemplate}"
x:Key="PivotTemplateSelector" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot Name="PivotMain" Title="Title" ItemsSource="{Binding Path=Parts}" ItemTemplateSelector="{StaticResource PivotTemplateSelector}">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="local:DetailModel">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
</Grid>
Code behind
public sealed partial class MainPage : Page
{
ObservableCollection<DetailModel> Parts;
ObservableCollection<SettingModel> Settingss;
public MainPage()
{
this.InitializeComponent();
Settingss = new ObservableCollection<SettingModel>()
{
new SettingModel()
{
IsMonitored=true,
OffContent="work at",
OnContent="content"
}
};
Parts = new ObservableCollection<DetailModel>()
{
new DetailModel()
{
Name="Settings",
Settingss=Settingss
},
new DetailModel()
{
Name="test1"
},
new DetailModel()
{
Name="test2"
}
};
datasources datasource = new datasources()
{
Parts = Parts
};
this.DataContext = datasource;
}
}
public class PivotTemplateSelector : DataTemplateSelector
{
public DataTemplate itemstemplate { get; set; }
public DataTemplate settingtemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
DetailModel itemget = item as DetailModel;
if (itemget.Name == "Settings")
{
return settingtemplate;
}
else
return itemstemplate;
return base.SelectTemplateCore(item);
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return SelectTemplateCore(item);
}
}
public class datasources
{
public ObservableCollection<DetailModel> Parts { get; set; }
}
public class DetailModel
{
public string Name { get; set; }
public ObservableCollection<SettingModel> Settingss { get; set; }
}
public class SettingModel
{
public string OffContent { get; set; }
public string OnContent { get; set; }
public bool IsMonitored { get; set; }
}