Databind my Accordion control in Silverlight 4 - data-binding

I have been searching all over the web trying to find an example of databinding an Accordion control.
I have written a simple test app to try and databind, and I can get the headers to bind, but can't seem to figure out how to get the content to bind. Could someone help me out?
Here is my XAML:
<tk:Accordion HorizontalAlignment="Left" Margin="12,12,0,0" Name="accordion1" Width="181" Height="325" Background="White" VerticalAlignment="Top">
<tk:Accordion.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding MenuHeaderName}" />
</StackPanel>
</DataTemplate>
</tk:Accordion.ItemTemplate>
<tk:Accordion.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=MenuItems.MenuItemName}" />
</StackPanel>
</DataTemplate>
</tk:Accordion.ContentTemplate>
</tk:Accordion>
And here is my code behind:
public partial class MainPage : UserControl
{
public class MenuItem
{
public MenuItem(string name) { MenuItemName = name; }
public string MenuItemName { get; set; }
}
public class MenuHeader
{
public MenuHeader(string name)
{
MenuItems = new List<MenuItem>();
MenuHeaderName = name;
}
public string MenuHeaderName { get; set; }
public List<MenuItem> MenuItems { get; set; }
}
public MainPage()
{
InitializeComponent();
List<MenuHeader> menuHeaders = new List<MenuHeader>();
MenuHeader robots = new MenuHeader("Robots");
robots.MenuItems.Add(new MenuItem("Robots - Item 1"));
robots.MenuItems.Add(new MenuItem("Robots - Item 2"));
robots.MenuItems.Add(new MenuItem("Robots - Item 3"));
menuHeaders.Add(robots);
MenuHeader pirates = new MenuHeader("Pirates");
pirates.MenuItems.Add(new MenuItem("Pirates - Item 1"));
pirates.MenuItems.Add(new MenuItem("Pirates - Item 2"));
pirates.MenuItems.Add(new MenuItem("Pirates - Item 3"));
menuHeaders.Add(pirates);
accordion1.ItemsSource = menuHeaders;
}
}

Figured it out.
Here is the XAML that works...
<tk:Accordion HorizontalAlignment="Left" Margin="12,12,0,0" Name="accordion1" Width="181" Height="325" Background="White" VerticalAlignment="Top">
<tk:Accordion.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding MenuHeaderName}" />
</StackPanel>
</DataTemplate>
</tk:Accordion.ItemTemplate>
<tk:Accordion.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding MenuItems}" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MenuItemName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</tk:Accordion.ContentTemplate>
</tk:Accordion>

Related

xamarin.forms second layer of bindable layout does not display binding

I am trying to create a tree similar to this :
I was able to create the first two layers, the parent being a scrollview (displaying the 7 items) the child being a bindable layout, displaying the sublayouts.
But the second sublayer is not binded to. The page just stays blank
<StackLayout BindableLayout.ItemsSource="{Binding dataPoints}" IsVisible="{Binding dataPointsVisible}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Margin="20,0,0,0">
<StackLayout>
<Label Text="{Binding identifier}"/>
<Label Text="{Binding type}"/>
<StackLayout>
<Label FontAttributes="Bold" Text="see dataPointSettings ->"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</StackLayout.GestureRecognizers>
</StackLayout>
<StackLayout BackgroundColor="Yellow"
HeightRequest="200"
BindableLayout.ItemsSource="{Binding dataPointSettings}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout BackgroundColor="Green">
<Label Text="{Binding alertingEmail}"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
<BoxView BackgroundColor="Black" HeightRequest="1"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
The "alertingEmail" is not displayed unfortunately.
How can I bind my views inside the second layer of bindable layouts?
You could refer to the code below:
Xaml: The same Xaml as yours.
Model:
public class DataPoints
{
public int identifier { get; set; }
public string type { get; set; }
public bool dataPointsVisible { get; set; }
public List<DataPointSettings> dataPointSettings { get; set; }
}
public class DataPointSettings
{
public string alertingEmail { get; set; }
}
ViewModel:
public class Page9ViewMode
{
public ObservableCollection<DataPoints> dataPoints { get; set; }
public Page9ViewMode()
{
dataPoints = new ObservableCollection<DataPoints>()
{
new DataPoints(){ dataPointsVisible=true, identifier=1, type="type1", dataPointSettings=new List<DataPointSettings>(){ new DataPointSettings() { alertingEmail="Email1-1"}, new DataPointSettings(){ alertingEmail="Email1-2" } } },
new DataPoints(){ dataPointsVisible=true, identifier=2, type="type2", dataPointSettings=new List<DataPointSettings>(){ new DataPointSettings() { alertingEmail="Email2-1"}, new DataPointSettings(){ alertingEmail="Email2-2" } } },
new DataPoints(){ dataPointsVisible=true, identifier=3, type="type3", dataPointSettings=new List<DataPointSettings>(){ new DataPointSettings() { alertingEmail="Email3-1"}, new DataPointSettings(){ alertingEmail="Email3-2" } } },
};
}
}
Screenshot:

How one ViewModel contains multiple ViewModel xamatin

I have 2 ViewModels (MVVM). I let show 2 as shown, it only shows data 1 ViewModel (the one below).
I put 1 and it shows up as normal.
This is how I display the data
<RefreshView x:DataType="locals:SliderViewModel"
Command="{Binding LoadSliderCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}">
<StackLayout Padding="8,0,8,4"
BindableLayout.ItemsSource="{Binding SliderShowInfos}"
Orientation="Horizontal"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout x:DataType="model:SliderShowInfo">
<Frame Padding="4"
HasShadow="False"
IsClippedToBounds="True"
BackgroundColor="Transparent">
<StackLayout Orientation="Horizontal">
<Frame Padding="0"
HasShadow="False"
CornerRadius="7"
IsClippedToBounds="True">
<Image Source="{Binding ImagesSlider}">
</Image>
</Frame>
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</RefreshView>
<RefreshView x:DataType="locals:ProductViewModel"
Command="{Binding LoadProductCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}">
<StackLayout Padding="8"
Orientation="Horizontal"
BindableLayout.ItemsSource="{Binding ProductInfos}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Frame Padding="5,0"
HasShadow="False"
IsClippedToBounds="True"
BackgroundColor="#fff">
<StackLayout x:DataType="model:ProductInfo">
</StackLayout>
</Frame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</RefreshView>
This is how I display the data. I'm trying to display product listing and photo listing data. Please give me a solution that can combine 2 ViewModel
Update
SliderViewModel.cs
public class SliderViewModel:BaseSliderViewModel
{
ISliderShowRepository sliderShowRepository = new SliderShowService();
public Command LoadSliderCommand { get; }
public ObservableCollection<SliderShowInfo> SliderShowInfos { get; }
public SliderViewModel()
{
LoadSliderCommand = new Command(async () => await ExecuteLoadSliderCommand());
SliderShowInfos = new ObservableCollection<SliderShowInfo>();
}
public void OnAppearing()
{
IsBusy = true;
}
async Task ExecuteLoadSliderCommand()
{
}
}
ProductViewModel.cs
public class ProductViewModel : BaseProductViewModel
{
IProductRepository productRepository = new ProductService();
public Command LoadProductCommand { get; }
public ObservableCollection<ProductInfo> ProductInfos { get; }
public Command ProductTappedView { get; }
public ProductViewModel(INavigation _navigation)
{
LoadProductCommand = new Command(async () => await ExecuteLoadProductCommand());
ProductInfos = new ObservableCollection<ProductInfo>();
ProductTappedView = new Command<ProductInfo>(OnViewDetailProduct);
Navigation = _navigation;
}
private async void OnViewDetailProduct(ProductInfo prod)
{
await Navigation.PushAsync(new DetailProduct(prod));
}
public void OnAppearing()
{
IsBusy = true;
}
async Task ExecuteLoadProductCommand()
{
IsBusy = true;
try
{
ProductInfos.Clear();
var prodList = await productRepository.GetProductsAsync();
foreach (var prod in prodList)
{
ProductInfos.Add(prod);
}
}
catch(Exception)
{
throw;
}
finally
{
IsBusy = false;
}
}
}
DashboardViewModel.cs
public class DashboardViewModel
{
public SliderViewModel SliderShowVM { get; set; }
public ProductViewModel ProductVM { get; set; }
}
Dashboard.xaml.cs
public Dashboard()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, true);
BindingContext = new DashboardViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
You cant just have 2 ViewModels for a single page. Instead, you can create another ViewModel, which will contains those 2 ViewModels that you need.
public class DashboardViewModel
{
public SliderShowViewModel SliderShowVM { get; set; }
public ProductViewModel ProductVM { get; set; }
}
And in dashboard constructor, assign the BindingContext
BindingContext = new DashboardViewModel();
And in your view, bind data like this:
...
<Image Source="{Binding SliderShowVM.ImagesSlider}">
...
BindableLayout.ItemsSource="{Binding ProductVM.ProductInfos}"
...
Also, if you don't need all data from SliderShowViewModel or ProductViewModel on your Dashboard, then you can define the properties that you really need for the dashboard inside DashboardViewModel, and to inject SliderShowViewModel and ProductViewModel instances via constructor (Might not be the best solution, but this way you will keep you view cleaner)
public class DashboardViewModel
{
public string RelevantProperty1 { get; set; }
public int RelevantProperty2 { get; set; }
...
public DashboardViewModel(SliderShowViewModel sliderVm, ProductViewModel productVm)
{
RelevantProperty1 = sliderVm.Something;
RelevantProperty2 = productVm.SomethingElse;
...
}
}
More explicit
DashboardViewModel class: It is a class with two properties of type SliderShowViewModel and ProductViewModel. These properties will store some instances (aka objects) of SliderShowViewModel and ProductViewModel respectively.
Create a DashboardViewModel instance and pass it as BindingContext for your Dashboard View:
public Dashboard()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, true);
var viewModel = new DashboardViewModel();
// Now we have the viewModel, but there is a problem.
// Remember those two viewModels (SliderShowViewModel and ProductViewModel)?
// Well, those properties are null, we have to provide values for them
// before setting the binding context.
viewModel.SliderViewModel = new SliderViewModel();
viewModel.ProductViewModel = new ProductViewModel();
BindingContext = viewModel;
}
Bind values from DashboardViewModel on our View:
<RefreshView Command="{Binding SliderViewModel.LoadSliderCommand}"
IsRefreshing="{Binding SliderViewModel.IsBusy, Mode=OneWay}">
<StackLayout Padding="8,0,8,4"
BindableLayout.ItemsSource="{Binding SliderViewModel.SliderShowInfos}"
Orientation="Horizontal"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout>
<Frame Padding="4"
HasShadow="False"
IsClippedToBounds="True"
BackgroundColor="Transparent">
<StackLayout Orientation="Horizontal">
<Frame Padding="0"
HasShadow="False"
CornerRadius="7"
IsClippedToBounds="True">
<Image Source="{Binding SliderViewModel.ImagesSlider}">
</Image>
</Frame>
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</RefreshView>
<RefreshView Command="{Binding ProductViewModel.LoadProductCommand}"
IsRefreshing="{Binding ProductViewModel.IsBusy, Mode=OneWay}">
<StackLayout Padding="8"
Orientation="Horizontal"
BindableLayout.ItemsSource="{Binding ProductViewModel.ProductInfos}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Frame Padding="5,0"
HasShadow="False"
IsClippedToBounds="True"
BackgroundColor="#fff">
</Frame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</RefreshView>
Give it a try and let me know if it works.

Multilevel Listview in xamarin forms

I need to make a Multilevel Listview in Xamarin forms.
Currently, I am using this for my Single level Grouping in my Menu Page
http://www.compliancestudio.io/blog/xamarin-forms-expandable-listview
I need to update something like the attached image. I have tried some of the Solution but all in vain.
Anyone has any idea about this.
Thanks
You could do this with Expander of Xamarin Community Toolkit.
Install Xamarin Community Toolkit from NuGet: https://www.nuget.org/packages/Xamarin.CommunityToolkit/
Usage: xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
Xaml:
<ContentPage.BindingContext>
<viewmodels:RootViewModel />
</ContentPage.BindingContext>
<ContentPage.Content>
<ScrollView Margin="20">
<StackLayout BindableLayout.ItemsSource="{Binding roots}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<xct:Expander>
<xct:Expander.Header>
<Label Text="{Binding Root}"
FontAttributes="Bold"
FontSize="Large" />
</xct:Expander.Header>
<StackLayout BindableLayout.ItemsSource="{Binding Node}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<xct:Expander Padding="10">
<xct:Expander.Header>
<Label Text="{Binding Key.Node}" FontSize="Medium" />
</xct:Expander.Header>
<StackLayout BindableLayout.ItemsSource="{Binding Value}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label Text="{Binding SubRoot}" FontSize="Small" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</xct:Expander>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</xct:Expander>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</ContentPage.Content>
Model:
public class Roots
{
public string Root { get; set; }
public Dictionary<Nodes, List<SubRoots>> Node { get; set; }
}
public class Nodes
{
public string Node { get; set; }
}
public class SubRoots
{
public string SubRoot { get; set; }
}
ViewModel:
public class RootViewModel
{
public List<Roots> roots { get; private set; }
public RootViewModel()
{
CreateMonkeyCollection();
}
public void CreateMonkeyCollection()
{
List<SubRoots> subRoot = new List<SubRoots>() { new SubRoots() { SubRoot = "SubNode" } };
Dictionary<Nodes, List<SubRoots>> node = new Dictionary<Nodes, List<SubRoots>>();
node.Add(new Nodes() { Node="Nodo1"}, null);
node.Add(new Nodes() { Node="Nodo2"}, null);
node.Add(new Nodes() { Node="Nodo3"}, null);
node.Add(new Nodes() { Node="Nodo4"}, subRoot);
roots = new List<Roots>()
{
new Roots(){ Root="Root1", Node=node},
new Roots(){ Root="Root2", Node= null}
};
}
}

Xamarin.Forms Why is my tap command not working?

I have this kind of tap command in my carousel view:
<Image
x:Name="img_adPic"
HeightRequest="150"
Aspect="AspectFill"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Source="{Binding thumbnail}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference page}}" CommandParameter="{Binding Path=BindingContext.CurrentIndex, Source={x:Reference page}}"/>
</Image.GestureRecognizers>
</Image>
I gave my contentpage a name :
<ContentPage x:Name="page" >
Now in my class:
public ICommand CarouselItemTapped{ get; set; }
public Page_MainMenuDetail()
{
InitializeComponent();
CarouselItemTapped = new Xamarin.Forms.Command((selectItem) =>
{
});
instance = this;
}
But no matter what I try, the command is NEVER fired. I think i did everything as in the tutorials, why is this not working?
Thank you
You could refer to the code below about how to use the TapCommand. The command would be triggled when you tap on the image.
Xaml:
<ContentPage.Content>
<AbsoluteLayout>
<CarouselView
x:Name="carouselView"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
IndicatorView="indicatorview">
<CarouselView.ItemTemplate>
<DataTemplate>
<Image
x:Name="img_adPic"
Aspect="AspectFill"
HeightRequest="150"
HorizontalOptions="FillAndExpand"
Source="{Binding .}"
VerticalOptions="FillAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference page}}" />
</Image.GestureRecognizers>
</Image>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView
x:Name="indicatorview"
AbsoluteLayout.LayoutBounds=" 0.5,0.95,100,100"
AbsoluteLayout.LayoutFlags=" PositionProportional"
IndicatorColor=" LightGray"
IndicatorSize=" 10"
SelectedIndicatorColor=" Black" />
</AbsoluteLayout>
</ContentPage.Content>
Code behind:
public partial class Page2 : ContentPage
{
public ICommand CarouselItemTapped { get; set; }
public ICommand TapCommand { get; set; }
public Page2()
{
InitializeComponent();
var list = new List<string>
{
"pink.jpg",
"pink.jpg",
"pink.jpg",
"pink.jpg",
"pink.jpg",
};
carouselView.ItemsSource = list;
//CarouselItemTapped = new Xamarin.Forms.Command((selectItem) =>
//{
//});
//instance = this;
TapCommand = new Command(commandEvent);
this.BindingContext = this;
}
public void commandEvent()
{
}
}

Adding items to list using data binding in windows phone 8

I am trying to add list to the items dynamically as a part of learning Data Binding. But the items are not getting added. The app is so simple to just add a string to the list.
The XAML Code is as follows
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<ListBox x:Name="TotalItems" Grid.Row="0" ItemsSource="{Binding Items_OC}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Width="440">
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Item : " VerticalAlignment="Top"/>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top" Margin="1,0,0,0" FontSize="{StaticResource PhoneFontSizeLarge}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBox x:Name="NewItem" Grid.Column="0" TextWrapping="Wrap" Width="359"/>
<Button x:Name="Add" Grid.Column="1" Content="Add"/>
</Grid>
</Grid>`
The XAML.CS code is as follows
public ObservableCollection<Items> items_OC;
public ObservableCollection<Items> Items_OC
{
get
{
return items_OC;
}
set
{
if (items_OC != value)
{
MessageBox.Show("Item to added to collection");
items_OC = value;
NotifyPropertyChanged("Items_OC");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Items : INotifyPropertyChanged, INotifyPropertyChanging
{
string name;
public string Name
{
get
{
return name;
}
set
{
if (name != value)
{
NotifyPropertyChanging("Name");
name = value;
NotifyPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangingEventHandler PropertyChanging;
private void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
}
private void Add_Click(object sender, RoutedEventArgs e)
{
Items it = new Items { Name = NewItem.Text };
Items_OC.Add(it);
}
Thanks for the help..
What your code seems missing is setting DataContext. Generally, binding resolves path from DataContext property. When you bind ItemsSource property this way :
<ListBox x:Name="TotalItems" Grid.Row="0" ItemsSource="{Binding Items_OC}" ...>
application will search Items_OC property of ListBox's DataContext. Currently, it won't find the property because DataContext is null. You can set DataContext to xaml.cs file from C# code like this :
this.DataContext = this;
or from XAML :
<phone:PhoneApplicationPage
.......
.......
DataContext="{Binding RelativeSource={RelativeSource Self}}"
/>
Item.cs:
public class Item
{
public string Name { get; set; }
}
MainPage.xaml:
<Grid x:Name="ContentPanel">
<ListBox x:Name="TotalItems"
ItemsSource="{Binding Items}"
VerticalAlignment="Top">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="Item :" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Vertical"
VerticalAlignment="Center"
Margin="0,10,0,0">
<TextBox x:Name="NewItemTextBox" />
<Button x:Name="AddNewItem"
Content="Add"
Click="AddNewItem_Click" />
</StackPanel>
</Grid>
MainPage.cs:
public ObservableCollection<Item> Items { get; set; }
public MainPage()
{
InitializeComponent();
Items = new ObservableCollection<Item> { new Item { Name = "Roman" } };
DataContext = this;
}
private void AddNewItem_Click(object sender, RoutedEventArgs e)
{
Items.Add(new Item { Name = NewItemTextBox.Text });
}
Make things simple, but not simpler!

Resources