UWP One to many binding - xaml

i'm building venue navigation app, so i have two views, one is a list of possible destinations and the second is an jpg image with markers on top, so problem is:
on my model:
public class NavigationItem
{
public string Name {get; set;}
public string Icon{get; set;}
public List<Vector2> Location {get; set;}
}
on my viewmodel:
{
public List<NavigationItem> NavigationItems {get; set;}
}
now the xaml part
<ScrollViewer>
<Grid>
<Image Source="map.jpg/>
<winui:ItemsRepeater ItemsSource="{Binding NavigationItems}">
<DataTemplate>
<!-- And that's where the question is....
I can't just do Image Source Binding, because location is List<Vector2>-->
</DataTemplate>
</winui:ItemsRepeater>
</ScrollViewer>
So, i thought to make another items repeater nested in first one, but i have no clue how to propagate Image Source to it like so:
<winui:ItemsRepeater ItemsSource="{Binding NavigationItems}">
<DataTemplate>
<winui:ItemsRepeater DataContext="{Binding}" ItemsSource="{Binding Location}" >
<DataTemplate x:DataType="models:NavigationItemModel">
<Image Source="{Binding Icon}"/>
</DataTemplate>
</winui:ItemsRepeater>
</DataTemplate>
</winui:ItemsRepeater>
All i get just bunch of binding errors, guess i'll just a user control and spawn all the crap in code behind...

If you want to bind the Image Source with the Icon property, you need to change the DataContext of Image to the DataContext of the second ItemsRepeater. So you can subscribe the ElementPrepared event from ItemsRepeater which means this event occurs each time an element is made ready for use. So when triggered this event, you can change the DataContext of Image in it.
.xaml:
<winui:ItemsRepeater ItemsSource="{Binding NavigationItems}">
<DataTemplate>
<winui:ItemsRepeater ItemsSource="{Binding Location}" ElementPrepared="ItemsRepeater_ElementPrepared">
<DataTemplate>
<Image Source="{Binding Icon}"/>
</DataTemplate>
</winui:ItemsRepeater>
</DataTemplate>
</winui:ItemsRepeater>
.cs:
private void ItemsRepeater_ElementPrepared(Microsoft.UI.Xaml.Controls.ItemsRepeater sender, Microsoft.UI.Xaml.Controls.ItemsRepeaterElementPreparedEventArgs args)
{
var repeaterDataContext = sender.DataContext;
Image MyImage = args.Element as Image;
MyImage.DataContext = repeaterDataContext;
}

Related

Xamarin Binding will not work with generated observable collection

My question boils down to why won't my observable collection binding work? I am attempting to bind an image url to a listview, but the binding will not work, pretty much just make a list of product images. I can generate the image if I just do a stack layout and declare the image based on the list position (ie image.source = list[i].url).
Below is the xaml
<ListView ItemsSource="{Binding ActImage}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Below is the c behind the xaml where "PicListPull" is the model and pictures is the previously generated list of image urls.
private ObservableCollection<PicListPull> actimage;
public ObservableCollection<PicListPull> ActImage;
ActImage = new ObservableCollection<PicListPull>();
for (int i = 0; i < pictures.Count(); i++)
{
var pics = new PicListPull()
{
ImageUrl = ImageSource.FromUri(pictures[i].Url)
};
ActImage.Add(pics);
}
If I look at the ActImage at an index, it has a value for the image source, so I can't figure out why it won't display the image.
Thanks in advance.
Update
Below is the PicListPull
public class PicListPull:INotifyPropertyChanged
{
private ImageSource imageurl;
//#region public properties
public ImageSource ImageUrl
{
get { return imageurl; }
set
{
this.imageurl = value;
RaisePropertyChanged("ImageUrl");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(String name)
{
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
The cell of ListView should be one of these type :
TextCell, ImageCell, SwitchCell, EntryCell or CustomCell (ViewCell)
In this case if you want to use custom cell then please update like this:
<ListView ItemsSource="{Binding ActImage}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
For more details you can check here : Custom Cells
there are three issues you need to fix
first, you can only bind to public properties
ItemsSource="{Binding ActImage}"
for this to work, ActImage needs to be a public property
public ObservableCollection<PicListPull> ActImage { get; set; }
second, you need to use a Cell in your template (as #Qwerty noted)
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
finally, when binding the Image, you can just use a string containing the url

Binding with Prism Navigation

I'm trying to bind string path in XAML using prism navigateto extension method from a button in ListView.
Apparently the BindingContext isn't recognized to be the same as in the ListView
Here's the sample code of what i'm trying to achieve.
<ListView
x:Name="MainMenu"
CachingStrategy="RetainElement"
HasUnevenRows="True"
ItemsSource="{Binding MenuItems}"
Margin="20,0,0,0"
SeparatorVisibility="Default">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<buttons:SfButton
Style="{StaticResource BaseButtonStyle}"
HeightRequest="70"
TextColor="{StaticResource MenuTextColor}"
Text="{Binding Title}"
HorizontalTextAlignment="Start"
VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"
ImageSource="{Binding MenuItemType, Converter={StaticResource MenuItemTypeConverter}}"
ShowIcon="True"
Command="{prism:NavigateTo Name={Binding Item.View}}"
/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Any ideas how to set the binding in this situations?
Regards.
Within any sort of ListView, CollectionView, etc where you are binding an ItemsSource and then have some sort of DataTemplate to display an individual item in that collection, the Binding Context within that DataTemplate is the individual item in the collection not the ViewModel that that provides the Binding Context of both your Page and the ListView.
There are technically a couple parts to this that you will want to understand. Let's say that your model looks like:
public class FooModel
{
public string Text { get; set; }
public string NavigationPath { get; set; }
}
And let's say that you have a collection of FooModel like:
public ObservableCollection<FooModel> FooItems { get; }
In XAML you might have something like:
<ListView ItemsSource="{Binding FooItems}">
However when you go to reference properties of FooItem you will just reference them like:
<ListView.DataTemplate>
<DataTemplate>
<ViewCell>
<Button Text="{Binding Text}"
Command="{prism:NavigateTo Name={Binding NavigationPath}" />
</ViewCell>
</DataTemplate>
</ListView.DataTemplate>
Now assuming that the issue isn't that you're just adding Item erroneously, let's look at some other possible issues/solutions. To start let's look at the start of our page.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
x:Name="page"
x:Class="HelloWorld.Views.ViewA">
The big thing to notice here is that I've added the x:Name attribute so that I can reference the page itself later. In general within the context of something like a ListView if I need to access say the FooCommand property within my ViewModel I might would change my XAML markup from:
<Button Command="{Binding FooCommand}" />
To instead look at the Page's BindingContext like this:
<Button Command="{Binding BindingContext.FooCommand, Source={x:Reference page}}" />
While this will help you in general within your ListView it still doesn't necessarily help you with the issue of using Prism's Navigation Extensions. For this you may need to pass in the SourcePage like the following:
<Button Command="{prism:NavigateTo 'Foo', SourcePage={x:Reference page}}" />
In the event this doesn't work for some reason then you may be possible that the BindingContext isn't getting set properly on the Navigation Extension itself. To work around this you would want to update your command as follows:
<Button x:Name="cellButton"
Command="{prism:NavigateTo Name={Binding View},
BindingContext={x:Reference cellButton}}" />
Note that if you need to reference the SourcePage or add the BindingContext to resolve your issue, please provide a sample that reproduces the issue and open an issue on GitHub.

How to Bind Parallax View within GridView DataTemplate?

I am trying to use a ParallaxView to make an image within the GridViewItem parallax as the GridView is scrolled. The intended effect is the same as the newsfeed in the Xbox UWP app on PC; images on the listview items there parallax as you scroll. This is shown visually in the following image:
I am running into a databinding data context issue, however. All of the examples I have seen are to make the background of the entire GridView or ListView parallax. A working example of that is as follows (very similar to the XAML Controls Gallery Sample found here):
<Grid>
<ParallaxView Name="GridViewParallaxView"
Source="{x:Bind MyGridView}"
VerticalShift="100">
<!-- This is the background image that parallaxes. -->
<Image></Image>
</ParallaxView>
<GridView Name="MyGridView">
<!-- GridView Content Here... -->
</GridView>
</Grid>
The problem I am running into is when trying to place the ParallaxView INSIDE of the DataTemplate in the ItemTemplate in the GridView.
<GridView Name="MyGridView"
ItemsSource="{x:Bind MyDataList}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="models:MyDataType">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ParallaxView Name="GridViewParallaxView"
Grid.Row="0"
Source="{x:Bind MyGridView}"
VerticalShift="100">
<!-- This is the image ON EACH GRIDVIEW ITEM that parallaxes. -->
<Image Source="{x:Bind MySource}"></Image>
</ParallaxView>
<TextBlock Name="ItemTitleTextBlock"
Grid.Row="1"
Text="{x:Bind Title}"></TextBlock>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Note that some properties (like MinWidth and Margin) have been omitted for brevity.
The code behind (not totally relevant, but to add context to some of the bindings):
public class MyPage : Page
{
public ObservableCollection<MyDataType> MyDataList { get; set; }
}
public class MyDataType
{
public string Title { get; set; }
public ImageSource MySource { get; set; }
}
This does not work because placing the 'ParallaxView' inside of the 'DataTemplate' changes the 'DataContext'. MyGridView can no longer be bound to directly like that. So how do I bind it?
Also, I read that the DataContext property is inherited by children in the XAML tree. I need the Image databinding to be in the same context as the DataTemplate. Is there a way to just change the DataContext for the ParallaxView?
You could use Binding, instead of x:Bind.
<ParallaxView Name="GridViewParallaxView"
Grid.Row="0"
Source="{Binding ElementName=MyGridView}"
VerticalShift="100">
<!-- This is the image ON EACH GRIDVIEW ITEM that parallaxes. -->
<Image Source="{x:Bind MySource}"></Image>
</ParallaxView>

ListView doesn't display data in xamarin

I am new to xamarin and trying to display a listview. My attempt is as follows.
<ContentPage.Content>
<StackLayout>
<ListView x:Name="ComListView" RowHeight="80">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid Margin="8">
<Label Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Text="hello world" FontAttributes="Bold" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
But when I ran the app, even I navigate to correct page; nothing display. For any other views, it will display the right output. So where could I get wrong on the listview?
You have to put items to the ListView. Currently, you only specify the ItemTemplate, which determines how an item will look (in your case, they will all look the same (i.e. Labels with 'hello world' text)). But the ListView is empty and so there are no items to show.
You can set items using the ItemsSource property of the ListView.
Usually, you use Binding inside an ItemTemplate to show item content.
Let's say we have a class Person:
class Person
{
public string Name { get; set; }
}
Then you create a List of Person objects and add it to your ListView in code:
var personnel = new List<Person>
{
new Person { Name = "Booker" },
new Person { Name = "Elizabeth" }
};
ComListView.ItemsSource = personnel;
And in your XAML, you have to set the ItemTemplate so that it shows the Name of each Person:
<Label Text="{Binding Name, Mode=OneWay}" />
Note: You can also bind to ItemsSource from XAML if you have a ViewModel.
You are not binding the data to the listview.
use the ItemSource property to bind data, either from xaml, code behind or use a binding to populate from your ViewModel

Update UserControl Textblock text from mainpage.xaml

I am writing a Windows Phone 8.1 Silverlight App. I made a user control NotificationsIconUserControl. It just contains a BELL/ALARM icon and textblock to display number of unread notifications.
I want to update this textblock text from mainpage.xaml
How to do this?
I tried using usercontrol expose properties but its the opposite thing. Also tried help from this question. how to use dependency property. Please edit the code below:
Usercontrol XAML:
<Grid x:Name="LayoutRoot"
Background="Transparent"
Height="Auto"
Width="Auto">
<Image
Name="Alarm_Icon"
Source="/Images/Status/Notification_Icon_1.png">
</Image>
<Ellipse
Name="Counter_Icon"
Height="45"
Width="45"
Margin="60,14,-6,50"
StrokeThickness="0"
Fill="{StaticResource DefaultTheme_IndianRedColor}">
</Ellipse>
<TextBlock
Name="Counter_Label"
Foreground="{StaticResource DefaultTheme_LightColor}"
FontSize="30"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
Margin="75,20,8,58"/>
</Grid>
Mainpage XAML part:
xmlns:MyUserControls="clr-namespace:Project.Custom.UserControls">
Mainpage .cs part:
private void ConfigureNotificationsIcon()
{
int NotificationsCounter = 4;
NotificationsIconUserControl NotificationsIconUserControlObject = new NotificationsIconUserControl();
NotificationsIconUserControlObject.Counter_Label.Text = NotificationsCounter.ToString();
}
I checked your code and its working completely....
ANd for the part of adding a Dependency property, write the following in the .cs file of UserControl
public partial class NotificationIconUserControl : UserControl
{
public NotificationIconUserControl()
{
InitializeComponent();
}
public string NotificationLabel
{
get { return (string)GetValue(NotificationLabelProperty); }
set { SetValue(NotificationLabelProperty, value); }
}
// Using a DependencyProperty as the backing store for Spacing. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NotificationLabelProperty =
DependencyProperty.Register("NotificationLabel", typeof(string), typeof(NotificationIconUserControl), new PropertyMetadata("hellllo"));
}
After that you can use TemplateBinding to do your job