UWP XAML binding a Observable Collection class property - c#

I have a problem - I want to bind into a GridView a class property which is a ObservableCollection.
the class code:
public class Moment
{
...
public ObservableCollection<Uri> PhotoFilePath { get; set; }
}
XAML
<GridView Grid.Row="0"
Name="PhotosGridView"
ItemsSource="{x:Bind moment}">
moment is a instance of Moment in MainPage.xaml
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Moment">
<Image Width="75" Height="75">
<Image.Source>
<BitmapImage UriSource="{x:Bind PhotoFilePath}" />
here i get
Severity Code Description Project File Line Suppression State
Error Invalid binding path 'PhotoFilePath' : Cannot bind type 'System.Collections.ObjectModel.ObservableCollection(System.Uri)' to 'System.Uri' without a converter
</Image.Source>
</Image>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Thanks in advance

The way you have it right now doesn't seem to make sense. moment is an instance of Moment, not a list of things (which is what ItemsSource expects). The list of things is the PhotoFilePath property on Moment (which, as an aside, is poorly named since it is a collection, not a single path).
I'm assuming what you really want to do is bind ItemsSource to moment.PhotoFilePath:
ItemsSource="{x:Bind moment.PhotoFilePath}">
You'll need to change your DataTemplate to just target a Uri:
<DataTemplate x:DataType="Uri">
Note that I am not positive if you can just say "Uri" there. I don't have a UWP project in front of me.
and then UriSource to each item inside of that property:
<BitmapImage UriSource="{x:Bind}" />

The UriSource property of BitmapImage expects type 'System.Uri'
but you are trying to bind it to 'ObservableCollection of System.Uri'
(PhotoFilePath property of your Moment class is an ObservableCollection of System.Uri)
The answer is in the error message
'Cannot bind type 'System.Collections.ObjectModel.ObservableCollection(System.Uri)' to 'System.Uri'

Related

C# WinUI NavigationView MenuItemTemplate Throwing InvalidCastException

I'm trying to set up a NavigationView to be rendered by a list in the page's class, but I keep getting an exception thrown.
System.InvalidCastException: 'Unable to cast object of type 'WinRT.IInspectable' to type 'FeatureBranching.Views.MenuItem'.'
I have the following code in XAML:
<Page ...>
<Page.Resources>
<DataTemplate x:Key="NavigationViewMenuItem" x:DataType="local:MenuItem">
<NavigationViewItem Content="{x:Bind Name}" MenuItemsSource="{x:Bind Children}"/>
</DataTemplate>
</Page.Resources>
...
<NavigationView Grid.Row="1"
x:Name="MainNavigation"
Header="This is Header Text"
PaneDisplayMode="Left"
IsSettingsVisible="False"
MenuItemsSource="{x:Bind NavigationMenuItems}"
MenuItemTemplate="{StaticResource NavigationViewMenuItem}"
SelectionChanged="MainNavigation_SelectionChanged">
</NavigationView>
</Page>
Code behind class has:
public IEnumerable<MenuItem> NavigationMenuItems = new List<MenuItem>()
{
new MenuItem
{
Name = "Repositories",
Children= new List<MenuItem>()
{
new MenuItem
{
Tag = "123",
Name = "Test123",
Children = new ObservableCollection<MenuItem>()
}
}
}
};
It must have something to do with the DataTemplate because if I remove the DataType and x:Bind pieces from the DataTemplate and put constant strings in then it renders without exception. I've also tried making the list an ObservableCollection and it made no difference.
I don't know why (still new to WinUI development) but changing the {x:Bind...} inside the data template to {Binding...} fixed it. The Microsoft documentation uses x:Bind though, so not sure what that is about. If anyone knows why that would be great to know!
My DataTemplate is now... <NavigationViewItem Content="{Binding Path=Name}" MenuItemsSource="{Binding Path=Children}" />

UWP x:bind issue - Invalid binding path 'dpl' : Property 'dpl' not found on type 'DataTemplate'

I have a class PricingData and PricingSchedule. Where PricingSchedule is a List<> inside PricingData class. I want to bind data of this class to UWP controls.
Sample code is available to download here : https://github.com/jigneshdesai/SampleOfBindingIssue1.git
How Code looks: i have a start page(mainpage) that hosts ListView control, Listview has PricingUserControl within it. PricingUserControl looks like this
<TextBlock x:Name="lblPriceHeader" Text="{Binding PricingTitle}" Margin="0,0,50,0" />
<ComboBox x:Name="cbPriceValueList" ItemsSource="{x:Bind dpl}" DisplayMemberPath="PriceValue" SelectedValuePath="PriceValue" SelectedValue="{Binding DisplayPricing}" />
<ListView x:Name="lbPriceChangeSchedule" ItemsSource="{Binding PricingScheduleList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<ComboBox x:Name="cbSchedulePriceValueList" ItemsSource="{x:Bind dpl}" DisplayMemberPath="PriceValue" SelectedValuePath="PriceValue" />
<TextBlock Text="{Binding SchedulePricingTimeZone }" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
What i want to achieve: Combobox should populate a list of values (eg. 1USD, 2USD, 3USD etc.). Then when you provide List of records from database, the listbox will repeat PricingUserControl and combobox within it should set its value property (SelectedValue) as per record.
Issue:
ComboBox x:Name="cbPriceValueList" uses x:bind dpl where dpl is a local variable of PricingUserControl. It populates the list properly. The trouble is ComboBox x:Name="cbSchedulePriceValueList" it also has x:bind dpl but during compilation it display error "Invalid binding path 'dpl' : Property 'dpl' not found on type 'DataTemplate'."
I am wondering why x:bind dpl does not work at this point. ?
I have now realized that your problem is in fact that you need to reach to a Page property from within the DataTemplate, so here is a updated answer.
You cannot use x:Bind if you need to access an outside element's property from within a DataTemplate. Instead, you can use classic {Binding} expression. First add a name to your page:
<Page
...
x:Name="Page">
And now refer to this name from within the DataTemplate:
<ComboBox
x:Name="cbSchedulePriceValueList"
ItemsSource="{Binding ElementName=Page, Path=dpl}"
DisplayMemberPath="PriceValue"
SelectedValuePath="PriceValue" />
Original answer
To be able to use x:Bind inside of a DataTemplate, you must specify the data type the individual items of the control will have, using x:DataType. Suppose your PricingScheduleList is a List<MyApp.Models.MyType>, then you will first need to add this XML namespace to the <Page> element:
xmlns:models="using:MyApp.Models"
And then set the x:DataType attribute as follows:
<DataTemplate x:DataType="models:MyType">
...
</DataTemplate>
You can confirm this works by the fact that IntelliSense should now suggest you the properties of MyType when you start writing the x:Bind expression.
By checking your code, the reason why SelectedValue does not take effect is when you choose the item from ComboBox, you didn't notify your DisplayPricing to change. So you need to implement INotifyPropertyChanged interface in your PricingData. Do the same behavior in PricingSchedule.
public class PricingData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
......
public string DisplayPricing
{
get => $"{PricingValue} {PricingCurrency}";
set
{
var sp = value.Split(' ');
PricingValue = sp.First();
PricingCurrency = sp.Last();
OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
PScheduleUserControl.xaml:
<ComboBox x:Name="cbPriceValueList" ItemsSource="{x:Bind myList}" DisplayMemberPath="PriceValue" SelectedValuePath="PriceValue" SelectedValue="{Binding DisplayPricing,Mode=TwoWay}" />

Binding BitmapImage UriSource

I'm trying to develop my first Windows Store application.
I'm using the Hub Application template.
I want to display an Image from a Url in the first section of my HubPage:
<HubSection ... >
<DataTemplate>
<Grid ... >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
...
</Grid.RowDefinitions>
<Image Name="UserProfileImage" Margin="100, 0, 100, 0" Grid.Row="0" VerticalAlignment="Top">
<Image.Source>
<BitmapImage UriSource="{Binding ImageUrl}"></BitmapImage>
</Image.Source>
</Image>
</Grid>
</DataTemplate>
</HubSection>
And in my HubPage.xaml.cs:
[DefaultValue("http://images6.fanpop.com/image/photos/34200000/more-dumb-images-of-philip-j-fry-futurama-34257101-1440-900.png"")]
public string ImageUrl { get; set; }
But nothing is shown. If I set manually in the xaml file an image url it works fine...
The problem is, that the Binding mechanism does not know where to look for the ImageUrl property.
You can either set the DataSource of the tag or any of it's parents to an instance of the class, where the property is defined.
Or you use more information in each Binding statement. To bind to yourself, just use
UriSource="{Binding RelativeSource={RelativeSource Self}, Path=ImageUrl}"
or
UriSource="{Binding ImageUrl, RelativeSource={RelativeSource Self}}"
see also WPF Bind to itself
Edit:
You also need to have a notification mechanism on your variable you are binding to. Either make it a DependencyProperty or use a PropertyChanged event (either through INotifyPropertyChanged and call PropertyChanged on changes in the setter with the name of the property, or create an event called <name of property>Changed and invoke this event on changes.
Unfortunately it seems that BitmapSource.UriSource cannot be databound (or more specifically, it can only be bound once and further changes ignored). See discussion here.
In your code you're using <DataTemplate> which means the parent control of this will be either <ListView> or something like that.
Your code reflects very few things about your scenario.
I suggest you to bind ImageSource instead of string
Instead working on XAML part, I suggest you to edit your code-behind part.
I'm briefing the sample here. relate this, with your case and do needful.
Example:-
// BlankPage.xaml ----
// eg. I've a listView and i want to display
// image dynamically in DataTemplate
<ListView x:Name="lstView">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding bmp}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now, define a model class to give itemsSource to ListView.
// In BlankPage.xaml.cs file
public class model
{
public ImageSource bmp { get; set; }
}
Now, say for example I'm assigning itemsSource to ListView in Page_Loaded event.
// in BlankPage.xaml.cs file
// declare this BlankPage's Loaded event in BlankPage's Constructor
// and define the event handler like this
void BlankPage2_Loaded(object sender, RoutedEventArgs e)
{
model m1 = new model()
{
bmp = new BitmapImage(new Uri("ms-appx:///Assets/TechVista(300x300).png", UriKind.Absolute))
};
// here "ms-appx:///Assets/TechVista(300x300).png" should be the Full-System-Path where image is located.
// and according to that declare the UriKind as Relative or Absolute or other.
List<model> lstModels = new List<model>();
lstModels.Add(m1);
// you can add as many models as you want here.
// for reference I'm adding only one here.
lstView.ItemsSource = lstModels;
}
It will work for sure.
For more exact answer, elaborate little more here.
Hope that helps..!

providing source for an Image control

I need to databind into a element inside my custom class. i've given the ItemSource as the ObservableCollection of telerik:RadTransitionControl via an attached property. However, I need to provide the image member as the source to Image control. I tried the following method and was unsuccessful.
<Grid Background="Black">
<telerik:RadTransitionControl x:Name="radControl" adRotator:AdRotatorExtensions.ItemChangeDelay="0:0:3"
adRotator:AdRotatorExtensions.CurrentSelectedIndex="0"
adRotator:AdRotatorExtensions.IndexChanged="{Binding TopItemCommand, Mode=OneWay}"
adRotator:AdRotatorExtensions.ItemsSource="{Binding Path=ImagePaths}"
VerticalAlignment="Center"
HorizontalAlignment="Center" Width="650">
<telerik:RadTransitionControl.Transition>
<telerik:MotionBlurredZoomTransition />
</telerik:RadTransitionControl.Transition>
<telerik:RadTransitionControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding Path=ImagePaths.AdImage}" />
</DataTemplate>
</telerik:RadTransitionControl.ContentTemplate>
</telerik:RadTransitionControl>
</Grid>
an ImagePaths object is already set as the DataContext for the item. this means that the binding is already pointing (so to speak) at an instance of the object. so, when you tell it to bind on ImagePaths.AdImage it does not know how to find the property you are looking for. Good news is, all you have to do is provide the path on the object-- remove the ImagePaths part (and the dot) and you should be good to go.
for example...
class something
{
public string someImage {...}
}
<DataTemplate> <!--------- the data context of this item is an instance of
my "something" class, so i need to set the path
to be the property on the object --->
<Image Source="{Binding Path=someImage}" />
</DataTemplate>
here is a very helpful article on debugging bindings in WPF
for more info here is an excellent article on MSDN
here is a datatemplate article from Dr. WPF

Bind a collection to a WPF ListBox

Update: I've updated the code based on your help so far, and still no luck. When the application loads the ListBox has no items. I assign junk values to Customers in the windows's contructor, and then am also trying to set the ListBox's DataContext as follows:
CustomerList.DataContext = Customers;
--- Original Question (with updated code) ---
I'm having trouble with databinding in a WPF project.
I have a class, Customer, as follows:
public class Customer
{
public String Name { get; set; }
public String Email { get; set; }
}
In my XAML's code behind I have a collection of customers as follows:
public List<Customer> Customers { get; set; }
I'm trying to bind each customer to a ListBox with a ListItemTemplate displaying the customer's information (name/email) in TextBoxes along with a button which locks/unloacks the TextBoxes (sets the IsEnabled property to true or false).
What's the best way to go about this?
So far I've been tryingt he following with no success.
In the XAML I currently have the following (ignoring the toggle part for now, I'm just trying to get the collection itself to be listed.):
<Window.Resources>
<CollectionViewSource x:Key="Customers" Source="{Binding Path=Customers, Mode=TwoWay}"/>
<DataTemplate x:Key="Customer">
<StackPanel Orientation="Horizontal">
<TextBox Content="{Binding Name}" />
<TextBox Content="{Binding Email}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding Source={StaticResource Customers}}"
ItemTemplate="{StaticResource ResourceKey=Customer}"
Name="CustomerList"
Height="300" />
</StackPanel>
You need to change
ItemsSource="{Binding Source=Customers}"
to
ItemsSource="{Binding Source={StaticResource Customers}}" DataContext="{StaticResource Customers}"
Code similar to the updated one works for me after changing
<TextBox Content="{Binding Name}" />
to
<TextBox Text="{Binding Name}" />
As TextBox doesn't have Content property(like a Label), the former refused to compile in VS.
Well, it is set to Text in definition:
[ContentPropertyAttribute("Text")]
public class TextBox : TextBoxBase, IAddChild
But I thought it is only used between the brackets(<TextBox>Like so</TextBox>)?
Could this be the source of the problem?
Try setting the ItemsSource of your CustomerList as follows: ItemsSource="{Binding}". You've set the DataContext of the ListBox to the list of customers, you need to set the ItemsSource to the same collection, hence, the direct binding.
Another thing that you can do, in case you prefer to use the CollectionViewSource, is to set the DataContext of your window to the same class DataContext=this, because without this, the resource definition won't be able to locate the "Customers" collection that you defined in the code behind. If you do this, however, you don't need CustomerList.DataContext = Customers; because you're directly assigning the ItemsSource to a static resource, not relatively to the DataContext.
One more thing. I think you should give the CollectionViewSource and the corresponding collection in the code behind different names. This isn't going to cause a runtime issue, but it makes it hard to maintain the code ;)
Hope this helps :)

Categories

Resources