Xamarin.Forms inflate view in code and pass parameter - xamarin.forms

I set up multiple different views that I can inflate from xaml like so:
<Grid Grid.Row="2" x:Name="grid_someelsesprofile_currentproducts">
<!-- ID = 4 for profile-->
<views:CV_AllAdsRes>
<x:Arguments >
<x:Int32>4</x:Int32>
</x:Arguments>
</views:CV_AllAdsRes>
</Grid>
I even pass a parameter, in this case an int with the value of 4.
Now I need to be able to inflate this also but from code and also pass the parameter and add this view to the grid. So basically all I am doing from above only from code basis.
CV_AllAdsRes is a contentview file.
How would that translate?
Thank you :)

It is actually super easy:
load the class holding the view and logic:
var xy = new CV_AllAdsRes(4, userID);
Then add class to grid.children method:
grid_someelsesprofile_currentproducts.Children.Add(xy);

Related

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!

Master Detail breaks parameters?

I don't need a MasterDetail until I've navigated through two regular ContentPages first, where I collect information that the MasterDetail will need. The ContentPages make use of the INavigationParameters sent to OnNavigatedTo and OnNavigatedFrom for that collected information.
The the SecondPage viewmodel handles a button command wherein the handler calls the MasterDetail:
NavigationService.NavigateAsync("CompareSelectionsMasterDetail/CompareSelectionsNavigationPage/RangeCircle",
parameters);
Sure enough, the RangeCircleViewModel.OnNavigatedTo receives the parameters. However, when I go back (like with the Android back button), its OnNavigatedFrom is not called, thereby sending null parameters back leaving my ContentPages with no idea what their state was.
The viewmodel for CompareSelectionMasterDetail doesn't do anything and the viewmodel for CompareSelectionsNavigationPage just does this:
public class CompareSelectionsNavigationPage : NavigationPage, INavigationPageOptions
{
public bool ClearNavigationStackOnNavigation => false;
}
I'm not sure what it means to have a NavigationPage in the MasterDetailPage XAML and the separate CompareSelectionsNavigationPage that I call with Prism but if I remove the XAML one, the RangeCircle page doesn't render. If I only have the XAML one and change the Prism navigation to CompareSelectionsMasterDetail/NavigationPage/RangeCircle then I get the same behavior as using both NavigationPages.
My MasterDetail XAML is simple.
> <MasterDetailPage.Master>
> <NavigationPage Title="Required Foo" Icon="hamburger_icon.png">
> <x:Arguments>
> <ContentPage Title="Menu">
> <StackLayout Padding="40">
> <!-- TODO: // Update the Layout and add some real menu items -->
> <Label Text="Hello John"/>
> <Button Text="Range Circle" Command="{Binding NavigateToRangeCircleCommand}" CommandParameter="ViewA" />
> </StackLayout>
> </ContentPage>
> </x:Arguments>
> </NavigationPage> </MasterDetailPage.Master>
Every example I can find of Master Detail (especially with Prism) starts off the app with Master Detail page. I'm not sure if my usage is what breaks OnNavigatedFrom? (Basically ContentPage(Page1)->ContentPage(Page2)->MasterDetail->NavigationPage->NavigationPage->ContentPage(RangeCircle))
The individual ContentPages don't have a problem calling OnNavigatedFrom.

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}">

How add a global "IsReadOnly" style to all DataGridTextColumns

I currently have a ResourceDictionary file for my WPF application, which pretty much adds every style that I could possibly want throughout all of my application's DataGrids.
Except one.
How can I add a global "IsReadOnly" setter, for all of my DataGrid's DataGridTextColumn columns ?
Basically, I use a few DataGrids, and if I want to display read-only data in a particular column, I'll just display the data using a DataGridTextColumn:
<WPFtoolkit:DataGridTextColumn Binding="{Binding Path=DOB,StringFormat='dd/MMM/yyyy'}" Header="DOB" Width="120" />
However, if I have a column which has editable data, then I'll use a DataGridTemplateColumn instead.
<WPFtoolkit:DataGridTemplateColumn xHeader="Department Name" >
<WPFtoolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="175"
ItemsSource="{Binding Source={StaticResource AllDepartmentsDataSource}}"
SelectedValue="{Binding DepartmentID}" SelectedValuePath="DepartmentID" DisplayMemberPath="DepartmentName"
VerticalAlignment="Center"
>
</ComboBox>
</DataTemplate>
</WPFtoolkit::DataGridTemplateColumn.CellTemplate>
</tWPFtoolkit:DataGridTemplateColumn>
The problem is, for every one of my DataGridTextColumns, I specifically need to add the IsReadOnly parameter, otherwise the user can (incorrectly) edit that data:
<WPFtoolkit:DataGridTextColumn IsReadOnly="True" Binding="{Binding Path=DOB,StringFormat='dd/MMM/yyyy'}" Header="DOB" Width="120" />
Is there a way to add this "IsReadOnly" setting globally, in the ResourceDictionary file, to all of my DataGridTextColumns...?
I can add global styles to DataGrid, DataGridColumnHeader, DataGridRow, and DataGridCell, but if I try to define a style with a TargetType of DataGridTextColumn, then Visual Studio complains that DataGridTextColumn is not derived from a FrameworkElement or FrameworkContentElement.
<Style TargetType="{x:Type WPFToolkit:DataGridTextColumn}">
<Setter Property="IsReadOnly" Value="True"/>
</Style>
I can add IsReadOnly to the Grid style, but this makes all columns uneditable !
Can anyone think of an quick and easy way to add this simple property to the DataGridTextColumns ?
Update:
My solution has been (reluctantly) to add a Loaded handler to each of my DataGrids, which runs this code:
void grdGrid_Loaded(object sender, RoutedEventArgs e)
{
DataGrid dg = (DataGrid)sender;
foreach (DataGridColumn col in dg.Columns)
{
DataGridTextColumn textCol = col as DataGridTextColumn;
if (textCol != null)
{
textCol.IsReadOnly = true;
}
else
{
// This DataGridColumn isn't of type "DataGridTextColumn", so do nothing.
}
}
}
You could, of course, put this in your own DataGrid-inherited control, rather than repeating it for each of your DataGrids.
(Sigh.)
Why didn't MS make IsReadOnly an attachable property..? It would've made life so much easier!

Resources