I am trying to follow this tutorial. https://www.youtube.com/watch?v=mBlzs5owIEY
I am able to populate a listview with some data, the listview is displaying the correct data when i'm running the emulator.
Could you please explain what is the meaning of the error below?
I want to understand what is causing it, and how do I resolve the issue?
Error
Severity Code Description Project File Line Suppression State
Error 'local' is an undeclared prefix. Line 7, position 10. C:\Workspaces\HelloWorld\App1\App1\App1\XAML\ListView.xaml 7
App.xaml
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace App1
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new ListView();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
}
Debtviewmodel.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace App1
{
class DebtViewModel
{
public string[] Debt { get; set; }
public DebtViewModel()
{
string[] debt = new string[] { "1", "2", "3" };
Debt = debt;
}
}
}
ListView.xaml
<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:local="clr-namespace:App1"
mc:Ignorable="d"
x:Class="App1.ListView">
<!-- Required to map viewmodel -->
<ContentPage.BindingContext>
<local:DebtViewModel />
</ContentPage.BindingContext>
<!-- Bind variable in view model to listview itemsource -->
<ContentPage.Content>
<StackLayout>
<ListView x:Name="lvDebt" ItemsSource="{Binding Debt}">
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Related
I've got a very basic view.
<ContentPage x:Class="ThetaRex.InvestmentManager.Merlin.Views.ScenarioSelectionPage"
Title="{Binding Title}"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns="http://xamarin.com/schemas/2014/forms">
<StackLayout>
<ListView ItemsSource="{Binding Items}"/>
</StackLayout>
<ContentPage/>
The code behind is also very simple:
namespace ThetaRex.InvestmentManager.Merlin.Views
{
using System.ComponentModel;
using ThetaRex.InvestmentManager.Merlin.ViewModels;
using Xamarin.Forms;
public partial class ScenarioSelectionPage : ContentPage
{
public ScenarioSelectionPage()
{
InitializeComponent();
this.BindingContext = this.ViewModel = new ScenarioSelectionViewModel();
}
public ScenarioSelectionViewModel ViewModel { get; set; }
protected override void OnAppearing()
{
base.OnAppearing();
ViewModel.LoadItemsCommand.Execute(null);
}
}
}
Coming from a pure MVVM environment in WPF and UWP, I want to bind the view to the viewmodel in XAML, not using the this.Binding = ViewModel in the code behind. I've tried:
<ContentPage x:Class="ThetaRex.InvestmentManager.Merlin.Views.ScenarioSelectionPage"
xmlns:controls="clr-namespace:ThetaRex.InvestmentManager.Merlin.Controls"
BindingContext="{Binding ViewModel}"
Title="{Binding Title}"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns="http://xamarin.com/schemas/2014/forms">
But it didn't work. How do I bind to the ViewModel from XAML?
Note: I know that I can create a view model from scratch in XAML, but it doesn't use the same instance that the code behind in the view uses, so that's not an option.
If I understood what you want, the solution is build a ViewModelLocator like this:
ViewModelLocalizator Class
public static class ViewModelLocalizator
{
public static readonly BindableProperty AutoWireViewModelProperty =
BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocalizator), default(bool), propertyChanged: OnAutoWireViewModelChanged);
public static bool GetAutoWireViewModel(BindableObject bindable)
{
return (bool)bindable.GetValue(AutoWireViewModelProperty);
}
public static void SetAutoWireViewModel(BindableObject bindable, bool value)
{
bindable.SetValue(AutoWireViewModelProperty, value);
}
/// <summary>
/// VERIFY THE VIEW NAME AND ASSOCIATE IT WITH THE VIEW MODEL OF THE SAME NAME. REPLACING THE 'View' suffix WITH THE 'ViewModel'
/// </summary>
private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
{
if (!(bindable is Element view))
{
return;
}
var viewType = view.GetType();
var viewModelName = viewType.FullName.Replace(".Views.", ".ViewModels.").Replace("Page", "ViewModel");
var viewModelType = Type.GetType(viewModelName);
if (viewModelType == null) { return; }
var vmInstance = Activator.CreateInstance(viewModelType);
if (vmInstance != null)
{
view.BindingContext = vmInstance;
}
}
}
Using It on your View
<ContentPage x:Class="YourProject.Views.YourTestPage"
Title="{Binding Title}"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:viewModelBase="clr-namespace:YourProject.ViewModels;assembly=YouProject"
viewModelBase:ViewModelLocalizator.AutoWireViewModel="true"
>
<StackLayout>
<ListView ItemsSource="{Binding Items}"/>
</StackLayout>
<ContentPage/>
I'm trying to do some basic data binding from my XAML view to my view model in my Xamarin Forms (4.2) app. I navigate to the PhotoUploadPage from a different page.
PhotoUploadPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="AgentConnectMobile.Views.PhotoUploadPage"
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"
Title="Upload Photo"
mc:Ignorable="d">
<ContentPage.Content>
<!--Upload.Id below isn't showing -->
<Label Text="{Binding Upload.Id}" />
</ContentPage.Content>
</ContentPage>
PhotoUploadPage.xaml.cs
public partial class PhotoUploadPage : ContentPage
{
PhotoUploadViewModel viewModel;
public PhotoUploadPage(PhotoUploadViewModel viewModel)
{
this.viewModel = viewModel;
BindingContext = this.viewModel;
InitializeComponent();
}
}
PhotoUploadViewModel.cs
public class PhotoUploadViewModel : BaseViewModel
{
public Upload Upload { get; set; }
public PhotoUploadViewModel(Upload upload = null)
{
Upload = upload;
}
}
Upload.cs
public class Upload
{
public string Id;
}
Navigate to PhotoUploadPage
var upload = new Upload
{
Id = "abc123",
};
await Navigation.PushAsync(new PhotoUploadPage(new PhotoUploadViewModel(upload)));
I put breakpoints in PhotoUploadPage.xaml.cs and the BindingContext is getting set and I can see Upload on it with my Id value, but the value never appears in the Label text. I have also switched the order of InitializeComponent() and setting the BindingContext but that didn't solve anything either.
What am I doing wrong? I believe I'm doing this other places and it is working just fine...
When trying to create a simple Xamarin app, on debugging I will immediately get a dialog box quoting:
Object Reference not set to an instance of an object
Nothing appears in my error list.
Can anyone give me tips on where I am going wrong?
Many thanks,
Gary
MainPage.xaml
<xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamarinApp1"
x:Class="XamarinApp1.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Click Me" Clicked="Button_Clicked" />
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XamarinApp1
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
int count = 0;
private void Button_Clicked(object sender, EventArgs e)
{
count++;
((Button)sender).Text = $"You clicked {count} times.";
}
}
app.xaml.cs
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace XamarinApp1
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
I try to create something like that:
<Label Text="{Binding oResult.hi, StringFormat='Hallo: {0}'}" />
And it works fine! But i wish that the String "Hallo" should get out from the resx file.
Like this:
<Entry Placeholder="{i18n:TranslateExtension Text=password}" IsPassword="true" />
Also i will do a combination of both.
Thank you!
You could achieve this with a simple markup extension and strings in resx files.
Xaml extension:
using System;
using Xamarin.Forms.Xaml;
using Xamarin.Forms;
using System.Resources;
namespace i18n
{
[ContentProperty("Key")]
public class TranslateExtension : IMarkupExtension
{
public string Key { get; set; }
static ResourceManager ResourceManagerInstance;
#region IMarkupExtension implementation
public static void Init(ResourceManager r){
ResourceManagerInstance = r;
}
public object ProvideValue(IServiceProvider serviceProvider)
{
if (ResourceManagerInstance == null)
{
throw new InvalidOperationException("Call TranslateExtension.Init(ResourceManager) in your App.cs");
}
return ResourceManagerInstance.GetString(this.Key);
}
#endregion
}
}
Example App.cs:
using Xamarin.Forms;
using i18n;
namespace ResourceLocalizationMarkup
{
public class App : Application
{
public App()
{
TranslateExtension.Init(Localization.Strings.ResourceManager);
MainPage = new NavigationPage(new MyPage());
}
}
}
Example 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:i18n="clr-namespace:i18n"
x:Class="ResourceLocalizationMarkup.MyPage">
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<Label Text="{i18n:TranslateExtension hello_world}"/>
<Label Text="{Binding Name, StringFormat={i18n:TranslateExtension thanks_user}}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
I'm currently working on WPF App with Prism 6...I have ShellViewModel, ViewAViewModel and ViewBViewModel.
Inside Shell.xaml, I have "mainRegion" defined. When app is started, I show ViewA in that Region by default.
Now, When I go to from ViewA to ViewB, at this point(Inside ViewBViewModel), I need to have context of ShellViewModel.
Any suggestion to achieve this?
the full source code!
ViewA.xaml
<UserControl x:Class="ModuleA.Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
<TextBlock Text="{Binding Title}" FontSize="38" />
<Button Command="{Binding UpdateCommand}" Width="100">Update</Button>
</StackPanel>
</Grid>
</UserControl>
ViewA.xaml.cs
using ModuleA.RibbonTabs;
using PrismDemo.Core;
using System.Windows.Controls;
namespace ModuleA.Views
{
[RibbonTab(typeof(ViewATab))]
public partial class ViewA : UserControl, ISupportDataContext
{
public ViewA()
{
InitializeComponent();
}
}
}
ViewB.xaml
<UserControl x:Class="ModuleA.Views.ViewB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
<TextBlock Text="{Binding Title}" FontSize="38" />
<Button Command="{Binding UpdateCommand}" Width="100">Update</Button>
</StackPanel>
</Grid>
</UserControl>
ViewB.xaml.cs
using ModuleA.RibbonTabs;
using PrismDemo.Core;
using System.Windows.Controls;
namespace ModuleA.Views
{
[RibbonTab(typeof(ViewBTab))]
//the main view can inject any number of tab
//uncomment the following lines and test
//I added the same tab just for demo purposes
//[RibbonTab(typeof(ViewBTab))]
//[RibbonTab(typeof(ViewBTab))]
//[RibbonTab(typeof(ViewBTab))]
public partial class ViewB : UserControl, ISupportDataContext
{
public ViewB()
{
InitializeComponent();
}
}
}
ModuleAModule.cs
using Microsoft.Practices.Unity;
using ModuleA.Views;
using Prism.Modularity;
using Prism.Unity;
namespace ModuleA
{
public class ModuleAModule : IModule
{
IUnityContainer _container;
public ModuleAModule(IUnityContainer container)
{
_container = container;
}
public void Initialize()
{
//register for nav
_container.RegisterTypeForNavigation<ViewA>();
_container.RegisterTypeForNavigation<ViewB>();
}
}
}
RibbonTabAttribute.cs
using System;
namespace PrismDemo.Core
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class RibbonTabAttribute : Attribute
{
public Type Type { get; private set; }
public RibbonTabAttribute(Type ribbonTabType)
{
Type = ribbonTabType;
}
}
}
ISupportDataContext.cs
namespace PrismDemo.Core
{
public interface ISupportDataContext
{
object DataContext { get; set; }
}
}
bootstrapper.cs
using Prism.Unity;
using PrismDemo.Views;
using System.Windows;
using Microsoft.Practices.Unity;
using Prism.Modularity;
using ModuleA;
using Prism.Regions;
using PrismDemo.Prism;
using System.Windows.Controls.Ribbon;
namespace PrismDemo
{
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureModuleCatalog()
{
var catalog = (ModuleCatalog)ModuleCatalog;
catalog.AddModule(typeof(ModuleAModule));
}
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
var behaviors = base.ConfigureDefaultRegionBehaviors();
behaviors.AddIfMissing(RibbonRegionBehavior.BehaviorKey, typeof(RibbonRegionBehavior));
return behaviors;
}
}
}
App.xaml
<Application x:Class="PrismDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PrismDemo">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs
using System.Windows;
namespace PrismDemo
{
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var bs = new Bootstrapper();
bs.Run();
}
}
}
Shell.xaml
<Window x:Class="PrismDemo.Views.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="Shell" Height="720" Width="1280">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Ribbon Grid.Row="0" prism:RegionManager.RegionName="RibbonTabRegion"/>
<DockPanel LastChildFill="True" Grid.Row="1">
<StackPanel>
<Button Content="Navigate ViewA" Command="{Binding NavigateCommand}" CommandParameter="ViewA" />
<Button Content="Navigate ViewB" Command="{Binding NavigateCommand}" CommandParameter="ViewB" />
</StackPanel>
<ContentControl prism:RegionManager.RegionName="ContentRegion" Margin="1,3,3,3" />
</DockPanel>
</Grid>
</Window>
Shell.xaml.cs
namespace PrismDemo.Views
{
public partial class Shell
{
public Shell()
{
InitializeComponent();
}
}
}
ShellViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
namespace PrismDemo.ViewModels
{
public class ShellViewModel : BindableBase
{
IRegionManager _regionManager;
public DelegateCommand<string> NavigateCommand { get; set; }
public ShellViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
NavigateCommand = new DelegateCommand<string>(Navigate);
}
void Navigate(string navigationPath)
{
_regionManager.RequestNavigate("ContentRegion", navigationPath);
}
}
}
RibbonRegionBehavior.cs
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Specialized;
using PrismDemo.Core;
using System.Windows.Controls.Ribbon;
namespace PrismDemo.Prism
{
public class RibbonRegionBehavior : RegionBehavior
{
public const string BehaviorKey = "RibbonRegionBehavior";
public const string RibbonTabRegionName = "RibbonTabRegion";
protected override void OnAttach()
{
if (Region.Name == "ContentRegion")
Region.ActiveViews.CollectionChanged += ActiveViews_CollectionChanged;
}
private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var tabList = new List<RibbonTab>();
foreach (var newView in e.NewItems)
{
foreach (var atr in GetCustomAttributes<RibbonTabAttribute>(newView.GetType()))
{
var ribbonTabItem = Activator.CreateInstance(atr.Type) as RibbonTab;
if (ribbonTabItem is ISupportDataContext && newView is ISupportDataContext)
((ISupportDataContext)ribbonTabItem).DataContext = ((ISupportDataContext)newView).DataContext;
tabList.Add(ribbonTabItem);
}
tabList.ForEach(x => Region.RegionManager.Regions[RibbonTabRegionName].Add(x));
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
var views = Region.RegionManager.Regions[RibbonTabRegionName].Views.ToList();
views.ForEach(x => Region.RegionManager.Regions[RibbonTabRegionName].Remove(x));
}
}
private static IEnumerable<T> GetCustomAttributes<T>(Type type)
{
return type.GetCustomAttributes(typeof(T), true).OfType<T>();
}
}
}
and this is the structure of the demo app:
this solution was provided by Brian Lagunas (prism owner) in his Pluralsight course (Prism Problems & Solutions: Loading Dependent Views), ** **There is another solution for this problem provided (again) by brian, https://www.youtube.com/watch?v=xH6OgCxdXQc, but I think the first solution is the best and the simplest
the view injected in the content region can injects any number of tabs, see comment in ViewB.xaml.cs