Unable to bind ObservableCollection to DataGrid in UserControl - uno-platform

I am trying to pass ObservableCollection from the XAML Page to the UserControl.
In a normal Visual Studio WPF project .NET Framework 4.8, within the UserControl the below XAML code works fine and the data can be seen within the Page.
<DataGrid Grid.Row="0" x:Name="dgAuthors" ItemsSource="{Binding Authors, RelativeSource= RelativeSource Mode=FindAncestor, AncestorType=local:UserControl1}}
Code behind of User Control as below:
public UserControl1()
{
InitializeComponent();
dgAuthors.DataContext = this;
}
public ObservableCollection<Author> Authors
{
get { return (ObservableCollection<Author>)GetValue(AuthorsProperty); }
set { SetValue(AuthorsProperty, value); }
}
// Using a DependencyProperty as the backing store for Authors. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AuthorsProperty =
DependencyProperty.Register("Authors", typeof(ObservableCollection<Author>), typeof(UserControl1), null);
But in UNO for .NET 5.0 framework, it is unable to find the Mode=FindAncestor and AncestorType when Binding is used within the ItemSource of the DataGrid.
Any help would be highly appreciated.
Many thanks
Neeraj

With the WinUI flavour of XAML used by Uno Platform, you can use x:Bind syntax to bind to a property in code-behind:
<DataGrid Grid.Row="0" x:Name="dgAuthors" ItemsSource="{x:Bind Authors, Mode=OneWay}} />

Related

Xamarin forms parallel View loading

I have a DashboardPage in Xamarin Forms where I "inject" another 3 views with the following method:
<StackLayout IsVisible="{Binding IsDashboardItemVisible}">
<views:FirstView/>
<views:SecondView/>
<views:ThirdView/>
</StackLayout>
All of these views have their viewmodels (FirstViewModel, SecondViewModel, ThirdViewModel).
Every view have an Init method binded to the OnAppearing() event with the following appearance (xct is the Community Toolkit what I use):
<ContentPage.Behaviors>
<xct:EventToCommandBehavior EventName="Appearing" Command="{Binding RefreshCommand}" />
</ContentPage.Behaviors>
So in my xaml.cs the OnAppearing() is not overridden, but the FirstViewModel, SecondViewModel, ThirdViewModel has an ICommand what is connected with the RefreshAsync method.
public IAsyncCommand RefreshCommand { get; set; }
RefreshCommand = new AsyncCommand(RefreshAsync);
public async Task RefreshAsync()
{
...do the refresh logic...
}
So here are my questions:
In this scenario the 3 "injected" view will be loaded parallel or FirstView first, SecondView second, etc?
What is the best practice to communicate between this seperated views? For example when an user triggers a method on the FirstViewModel I would like to change something on the ThirdView (or ThirdViewModel). I read about the MessagingCenter, but I don't know if that is the best opportunity.

Xamarin.Forms Open ContentView with parameters in xaml

In my app there is a certain listview that I use over and over in my app, only with different elements in it. Therefore, I put everything inside a contentview and inflated it in my xaml like so:
<ContentPage Title="Newbies" BackgroundColor="#fafafa">
<views:CV_AllAdsRes />
</ContentPage>
The class looks like this:
public partial class CV_AllAdsRes : ContentView
{
public CV_AllAdsRes(int id)
{
InitializeComponent();
SetAds();
}
}
Now, this doenst work, because I am not using a "default constructor". If I remove the "int id" from the constructor, it works no problem. But I need to be able to inflate this content view with different parameters inside the xaml.
Am I understanding this concept wrong?
How can I inflate my content view and give it parameters via xaml?
Thank you
I solved it by using a second constructor next to the default one and giving it arguments from xaml like so:
<views:CV_AllAdsRes >
<x:Arguments >
<x:Int32>5</x:Int32>
</x:Arguments>
</views:CV_AllAdsRes>
this will give ID=5.

setting up a simple component with data binding

I am trying to set up a component with data binding. This is basically a seperate content view that would have a property Item of type Item and supports binding. The following is the definition for the binding:
public static readonly BindableProperty ItemProperty
= BindableProperty.Create(
nameof(Item), typeof(Item), typeof(ItemComponent), null,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: ItemPropertyChanged);
private readonly ItemComponentViewModel vm;
static void ItemPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = (ItemComponent)bindable;
view.Item = (Item)newValue;
}
public Item Item
{
get => (Item)GetValue(ItemProperty);
set
{
SetValue(ItemProperty, value);
if (vm != null) vm.Data = value; // break point here
}
}
The item doesn't seem to get bound. The commented line had a breakpoint and doesn't break. The complete source code is here: https://github.com/neville-nazerane/xamarin-component-sample
The above code can be found in the ItemComponent class. This component is called in the MainPage class.
Update
Just to explain what I am trying to simulate and why:
Why do we use MVVM in pages? While we'll have better type safety and performance by using the behind code directly, when the page's logic gets bigger, it becomes cleaner to handle it with a view model and to have a view that is simply bound to it.
Why do we have components? So that we can reuse a UI we intend to use with some functionality. If this functionality becomes complex it might need a view model for the same reason explained above. Hence, if pages need view models, I don't see why components won't need them at some point too.
This being considered this does feel like a particle requirement without easy to find examples.
So after looking at your example it turns out it's a bit of a complicated problem. So if my explanation is not clear, please let me know.
Basically the problem lies in these 2 code pieces:
MainPage.xaml(line 14):
<local:ItemComponent Item="{Binding Demo}" />
ItemComponent.xaml.cs (line 43):
public ItemComponent()
{
InitializeComponent();
vm = new ItemComponentViewModel();
BindingContext = vm; //this breaks the functionality
}
The first part you tell it to bind to the Demo property, and as normal it looks for this property in it's BindingContext. However in the second part you override it's BindigContext and set it to a ItemComponentViewModel this ViewModel however does not have a property Demo so the {Binding Demo} does not work on this new BindingContext you've set.
Now a possible solution for your demo application would be to change MainPage.xaml to the following code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SampleApp"
x:Class="SampleApp.MainPage"
x:DataType="local:MainViewModel"
x:Name="MyDemoPage">
<StackLayout>
<Label Text="Manual:" />
<Label Text="{Binding Demo.Title}" />
<Label Text="Component: " />
<local:ItemComponent Item="{Binding Path=BindingContext.Demo, Source={x:Reference MyDemoPage}}" />
</StackLayout>
</ContentPage>
Basically we now place the Demo binding outside of the BindingContext of our ItemComponent control. However if you want to use it in a ListView (if I remember correctly from your original question, this solution might not work and it's possible you'll have to drop the ItemComponentViewModel and bind directly to the properties (ListView will already make sure that the BindingContext of your ItemComponent is set to the current Item, no need to pass it around through a bindable property.
Hope this helps!

Binding to the SelectedItem in a ListBox which is in a DataTemplate for a ContentControl

Using an MVVM approach, I have a View that contains a ListBox and also a Grid in which I want to display information about the SelectedItem in the ListBox. I want to set the DataContext for the Grid to the SelectedItem.
However, the ListBox is buried as follows: A ContentControl that is bound to a DataTemplate that is a UserControl View that contains the ListBox.
Here is the MainWindow Grid that I'm not sure how to bind:
<Grid DataContext="{Binding ElementName=MyList, ????}">
Here is the ContentControl in the same View:
<ContentControl x:Name="MyList"
Content="{Binding}"
ContentTemplate="{StaticResource MyListTemplate}"/>
Here is the Data Template in the same View:
<Window.Resources>
<DataTemplate x:Key="MyListTemplate">
<v:MyListView/>
</DataTemplate>
</Window.Resources>
Here is MyListView:
<UserControl>
<ListBox Name="MyListBox" ItemsSource="{Binding ItemList}"/>
</UserControl>
I am adding to code I wrote a couple of years ago and have been away from WPF for a while, so alas, I am rusty on my data binding. I have been trying to add a SelectedItem property to the view model for MyListView and/or the MainWindow. I expect this may require RelativeSource.
Doh! I was forgetting to specify the OnPropertyChanged call for my property.
In the UserControl ListBox, I needed this:
ItemsSource="{Binding ItemList}" SelectedItem="{Binding MySelectedItem}"
In the main window view model, I needed this:
public MyItemViewModel MySelectedItem
{
get { return _mySelectedItem; }
set
{
_mySelectedItem = value;
OnPropertyChanged("MySelectedItem");
}
}
Then, in the main window, the binding is simply:
<Grid DataContext="{Binding MySelectedItem}">

Binding to selection of ViewModel

I'm trying to bind my ListBox to a selection of my ViewModel, because I have multiple ListBoxes in a Pivot and I don't want to type out the entire Page for each property. To illustrate my issue, here's a small sample:
XAML:
<DataTemplate x:Key="PropertyTemplate">
<StackPanel>
<TextBlock Text="{Binding Label}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
<controls:Pivot>
<controls:PivotItem>
<ListBox ItemsSource="{Binding PropertySelectionOne}" ItemTemplate="{StaticResource PropertyTemplate}" />
</controls:PivotItem>
<controls:PivotItem>
<ListBox ItemsSource="{Binding PropertySelectionTwo}" ItemTemplate="{StaticResource PropertyTemplate}" />
</controls:PivotItem>
</controls:Pivot>
ViewModel:
public class SomeViewModel
{
private Property _propOne;
public Property PropOne
{
get { return _propOne; }
set { _propOne = value; NotifyPropertyChanged("PropOne"); }
}
private Property _propTwo;
public Property PropTwo
{
get { return _propTwo; }
set { _propTwo = value; NotifyPropertyChanged("PropTwo"); }
}
private Property _propThree;
public Property PropThree
{
get { return _propThree; }
set { _propThree = value; NotifyPropertyChanged("PropThree"); }
}
}
So basically I want to bind my ListBoxes to PropertySelectionOne and PropertySelectionTwo, which would contain references to a selection of the properties in my ViewModel. For instance, PropertySelectionOne could include PropOne and PropTwo and PropertySelectionTwo could include PropTwo and PropThree.
Is there a simple way to "group" these properties to a new property to bind against without changing the architecture of my application?
Thanks
If you've got different properties to be displayed from the same date type in different list boxes, then arguably you need to split your view model, but you say you don't want to change the architecture of your application, which is your choice.
So, what you need to do is to provide a different ItemTemplate for each ListBox that defines which properties and how you want to display in each ListBox. Then you can bind the ItemsSource for all of the list boxes to the same data source but they will present different properties according to the ItemTemplate.
Not sure how familiar you are with these concepts, but you know that ItemsSource needs to be a collection of your data instances (SomeViewModel?), right?

Resources