I am having troubles with dynamically adding a PivotItem to a templated and databound Pivot.
The classes in use (a bit simplified to keep it quickly comprehensable);
class Menu {
string Name
List<Dish> Dishes_1;
List<Dish> Dishes_2;
List<Dish> Dishes_3;
}
class Dish {
string Description
string Price;
}
I want to use a Pivot to display a list of Menu-Objects. I create PivotItems dynamically based on the number of items in that list. Each PivotElement should thus follow the same layout and behave the same. The lay-out template and databinding is done in the .xaml as following;
<phone:Pivot x:Name="Mainpivot">
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</phone:Pivot.HeaderTemplate>
<phone:Pivot.ItemTemplate>
<DataTemplate>
<ListBox>
<TextBlock Text="Dishes_1"/>
<ListBox ItemsSource="{Binding Dishes_1}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Description}"/>
<TextBlock Text="{Binding Price}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
// ...
// this is repeated 3 times;
//a textblock and listbox per List<Dishes> in the Menu-class
</ListBox>
</DataTemplate>
</phone:Pivot.ItemTemplate>
</phone:Pivot>
I populate the Pivot in de .cs file with the following:
foreach (Menu m in List_Menus) {
PivotItem p = new PivotItem();
p.DataContext = m;
Mainpivot.Items.Add(p);
}
As I set the DataContext as a Menu-Object, the DataBinding (through xaml) should not require any more code here (so I think?).
The problem now being; it doesn't work...
Through looking with the debugger, It appears that the created PivotItem doesn't behave like the template defined in Mainpivot tells (or so I think). Looking at Mainpivot does show that PivotItems have been added, but that's it, I believe they're just empty all-null PivotItems. When executing in the emulator, It just shows an empty Pivot.
Any thoughts?
//PS: I don't use ViewModels, as I find them quite confusing (as a beginner) as concept. I don't think that has anything to do with the problem though?
A few things here. First, for your binding to work you'll need to use properties instead of fields.
public class Menu {
public string Name {get;set;}
public List<Dish> Dishes_1 { get; set; }
public List<Dish> Dishes_2 { get; set; }
public List<Dish> Dishes_3 { get; set; }
}
public class Dish {
public string Description { get; set; }
public string Price { get; set; }
}
Next, instead of your foreach loop to add the items to the pivot just set the items source:
Mainpivot.ItemsSource = List_Menus;
BTW - you really should look into learning MVVM. It is worth the time.
Related
I am trying to make funcionality, to make larger text across whole application for user when he clicks a 'increase font size' button. Using MVVM, I have done it like this:
Increase font size button click
increase value of double field 'fontSize' which is binded to almost every text in layout
Update UI with new value after button click
However I don't know how to achieve this in Collectionview where I have got Binding in .xaml file, with some particular List (item is model class). The collectionview DataTemplate contains labels where I want to increase font size. Is there a way to do this without adding 'fontSize' field in my model class. If not how to update UI with 'new' List with increased font sizes.
I appreciate any help, tips and discussions.
Thank you.
You can create bindableproperty(fontsize) in your viewmodel and use Relative Binding so the label in Collectionview can change it's fontsize,code like:
ViewMode:
public class ColViewModel:BindableObject
{
public ObservableCollection<Student> students { set; get; }
public static readonly BindableProperty FontSizeProperty =
BindableProperty.Create("fontsize", typeof(int), typeof(ColViewModel), null);
public int fontsize
{
get { return (int)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public ICommand IncreaseCommand { private set; get; }
public ColViewModel()
{students = new ObservableCollection<Student>();
getStudents();
fontsize = 24;
IncreaseCommand = new Command(() => {
fontsize++;
});
}
View:
<StackLayout>
<Label Text="This is a Title" FontSize="{Binding fontsize}"/>
<CollectionView ItemsSource="{Binding students}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" FontSize="{Binding Source={RelativeSource AncestorType={x:Type local:ColViewModel}}, Path=fontsize}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Click to increase fontsize" Command="{Binding IncreaseCommand}"/>
</StackLayout>
Edit:
xmlns:local="clr-namespace:MyForms2.ViewModels"
<TextBlock Name="currency" TextWrapping="Wrap" VerticalAlignment="Center"/>
<TextBlock Margin="5,0" Text="{Binding Text, ElementName=currency" TextWrapping="Wrap" VerticalAlignment="Center" FontSize="22" />
I am using the above code for binding property of one field to another in my WP7 application.
i want to do the similar task from back-end. any suggestions??
Bindings are working in a specified data context. You can set the data context of your layout root to the page instance, then you can bind to any of your properties. (DataContext is inherited through the child FrameworkElements.) If you want your binding to update its value whenever you change your property from code, you need to implement INotifyPropertyChanged interface or use Dependency properties.
<Grid x:Name="LayoutRoot">
<TextBox Text="{Binding Test, Mode=TwoWay}" />
</Grid>
public class MainPage : PhoneApplicationPage, INotifyPropertyChanged
{
private string test;
public string Test
{
get { return this.test; }
set
{
this.test = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Test"));
}
}
public MainPage()
{
InitializeComponents();
LayoutRoot.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
}
This is a stupid example since you can access your TextBox any time from MainPage, this has much more sense if you are displaying model objects with DataTemplates.
(I typed this on phone, hope it compiles..)
i got my solution as: var b = new Binding{ElementName = "currency", Path = new PropertyPath("Text")}; Textblock txt = new TextBlock(); txt.SetBinding(TextBlock.TextProperty, b);
My requirement as follows
1) Select the contact from the Contact ( select one or many)
2) Add the Mobile no and Contact name from selected contact in ListBox
3) To delete : Select a contact in the ListBox and delete dynamically and refresh data
My Problems:
a) How to I bind data from contact to ListBox?
PhoneCtc = new PhoneNumberChooserTask();
PhoneCtc.Completed += new EventHandler(PhoneCtc_Complete);
public void PhoneCtc_Complete(object sender, PhoneNumberResult e)
{
string No = e.PhoneNumber;
string Name = e.DisplayName;
// how to add this data dynamically to listbox and display ?
}
Thanks
Define the DataTemplate of the items in the listbox as shown below:
<ListBox Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<TextBlock Text="{Binding No}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then you'll have to create a class Contact containing No and Name:
public class Contact
{
public string No { get; set; }
public string Name { get; set; }
}
Now in code when you want to add an element, you just need to initialize an instance class Contact and set variables then write listBox1.Items.Add(Contact);
i have a Panorama control and ListBox controls inside the Panorama. is there any "event" that i can hook on to or any way to detect when all the data binding or UI display associated with the Panorama and/or ListBox controls are finished?
the reason i need to detect this event is because i want to show the ApplicationBar only after the Panorama and/or ListBox controls have completely binded and finished rendering.
for example, my XAML is defined as the following.
<controls:Panorama Name="panorama">
<controls:Panorama.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Details}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Field1}"/>
<TextBlock Text="{Binding Field2}"/>
...
<TextBlock Text="{Binding FieldN}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</controls:Panorama.ItemTemplate>
</controls:Panorama>
my plain-old CLR object (POCO) looks like the following.
public class MyPoco {
List<Detail> Details { get; set; }
}
public class Detail {
public string Field1 { get; set; }
public string Field1 { get; set; }
...
public string FieldN { get; set; }
}
in my c# code-behind, i bind the data as follows.
List<MyPoco> pocos = GetMyPocosFromSomewhere();
panorama.ItemsSource = myList;
ApplicationBar.IsVisible = true; //i only want to make this visible after the Panorama and ListBox controls have finished binding and rendering
right now, the code as i have sketched out above works, but the ApplicationBar is always visible before the Panorama/ListBox controls have rendered. to me, this makes the user experience awkward.
any help is appreciated.
Short answer would be "no, you can't detect it".
But a good solution is to add the command to the UI work queue aka. the Dispatcher. Like this:
Dispatcher.BeginInvoke(() => ApplicationBar.IsVisible = true);
That way, it'll first render it, when all the other UI tasks are done, and the experience shouldn't be so awkward.
A known "issue" with nesting ListBoxes in a Windows Phone 7 App is that for each parent category their respective child ListBox retains its own SelectedItems list. Well, I have a situation where this is expected behavior, but I'm having issues capturing both the Parent and Child ListBox selected lists.
Current Functionality:
1. List item
2. List item
3. Loading of Parent and Child ListBox data is working
4. Multi-select of Parent ListBox items works prefectly
5. Multi-select of Child ListBox items works, but is not accessible
6. Multi-select of Child ListBox items for a number of different parents works in the UI, but the selection is lost when scrolling large list sets and is not accessible
7. lstCategory is accessible directly, but lstSubCategory is not accessible directly (possibly, I just don't know how)
8. I'm bound to a ViewModel with one complex object that represents the two ListBoxes as two List objects.
Expected Functionality:
I would like to be able to select both Category (parent) and SubCategory (child) ListBox items as follows; (X) Denotes Selected:
Bread (X)
Loaf (X)
Croissant
Buscuit (X)
Donut
Fruit
Pinaple (X)
Strawberry
Drinks (X)
Water
Milk (X)
Juice (X)
Soda
Snacks (X)
Chips
Fries
Trail Mix
I would like to retain the selections even if this was a long list. So, what I want to capture and work with is:
Bread (X)
Loaf (X)
Buscuit (X)
Pinaple (X)
Drinks (X)
Milk (X)
Juice (X)
Snacks (X)
Since I have a CategoryID in each of the items' objects, I can strip the heirarchy information on capture.
For breivity, here is the essence of the code:
<ListBox
x:Name="lstCategory"
SelectionMode="Multiple"
ItemsSource="{Binding Categories}"
FontSize="32"
Margin="0,0,0,67">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding CategoryName}"
FontSize="36"
TextWrapping="Wrap"
Margin="20,0,0,0"
VerticalAlignment="Top"/>
<StackPanel Orientation="Vertical" Margin="60,0,0,0">
<ListBox
x:Name="lstSubCategory"
SelectionMode="Multiple"
ItemsSource="{Binding SubCategories}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SubCategoryName}"
FontSize="28"
TextWrapping="Wrap"
VerticalAlignment="Top"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the ViewModel:
public List<Category> Categories { get; set; }
public PostCategorySelectVM()
{
Categories = new List<Category>()
{
new Category()
{
CategoryID = 0,
CategoryName = "Bread",
SubCategories = new List<SubCategory>()
{
new SubCategory() {
CategoryID = 001,
SubCategoryName = "Loaf"
},
new SubCategory() {
CategoryID = 002,
SubCategoryName = "Croissant"
}
// ...
}
// ...
}
// ...
}
}
Category Class:
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public List<SubCategory> SubCategories { get; set; }
}
SubCategory Class:
public class SubCategory
{
public int CategoryID { get; set; }
public string SubCategoryName { get; set; }
}
Save Button Click Event:
private void btnSave_Click(object sender, RoutedEventArgs e)
{
foreach (Category item in lstCategory.SelectedItems)
{
catList.Add(item);
}
foreach (Category cat in catList)
{
scatList = cat.SubCategories;
foreach (SubCategory scat in scatList)
{
// How do I select the "Selected" SubCategories?
// How do I select the lstSubCategory control?
}
}
}
Final Notes:
The only lead I have has do do with dependancy properties, but the only examples I've seen require the FrameworkPresentation.dll which is not available on WP7.
The nested ListBox has the expected UI functionality (except for large lists removing cross-selections on scroll)
The user experience feels best when both Category and SubCategory are shown on the same screen.
Consider the UI functionality like a directory search engine. You may want to select the general category and/or the sub categories in different combinations, but the parent should not require a child and a child should not require a parent, yet both child and parent could exist (for specificity).
You could use checkboxes instead of textboxes in your data templates, and then bind the IsChecked property of the checkboxes to an IsSelected property in your Category/Subcategory classes:
<ListBox x:Name="lstCategory"
ItemsSource="{Binding Categories}"
FontSize="32"
Margin="0,0,0,67">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<CheckBox Content="{Binding CategoryName}"
FontSize="36"
IsChecked="{Binding IsSelected,Mode=TwoWay}"
Margin="20,0,0,0"
VerticalAlignment="Top"/>
<ListBox ItemsSource="{Binding SubCategories}" Margin="60,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding SubCategoryName}"
FontSize="28"
IsChecked="{Binding IsSelected,Mode=TwoWay}"
VerticalAlignment="Top"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You should also have your category/subcategory classes implement INotifyPropertyChanged to properly fire notifications when IsSelected is set.
assuming that, your save would look something like (this isnt' exact!)
private void btnSave_Click(object sender, RoutedEventArgs e)
{
catList.Clear();
catList.AddRange( lstCategory.Items.OfType<Category>().Where(x=>x.IsSelected));
scatList.Clear();
foreach (Category cat in catList)
{
scatList.AddRange(cat.SubCategories.Where(x=>x.IsSelected));
}
}