Data Binding for nested controls - c#

I want to do search inside some books, then result of each book appears in separate item of pivot control. each one is represented by a separate LongListSelector control inside that PivotItem. Now I want to know should I assing ItemsSource for LongListSelector or for its parent which is a Pivot?
There is a dictionary for all books:
private Dictionary<string, List<Model>> ItemResources = new Dictionary<string, List<Model>>();
and a List<Model> for each book, which will be saved as a value inside ItemResources above.
this is what I do:
foreach (var translation in ItemResources)
{
PivotItem pivotItem = new PivotItem
{
Header = translation.Key
};
LongListSelector lls = new LongListSelector
{
HideEmptyGroups = false,
IsGroupingEnabled = false
};
lls.ItemTemplate = Resources["template"] as DataTemplate;
lls.ItemsSource = translation.Value;
pivotItem.Content = lls;
ResultPivot.Items.Add(pivotItem);
}
and the template is a reusable DataTemplate which I redproduce it for each longlistselector inside each pivotItem of the ResultPivot:
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="template">
<StackPanel Margin="0,0,0,0" Orientation="Vertical">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Height="Auto" VerticalAlignment="Top" Width="455" Margin="3,20,3,0">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}" FontSize="{StaticResource PhoneFontSizeNormal}"/>
<TextBlock Text="{Binding Number}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}" FontSize="{StaticResource PhoneFontSizeNormal}"/>
</StackPanel>
<StackPanel Height="Auto" VerticalAlignment="Top" HorizontalAlignment="Left" Width="455" Margin="3,0,3,20">
<TextBlock Text="{Binding Text}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}" FontSize="{StaticResource PhoneFontSizeNormal}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
The problem is that nothing appears on the screen after running. I saw in debugging, that the values are there, but it seems something is wrong with data binding. How can I solve it? thanks
(this is a Windows Phone 8 App, but because the concept is the same for WPF and its wide community I added it too)

The UI has currently no way of knowing when the source changes. You need to either use an ObservableCollection or implement the INotifyPropertyChanged interface to properly notify the UI when the source changes.

Related

Hub with dynamic HubSections with GridViews and DataBinding

I would like to create a Hub with several HubSections via code. Each HubSection owns a single GridView so it look like every HubSection is a table (fullscreen) and I swipe left/right to view every table.
In my XAML page is only Hub the other stuff should be done by code. The HubSections should be created at runtime. For this I use a local settings storage to save some information about this, like how many HubSections etc.
Creating new HubSections is no problem but I'm stuck at adding a GridView to each HubSection because I don't understand the logic here. It looks like I have to add a DataTemplate and a GridView but my attempts all failed.
Note: each GridView has also it's own databinding from a Observable Collection.
So how to add a (?DataTemplate?) GridView with databinding to a HubSection ?
With a DataTemplate you build your layout. I have used in a Project following template to show a few data per day and create one Section for each day:
<Page.Resources>
<CollectionViewSource x:Name="HubViewModel"/>
<DataTemplate x:Key="DataTemplate">
<Grid Background="Transparent" Width="300" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,20">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" HorizontalAlignment="Center">
<TextBlock Text="{Binding SumShipmentsSA}" Style="{ThemeResource HeaderTextBlockStyle}" TextAlignment="Center" TextWrapping="NoWrap"/>
</StackPanel>
<StackPanel Grid.Row="1" HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal">
<TextBlock x:Uid="SummaryHubNat" Text="National" FontSize="10" Width="100" VerticalAlignment="Center" Margin="0,0,20,0"/>
<TextBlock Text="{Binding CountShipmentsNationalSA}" Style="{ThemeResource BodyTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock x:Uid="SummaryHubInt" Text="International" FontSize="10" Width="100" VerticalAlignment="Center" Margin="0,0,20,0"/>
<TextBlock Text="{Binding CountShipmentsInternationalSA}" Style="{ThemeResource BodyTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock x:Uid="SummaryHubCharter" Text="Charter" FontSize="10" Width="100" VerticalAlignment="Center" Margin="0,0,20,0"/>
<TextBlock Text="{Binding CountShipmentsCharterSA}" Style="{ThemeResource BodyTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
.
.
<Hub x:Name="MainHub" DataContext="{Binding Source={StaticResource HubViewModel}}" Margin="0,0,0,20"/>
In the Code page I used the following method to create to the Section:
private void AddHubSection(IEnumerable<DaySummary> list)
{
if (list != null)
{
list = list.OrderByDescending(x => x.Date);
foreach (var item in list)
{
if (item.Date.Date.Equals(DateTime.Now.Date))
{
continue;
}
HubSection hubSection = new HubSection();
TextBlock headerTextBlock = new TextBlock();
headerTextBlock.Text = item.Date.ToString("dddd dd.MMM");
headerTextBlock.FontSize = 15;
hubSection.Header = headerTextBlock;
hubSection.Margin = new Thickness(0);
object dataTemplate;
this.Resources.TryGetValue("DataTemplate", out dataTemplate);
hubSection.ContentTemplate = dataTemplate as DataTemplate;
hubSection.DataContext = item;
hubSection.DoubleTapped += HubSection_DoubleTapped;
MainHub.Sections.Add(hubSection);
}
}
}
I think the example can help you have fun while trying.

Move data from itemsource of one listbox to another listbox wp8

I am parsing a RSS Feed from one site and storing it in listbox1 and then parsing another RSS storing it in listbox2. Now I want to combine the data of listbox1 and listbox2 in listbox3. It might sound silly but I am unable to do it. The main problem is I am not able to access the controls inside the listbox.
<ListBox x:Name="list1" ItemsSource="{Binding RSSData}" DataContext="{Binding RSSData}" Visibility="Collapsed">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock HorizontalAlignment="Center" x:Name="txtpubDate" Foreground="#FF170505" Text="{Binding Path=pubDate}" TextDecorations="Underline" ></TextBlock>
<TextBlock Padding="18" Foreground="#FF0E0101" x:Name="txtTitle" TextWrapping="Wrap" Text="{Binding Path=Title}" FontWeight="Bold"></TextBlock>
<Image Height="200" x:Name="imageLink" Source="{Binding strImg}"></Image>
<TextBlock Foreground="#FF0F0202" Padding="35" TextTrimming="WordEllipsis" HorizontalAlignment="Left" x:Name="txtDesc" Margin="2" TextWrapping="Wrap" Text="{Binding Path=Description,Converter={StaticResource RssTextTrimmer}}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is the XAML code of my listbox1 and I am trying to do it with listbox3.itemsource=listbox1.itemsource; but nothing is happening.
I also tried to add data in this way: listbox3.items.add(listbox.items[i])
but nothing is working.
Please help.
You can loop through the contents of the listbox and add them one by one for instance:
foreach(object item in listbox1.items)
{
listbox3.items.add(item);
}
and then do the same for listbox2.

Ungrouped Long List Selector

Okay, So I am trying to bind a list of objects with a DisplayName property to a long list selector.
XAML Code
<phone:LongListSelector x:Name="lls_TemplateFields" HorizontalAlignment="Left" Width="450" Grid.Row="2" Height="400" LayoutMode="List" Background="#FF9E9D9D" IsGroupingEnabled="False">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding DisplayName}" Foreground="Black" FontSize="24"/>
</Grid>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
C# Code
List<AttributeDefinition> m_SelectedAttributes = new List<AttributeDefinition>();
lls_TemplateFields.ItemsSource = m_SelectedAttributes;
My Class AttributeDefinition contains a property for DisplayName. If I group the the list using a group key then the list will show up, however I cannot get just a plain list of items to show up. Like a listbox in WPF C#.
I am using this list to represent a list of chosen AttributeDefinitions from another list that shows all the AttributeDefinitions grouped alphabetically by their DisplayName property, and the Display Value is Binded to the DisplayName Property like shown below...
XAML
<phone:LongListSelector x:Name="lls_AttributeList" HorizontalAlignment="Left" Height="450" VerticalAlignment="Top" Width="450" HideEmptyGroups="True" IsGroupingEnabled="True" SelectionChanged="SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Grid>
<Border BorderThickness="2">
<TextBlock Text="{Binding DisplayName}" Foreground="{StaticResource PhoneChromeBrush}" FontSize="24"/>
</Border>
</Grid>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
<phone:LongListSelector.GroupHeaderTemplate>
<DataTemplate>
<Grid Width="50" Height="50" HorizontalAlignment="Left" VerticalAlignment="Center" Background="Blue">
<Border BorderThickness="4">
<TextBlock Text="{Binding Key}" Foreground="White" FontSize="38" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</Grid>
</DataTemplate>
</phone:LongListSelector.GroupHeaderTemplate>
</phone:LongListSelector>
C#
List<AttributeKeyGroup<AttributeDefinition>> DataSource = AttributeKeyGroup<AttributeDefinition>.CreateGroups(AttributeData,
Thread.CurrentThread.CurrentUICulture,
(AttributeDefinition aDef) => { return aDef.Type; },
true);
lls_AttributeList.ItemsSource = DataSource;
This list works just fine. And the data in the background is functioning properly, because as I select items from the total list of attributedefinitions they are removed from the LongListSelector and added to the LongListSelector of the selected lists Itemssource
For further inquiry into more code surrounding this User Control please feel free to ask I will disclose as much code as I can without breaking my non-disclosure agreement with my work. Thank you for taking the time to read and possibly help. Much appreciated.
List<AttributeDefinition> m_SelectedAttributes = new List<AttributeDefinition>();
lls_TemplateFields.ItemsSource = m_SelectedAttributes;
because you just instantiated this list and binding it right away? How about adding some items into the list
There are two answers to this. I have tried both and both work.
[1] You have to set the LongListSelector.ScrollTo(object) to the first object of your list when you update the data if the list has 1 or more items. Such as...
lls_TemplateFields.ItemsSource = m_SelectedAttributes;
if(m_SelectedAttributes.Count > 0)
{
lls_TemplateFields.ScrollTo(m_SelectedAttributes[0]);
}
[2] The proper way to fix this is to use ObservableCollections which work better with DataBinding and the WP8 WPF SDK UI to update on adding objects to the ObservableCollection.
ObservableCollection<AttributeDefinition> m_SelectedAttributes =
new ObservableCollection<AttributeDefinition>();
lls_TemplateFields.ItemsSource = m_SelectedAttributes

Binding data in C# and XAML?

I'm trying to use binding with C# and a XAML SemanticZoom control in my Metro app, but honestly I'm at a loss as to how to do it. Here's the XAML code I've gotten so far by piecing together from questions, articles, etc:
<SemanticZoom x:Name="boardZoom" Height="626" Margin="10,132,10,0" VerticalAlignment="Top">
<SemanticZoom.ZoomedInView>
<GridView IsSwipeEnabled="True" x:Name="ItemsGridView">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0"
HorizontalAlignment="Left" Background="White">
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" Width="200" Height="300"
FontFamily="Global User Interface" FontSize="40" Foreground="Black"
VerticalAlignment="Center" HorizontalAlignment="Left"/>
<Image Source="{Binding Image}" Height="60" Width="60"
VerticalAlignment="Center" Margin="0,0,10,0" Visibility="{Binding isImage}" />
<TextBlock Text="{Binding Category}" TextWrapping="Wrap" Width="200"
FontFamily="Global User Interface" Foreground="Black"
VerticalAlignment="Center" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</SemanticZoom.ZoomedInView>
<!--Didn't include SemanticZoom.ZoomedOutView since I'm still trying to get the ZoomedIn one working first-->
</SemanticZoom>
And my C# code:
List<PinStore.pin> pins = PinStore.CopyFromStream(response.GetResponseStream()); //returns a list of PinStore.pin objects, which have name, type, Image, isImage, and Category objects
System.Collections.ObjectModel.ObservableCollection<SemanticZoomed.zoomedIn> toSource = new System.Collections.ObjectModel.ObservableCollection<SemanticZoomed.zoomedIn>(); //should I be using ObservableCollection or something like List<> here?
foreach (PinStore.pin pin in pins)
{
SemanticZoomed.ZoomedIn toAdd = new SemanticZoomed.ZoomedIn(); //class with Title, Image, isImage, and Category objects
if (pin.type == "text")
{
toAdd.Title = pin.name;
toAdd.Image = null;
toAdd.isImage = Visibility.Collapsed;
toAdd.Category = pin.category;
}
toSource.Add(toAdd);
}
ItemsGridView.DataContext = toSource;
I've not had much experience in XAML/C#, and zero experience with binding. I'm not getting any errors, but I've noticed that if I replace ItemsGridView.DataContext = toSource; with ItemsGridView.ItemsSource = toSource; a blank stackpanel shows up in the GridView, and I can't seem to find a way to fill that with the Title and Category values I specify. Thanks!
Well you should first consider creating a ResourceDictionary so you can save your item style. Then you can set the ItemStyle to the Resource Dictionary.
But regardless you need to do: ItemsGridView.ItemsSource = toSource; If you do not choose to Bind the toSource in the GridView xaml.
Also make sure the SemanticZoomed.zoomedIn object implements the INotifyPropertyChanged interface, And calls the event properly. And make sure the Title, Image, Category, etc are public properties that call the event when edited. and Also you need make sure pin.Text is an actual value. {Making sure}.
If you want to learn more about data binding check out how they do it in C# & XAML with Windows 8 {Should be the same thing}:
http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464965.aspx

ListBox ItemsSource Binding doesn't work

I'm developing a Windows Phome application. I have the following ListBox on a page:
<ListBox Margin="10,10,8,8" x:Name="WallList">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="ListBoxItemLayout" Background="Transparent" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.33*"/>
<ColumnDefinition Width="0.77*"/>
</Grid.ColumnDefinitions>
<Image HorizontalAlignment="Left" Margin="0" Source="{Binding ImagePath}" Height="200"/>
<StackPanel Margin="5,0,0,0" Grid.Column="1">
<TextBlock x:Name="Name" TextWrapping="Wrap" Text="{Binding Name}" Style="{StaticResource PhoneTextTitle2Style}"/>
<TextBlock x:Name="Comment" Margin="0,5,0,0" TextWrapping="Wrap" Text="{Binding Comment}" Style="{StaticResource PhoneTextNormalStyle}" Height="130"/>
<TextBlock x:Name="When" TextWrapping="Wrap" Text="{Binding When}" Style="{StaticResource PhoneTextTitle3Style}" VerticalAlignment="Bottom"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'm using this to fill the ListBox on Loaded event:
this.WallList.ItemsSource = StartingWall.GetWallPosts();
And now I want to add more items programmatically when the user write down some text on a TextBox and click on a button. I want to place this text on Comment field.
I'm gooing to fill the rest fields with default data.
My question is:
How can I add more items to WallList ListBox?
Someone has suggested to do the following:
public ObservableCollection<WallPostEntry> MyWallPosts {get;set;}
// Initialize MyWallPosts to whatever
MyWallPosts.Add(new WallPostEntry("new entry"));
<ListBox Margin="10,10,8,8" x:Name="WallList" ItemsSource="{Binding MyWallPosts}">
But Binding ListBox ItemsSource doesn't work for me. I'm initializing MyWallPosts on constructor, just before InitializeComponent();, like this:
public Wall()
{
MyWallPosts = StartingWall.GetWallPosts();
InitializeComponent();
}
Any advice?
Thanks.
I see a couple wierd things:
first, you're using the itemssource binding in one place, but explicitly setting it in another? setting something in code will override/undo any bindings, so that could cause a problem (but it looks like you're setting it to the same thing so that shouldn't make a difference, but i'd remove the this.WallList.ItemsSource = StartingWall.GetWallPosts(); call entirely, and leave the ItemsSource="{Binding MyWallPosts}" in the xaml. the point of using bindings is to get rid of this kind of code)
second, you're setting mywallposts and using a binding, but not setting the datacontext on your object itself? simplest in your example would be to just add one line to your constructor:
public Wall()
{
DataContext = this;
MyWallPosts = StartingWall.GetWallPosts();
InitializeComponent();
}
My next suggestion would be to simplify until it works. leave the listbox but comment out all of the item/data templating to make sure that you don't have a bug in your template
Could he just add
a DataContext property to this:
<ListBox Margin="10,10,8,8" x:Name="WallList" ItemsSource="{Binding MyWallPosts}">
so:
<ListBox Margin="10,10,8,8" x:Name="WallList" ItemsSource="{Binding MyWallPosts}" DataContext="{Binding MyWallPosts}">
Would there be any way of setting the dataContext declarativelly?
tks,
Oscar

Categories

Resources