I have Xamarin Forms xaml:
// MainPage.xaml
<?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:BlankAppXamlXamarinForms"
x:Class="BlankAppXamlXamarinForms.MainPage">
<Label Text="{Binding myProperty}" />
</ContentPage>
And I have code behind:
// MainPage.xaml.cs
namespace BlankAppXamlXamarinForms {
public partial class MainPage : ContentPage
{
public string myProperty= "MY TEXT";
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
}
}
It should bind myProperty to label's text. However, nothing displays in the label. How to bind myProperty to label's text? (I know I should use ViewModel to be able to notify view about changes of the property but in this example I really just want to bind myProperty from code behind to the label)
You need to declare that you can "get" the variable.
public string myProperty { get; } = "MY TEXT";
If you actually want to change this variable in code, your class will need to implement INotifyPropertyChanged, otherwise it will always be "MY TEXT"
Related
I'm using view models for my ContentPage's in my Xamarin Forms 5 app and typically call an Init() method in my view model from the OnAppearing() method in code behind.
I tried the same approach in my ContentView but it's never hitting the OnAppearing() method.
This is my ContentView code:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.MyContentView">
<ContentView.BindingContext>
<vm:MyViewModel/>
</ContentView.BindingContext>
<ContentView.Content>
<StackLayout
BackgroundColor="{StaticResource PrimaryDark }"
HeightRequest="200">
<Label
Text="{Binding User.FullName}"
TextColor="White"
FontSize="Medium"
FontAttributes="Bold"
HorizontalOptions="CenterAndExpand"/>
</StackLayout>
</ContentView.Content>
</ContentView>
The view model for this content view looks like this:
public class MyViewModel : BaseViewModel
{
User user;
public MyViewModel()
{
}
public User User
{
get => user;
set
{
if (user == value)
return;
user = value;
OnPropertyChanged();
}
}
public async void Init()
{
// Get user info
var data = await _dbService.GetUser();
if(data != null)
{
User = data;
OnPropertyChanged(nameof(User));
}
}
}
And in my code behind, this is what I'm doing:
public partial class MyContentView : ContentView
{
MyViewModel _vm;
public MyContentView()
{
InitializeComponent();
_vm = new MyViewModel();
BindingContext = _vm;
}
protected virtual void OnAppearing()
{
_vm.Init();
}
}
This pattern is working nicely in my content pages but not working in a content view. What am I doing wrong here?
The content view doesn't have the lifecycle methods like the content page. So when the content view shows or displays on the screen, the OnAppearing() and OnDisAppearing method developer custom will not invoke.
So you can call the the page's OnAppearing() method to do that if there is only a content view in your page. And if there is not only one contentview, you can call the _vm.Init(); method when you use the instance of the content view.
Here's what I've done and it seems to be working fine.
First, I created a ContentView to display the flyout header which includes user's avatar and name. Notice that I set the view model for this content view in the XAML file -- see below:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.Views.FlyoutHeader">
<ContentView.BindingContext>
<vm:AppViewModel/>
</ContentView.BindingContext>
<ContentView.Content>
<StackLayout
BackgroundColor="{StaticResource PrimaryDark }"
HeightRequest="200">
<xct:AvatarView
Source="{Binding UserInfo.AvatarUrl}"
Size="100"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"/>
<Label
Text="{Binding UserInfo.FullName}"
TextColor="White"
FontSize="Medium"
FontAttributes="Bold"
HorizontalOptions="CenterAndExpand"
Margin="0,0,0,30"/>
</StackLayout>
</ContentView.Content>
</ContentView>
I then created a view model named AppViewModel that I intend to use in multiple places, including the FlyoutHeader.xaml that I shared above. Here's what AppViewModel looks like:
public class AppViewModel : BaseViewModel
{
User user { get; set; }
public AppViewModel()
{
}
public User UserInfo
{
get => user;
set
{
if (user == value)
return;
user = value;
OnPropertyChanged();
}
}
public async void Init()
{
if(user == null || user.Id == Guid.Empty)
{
var data = await _dbService.GetUser();
if(data != null)
{
UserInfo = data;
OnPropertyChanged();
}
}
}
}
Finally, in the code behind for FlyoutHeader.xaml.cs, I call the Init() method of the view model in the constructor:
public partial class FlyoutHeader : ContentView
{
AppViewModel _vm;
public FlyoutHeader()
{
InitializeComponent();
_vm = new AppViewModel();
_vm.Init();
BindingContext = _vm;
}
}
I'm actually a bit concerned that there maybe tight coupling with the UI and the async call being initiated in the constructor may tie up the UI thread and delay it. Please let me know if there's a better way to handle this.
I have been unable to get a simple test working to display a title in the Navigation bar of my Xamarin forms app. I am providing my code below, basically when I set the title on the content page and wrap the content page in a navigation page, the navigation bar is not displaying the corresponding title from the content page.
App.cs
public partial class App : Application
{
public App()
{
InitializeComponent();
Current.MainPage = new NavigationPage(new Presentation.Views.SettingsDeviceUnitView());
}
}
ContentPage.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SettingsDeviceUnitView : ContentPage
{
public SettingsDeviceUnitView()
{
InitializeComponent();
}
}
ContentPage.xaml
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls="clr-namespace:GFIApp.Presentation.Controls"
mc:Ignorable="d"
x:Class="TestApp.Presentation.Views.SettingsDeviceUnitView"
BackgroundColor="{StaticResource Key=ColorPrimary}"
Title="TestTitle">
<ContentPage.Content>
<Grid>
</Grid>
</ContentPage.Content>
</ContentPage>
Yet I see nothing in the navigation bar
Has anyone seen this behavior before or have any suggestions? Thank you!
Per FreakyAli's response I tested setting the navigation bar text color and I am now able to see the title. Thank you for the suggestion FreakyAli!
For reference, I added the following in my App.cs
NavigationPage nav = new NavigationPage(new Presentation.Views.SettingsDeviceUnitView());
nav.BarTextColor = Color.White;
Current.MainPage = nav;
I'm using the Partial Views feature of Prism 7.1 in Xamarin in which the ContentView can have its own ViewModel. The binding to the viewmodel is working fine. However, I would also like to set a BindableProperty. For example, I would like to set a Title property on the ContentView. If the ContentView does not have its own ViewModel the Binding works fine. If it does have its own ViewModel the binding never occurs.
MainPage.xaml
<controls:CustomContentView Title="My Custom View Title"
mvvm:ViewModelLocator.AutowirePartialView="{x:Reference self}"/>
CustomContentView.cs:
public static readonly BindableProperty TitleProperty =
BindableProperty.Create(
nameof(Title),
typeof(string),
typeof(CustomContentView));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
CustomContentView.xaml:
<ContentView.Content>
<StackLayout>
<Label Text="{Title}" />
</StackLayout>
</ContentView.Content>
If I set a breakpoint on the Title's set method, it never gets hit and the Title in the Label control is never bound.
By using a Partial View your Binding Context is the ViewModel for the View not the View itself...
By setting the Label's text in the CustomContentView like:
<StackLayout>
<Label Text="{Binding Title}" />
</StackLayout>
This would expect to bind to something like:
public class CustomContentViewModel : BindableBase
{
private string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
}
Since you're instead expecting the binding to pull from the code behind, the XAML would need to be something like:
<ContentView x:Class="AwesomeApp.Views.CustomContentView"
x:Name="self">
<StackLayout>
<Label Text="{Binding Title,Source={x:Reference self}}" />
</StackLayout>
</ContentView>
I have this template that's simplified for the question:
<?xml version="1.0" encoding="UTF-8"?>
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:t="clr-namespace:Japanese.Templates"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.RoundButtonText" x:Name="this">
<Label Text="ABC" />
</Frame>
and this C#
using Xamarin.Forms;
namespace Japanese.Templates
{
public partial class RoundButtonText : BaseFrameButtonTemplate
{
public RoundButtonText()
{
InitializeComponent();
?? = Color.Red;
}
}
}
Can someone help me by telling me how I can change the TextColor of the label in the XAML inside the constructor of the back-end C#. Note that there's much more to this but I'm simplifying what I need for the question as if I know this then I can do the rest.
Specify x:Name="MyLabel" on the Label you want to access.
Then you can access that Label in your back-end C# file like so:
public RoundButtonText()
{
InitializeComponent();
MyLabel.TextColor = Color.Red; //OR
MyLabel.BackgroundColor = Color.Red;
}
That allows you to be able access any public property on the Label
I am creating a ListView whose items I want to directly control with code. I have the following xaml that declares a generic ListView.
<?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:ViewCellClick"
x:Class="ViewCellClick.MainPage">
<ListView x:Name="___listview" HasUnevenRows="True">
<ListView.ItemTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
and then in the code behind, I set up the ItemsSource property.
namespace ViewCellClick
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
___listview.ItemsSource = Repository.GetList();
___listview.ItemTemplate = new DataTemplate(typeof(CustomViewCell));
}
}
and this leverages a custom template defined as...
public class CustomViewCell : ViewCell
{
public CustomViewCell()
{
var stack = new StackLayout();
stack.Children.Add(new Label() { Text = "Label" });
// HOW DO I ACCESS THE MODEL HERE SO I CAN DRAW CUSTOM UI DEPENDING ON THE MODEL INSTANCE?
View = stack;
}
}
}
at this level, I have a very generic custom view cell as it only creates a label. However, say the model is complicated, and depending on the data in the model I would draw a different UI for that ViewCell instance.
How do I get access to the model for each CustomViewCell and then draw the UI according to that model?
I tried BindingContext, but it's null in the CustomViewCell constructor.
The BindingContext of thew ViewCell will have a reference to the current item in the list. You will need to cast it to the appropriate type in order to use it.