xaml converter error in reactiveui - xaml

I get the following mysterious error when trying to use ReactiveUI's MVVM framework in a Windows Store app:
Error: Converter failed to convert value of type 'ReactiveUI.RoutingState, ReactiveUI,
Version=5.4.0.0, Culture=neutral, PublicKeyToken=null' to type 'IRoutingState';
BindingExpression: Path='Router' DataItem='MVVMTestWS.AppBootstrapper'; target element
is 'ReactiveUI.Xaml.RoutedViewHost' (Name='null'); target property is 'Router'
(type 'IRoutingState').
The Xaml looks like the following:
<Page
x:Class="MVVMTestWS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMTestWS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Xaml="using:ReactiveUI.Xaml"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Xaml:RoutedViewHost Router="{Binding Router}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
</Grid>
</Page>
The AppBootstrapper object looks like the following:
public class AppBootstrapper : ReactiveObject, IScreen
{
public IRoutingState Router { get; private set; }
public AppBootstrapper(IMutableDependencyResolver dependencyResolver = null, IRoutingState testRouter = null)
{
Router = testRouter ?? new RoutingState();
dependencyResolver = dependencyResolver ?? RxApp.MutableResolver;
// Bind
RegisterParts(dependencyResolver);
// TODO: This is a good place to set up any other app
// startup tasks, like setting the logging level
LogHost.Default.Level = LogLevel.Debug;
// Navigate to the opening page of the application
Router.Navigate.Execute(new HomePageVM(this));
}
The error mystifies me! Router is clearly declared IRoutingState, I've checked that it isn't null, and I've done my best to make sure there aren't two definitions of it floating around. There was some chatter on the web about WS apps having trouble binding to interfaces, but ICommand in a Button works just fine (I tested it in the same project).
This is a dirt simple WS app built for Win 8.1 using VS2013. I'm using the 5.4.0 release of ReactiveUI (if that makes a difference here). I am a newbie when it comes to writing WS apps. Any suggestions of what I've done wrong or what more I can do to help debug this would be most helpful!

Computers are weird. Just make it the concrete class RoutingState, the interface is probably going away in ReactiveUI 6 anyways

Related

How to bind entry from page with ViewModel

I'm trying to implement Login system like this:
public Command LoginCommand => new Command(async () =>
{
LoginModel model = new LoginModel("dnetTest", "dnetTest"); // (get value from entry instead of "dnetTest")
if (model.CheckInformation())
{
bool isSuccess = await LoginService.Login(model);
if (isSuccess)
{
await Application.Current.MainPage.DisplayAlert("Пријављивање", "Успешно сте се пријавили", "OK.");
Application.Current.MainPage = new MainPage();
}
}
My LogingPage also have:
<Label Text="Korisničko ime"/>
<Entry x:Name="Entry_Username" Placeholder="Korisničko ime"/>
<Label Text="Lozinka"/>
<Entry x:Name="Entry_Password" Placeholder="Lozinka"/>
<Button Text="Prijavi se" Command="{Binding LoginCommand}"/>
So, my question is how to bind Entry_Username and Entry_Password with LoginModel in LoginViewModel?
And is there any way to bind it without using x:Names?
To be fair; this is a very basic MVVM question that is easy to find, of which the solution is in multiple blogs and pages. But, let me help you get started.
And is there any way to bind it without using x:Names?
The whole point of data binding is that you do not have to make any hard references to these controls. We want to separate the logic from the UI so that we can easily replace one or both without having to touch the other. For instance, say that you want to implement a new design, if you use data binding, you can just show the current properties in the view model (or page model as it is referred to in the Xamarin space as well) if you reference those in your new UI.
If you would have all kind of x:Name references, you would not only have to touch your UI, but also the view model and go through all the references to those fields and replace them as well.
Using data binding promotes reusability and testability mostly.
As for your specific case. I can't see your full code, so there will be some assumptions made here. First, I'm going to assume that your LoginCommand lives in a view model of its own right now. You are already using data binding there, which is good. I don't directly see why you would need a separate model for the view model and the login, possibly your LoginModel is more like a service. Also, I'm assuming you're doing this manually, without the help of an MVVM framework. It's good to know what happens under the hood, but I would recommend looking at using an MVVM framework like FreshMvvm or Prism for example.
The login page that holds your XAML, I will call LoginPage.xaml which should have a LoginPage.xaml.cs code-behind file. In there, go into the constructor and specify this line:
public LoginPage()
{
InitializeComponents();
// This line is relevant here
BindingContext = new LoginViewModel();
}
Seeing that your LoginCommand is already using data binding, this is probably here already.
Now, in your LoginPage.xaml, change your XAML to this:
<Label Text="Korisničko ime"/>
<Entry Text="{Binding Username}" Placeholder="Korisničko ime"/>
<Label Text="Lozinka"/>
<Entry Text="{Binding Password}" Placeholder="Lozinka"/>
<Button Text="Prijavi se" Command="{Binding LoginCommand}"/>
Notice how I removed the x:Name attributes and added the Text attributes on the two Entry controls.
Next, go into your LoginViewModel.cs and add two properties, like this:
public string Username { get; set; }
public string Password { get; set; }
Whenever the text changes in your Entry controls, these properties should contain the value accordingly. Now, you can change the code you posted to something like this:
public Command LoginCommand => new Command(async () =>
{
// Notice how I changed this line
LoginModel model = new LoginModel(Username, Password);
if (model.CheckInformation())
{
bool isSuccess = await LoginService.Login(model);
if (isSuccess)
{
await Application.Current.MainPage.DisplayAlert("Пријављивање", "Успешно сте се пријавили", "OK.");
Application.Current.MainPage = new MainPage();
}
}
This should work for you!
As mentioned, I would recommend looking further into MVVM as a whole and also MVVM frameworks. Here is the official Docs page, a good writeup by Adam Pedley and something I wrote myself a while back.

Binding Win10 MapControl while specifying MapAnimationKind

I'm using the MapControl for Windows 10 and binding it's Center property to my view model like so:
<Maps:MapControl x:Name="MapControl" Center="{Binding MapCenter, Mode=TwoWay}" />
This works, but I'd like to use a parabolic animation when transitioning the map from one center position to the next when my view model changes. I'd like it to behave the way it would if I had called TrySetViewAsync from my code behind passing a MapAnimationKind:
MapControl.TrySetViewAsync(geopoint, 13, null, null, Maps.MapAnimationKind.Bow)
How can I achieve this effect while maintaining separation of concerns?
With the help of MVVMLight Messenger, we can trigger it from the ViewModel easily.
About how to use MVVM Light in Windows 10 Universal App, see my answer in this one.
For example, the following method is placed in MainPage.xaml.cs:
private async void SetMapView(Geopoint point)
{
await MainMap.TrySetViewAsync(point, 13,0,0,Windows.UI.Xaml.Controls.Maps.MapAnimationKind.Bow);
}
Register the MainPage.xaml.cs to the default messenger provided by MVVMLight
Messenger.Default.Register<Geopoint>(this, Constants.SetMapViewToken, SetMapView);
Used a string Constants.SetMapViewToken as a token to identify the message and assigned SetMapView as an action
internal class Constants
{
public static string SetMapViewToken = "SetMapView";
}
Trigger SetMapView method in ViewModel:
Messenger.Default.Send<Geopoint>(MyLocation.Coordinate.Point, Constants.SetMapViewToken);
I've created a completed sample and uploaded to Github.
Enjoy it:)

Xamarin Forms Dynamically Load Content in a Page

My current set up:
Xamarin Forms, consisting of iOS, Android, WP app and shared PCL.
Using MVVM Light to keep a nice separation of concerns.
Brief intro into what I want to achieve. I want to have a Base page that has a Cancel and Next button. On pressing the Next button Content is loaded dynamically within that base page.
Xaml View:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LogInPresenterView">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Cancel" Priority="0" Order="Primary" Command="{Binding Cancel}"></ToolbarItem>
<ToolbarItem Text="Next" Priority="1" Order="Primary" Command="{Binding Next}"></ToolbarItem>
</ContentPage.ToolbarItems>
</ContentPage>
ViewModel Code:
public class LogInPresenterViewModel : ViewModelBase
{
public LogInPresenterViewModel() {}
private RelayCommand _next;
public RelayCommand Next
{
get
{
return _next ?? (_next = new RelayCommand(async () => await DoNext()));
}
}
private async Task DoNext()
{
// IN HERE I WOULD LIKE TO DYNCAMICALLY LOAD CONTENT / VIEWS
}
}
Usually you would have a StackLayout etc before the element. However, on clicking the Next Toolbar Item I want to dynamically load content (that has a viewmodel).
So maybe my ICommand for my next button checked to see what the current content type was, and depending on that I would load another bit of content.
The scenario would be, the base page would load along with the first bit of content - Enter Email and Password. User enters that then clicks on next, if all ok, the content is replaced with the option to enter a security code, keeping the base Close and Next buttons at the top.
Hopefully this makes sense. I know what I want to do in my head, I just don't know how to translate that into Xamarin Forms...
Ok,
So first job is to create your region service in your PCL. This will look something like this:
using System;
using System.Collections.Generic;
namespace xxx
{
public class RegionService : IRegionService
{
private Dictionary<string, object> _regionDictionary;
public RegionService ()
{
_regionDictionary = new Dictionary<string, object> ();
}
#region IRegionService implementation
public bool RegisterRegion (string regionName, object regionObject)
{
object region = null;
_regionDictionary.TryGetValue (regionName, out region);
if (region != null)
_regionDictionary [regionName] = regionObject;
else
_regionDictionary.Add (regionName, regionObject);
return true;
}
public object ResolveRegion (string regionName)
{
object region = null;
_regionDictionary.TryGetValue (regionName, out region);
if (region == null)
throw new RegionServiceException ("Unable to resolve region with given name");
return region;
}
#endregion
}
}
This when you create your page with the dynamic content register your dynamic contentview in your code behind:
ContentView contentView = this.FindById<ContentView>("myContentView");
regionService.RegisterRegion("DynamicView", contentView);
You'll need to create an interface for your views and pages to use to indicate which region they wish to be presented in:
using System;
namespace xxx
{
public interface IRegionView
{
string GetRegionName ();
}
}
Then in your code behind for your view implement this interface to return the name of the region to display in.
You now need a custom presenter to use this region code. I use MVVMCross, so the details will vary for the MVVM implementation you are using, but essentially something like this is what you need:
public async static Task PresentPage(Page page)
{
if (typeof(IRegionView).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
{
IRegionService regionService = Mvx.Resolve<IRegionService>();
string regionName = (page as IRegionView).GetRegionName();
Page region = regionService.ResolveRegion(regionName) as Page;
if (typeof(IModalPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
await region.Navigation.PushModalAsync(page);
else if (typeof(IPopupPage).GetTypeInfo().IsAssignableFrom(page.GetType().GetTypeInfo()))
region.PushOverlayPage(page);
else if (typeof(NavigationPage).GetTypeInfo().IsAssignableFrom(region.GetType().GetTypeInfo()))
await (region as NavigationPage).PushAsync(page);
}
}
I hope this is useful for you :)
So if this was me. I would create a region service where the contentview registers a unique region name.
Content would then be marked to use that region, and a custom presenter can be used to show the view model's content in the appropriate region.
I'm on my phone whilst travelling at the moment but I can post some code later on if that helps :)
Tristan
You can dynamically load Xamarin Forms UI with XAML.
Old Answer:
This can be achieved with the use of the LoadFromXaml method. It works in the same was as XamlReader.Load in Silverlight/WPF. It is a hidden method that can be only accessed through reflection. There is an article on how to do it here:
http://www.cazzulino.com/dynamic-forms.html
But, I would like to ask to you go to this feature request at Xamarin and ask that the method be made public so that it becomes a fully supported feature:
https://forums.xamarin.com/discussion/comment/252626

XamlParseException when using Graph# libraries

I'm trying to use the Graph# libraries in my VSPackage project, but unfortunately there are some obstacles to conquer. Here is what I did:
I copied all the following DLL's to a folder /Libraries in the project root:
GraphSharp.dll
GraphSharp.Controls.dll
QuickGraph.dll
WPFExtensions.dll
The Build Action of all is 'Content' and the option Copy to Output is set to 'Do not copy'.
I added those references to my project. (Add Reference... -> Browse -> select them from the /Library folder)
After that, I created the following files. You can see that the ViewModel is set to be the DataContext of the UserControl and that it defines a "MethodGraph" to which the UI binds.
XAML file
<UserControl x:Class="Biocoder.InteractiveExploration.View.ExplorationControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:graphsharp="clr-namespace:GraphSharp.Controls;assembly=GraphSharp.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Grid.Row="0" ItemsSource="{Binding SelectedMethods}">
<ListView.View>
<GridView>
<GridViewColumn Header="Method" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="ReturnType" DisplayMemberBinding="{Binding ReflectionInfo.ReturnType}"/>
<GridViewColumn Header="Incoming Calls"/>
<GridViewColumn Header="Outgoing Calls"/>
</GridView>
</ListView.View>
</ListView>
<graphsharp:GraphLayout Graph="{Binding MethodGraph}"/>
</Grid>
</UserControl>
Code-behind
public partial class ExplorationControl : UserControl
{
public ExplorationControl()
{
InitializeComponent();
// set the datacontext
DataContext = InteractiveExplorationPackage.ExplorationToolViewModel;
}
}
ViewModel
public class ExplorationToolViewModel : ViewModelBase
{
private IBidirectionalGraph<object, IEdge<object>> _methodGraph;
public IBidirectionalGraph<object, IEdge<object>> MethodGraph
{
get { return _methodGraph; }
set
{
if (value != _methodGraph)
{
_methodGraph = value;
NotifyPropertyChanged("MethodGraph");
}
}
}
public ExplorationToolViewModel()
{
InitializeViewModel();
}
private void InitializeViewModel()
{
SelectedMethods = new ObservableCollection<Method>();
CreateGraph();
}
private void CreateGraph()
{
var g = new BidirectionalGraph<object, IEdge<object>>();
// add vertices
string[] vertices = new string[5];
for (int i = 0; i < 5; i++)
{
vertices[i] = i.ToString();
g.AddVertex(vertices[i]);
}
// add edges
g.AddEdge(new Edge<object>(vertices[0], vertices[1]));
g.AddEdge(new Edge<object>(vertices[1], vertices[2]));
g.AddEdge(new Edge<object>(vertices[2], vertices[3]));
g.AddEdge(new Edge<object>(vertices[3], vertices[1]));
g.AddEdge(new Edge<object>(vertices[1], vertices[4]));
MethodGraph = g;
}
}
Fortunately, I can compile the whole project but at runtime the following error in the XAML occurs on the tag (right above the needed tag):
Could not load file or assembly 'GraphSharp.Controls, PublicKeyToken=null' or one of its dependencies. The system cannot find the file.
But I referenced the assemblies, they are listed in the references list.
What's the problem? Could it be that assemblies have to be referenced another way round than usual when writing a Visual Studio Package (plugin)?
EDIT: I just tried to get it work in another project, so I just set up a normal WPF Application and did all the above. In this solution everything works properly! That's so strange!
Hope you can help me :-)
Best regards!
Are you able to use nuget in your project? If so, instead copying the dll-s and references manually you should try run install-package graphsharp command.
I think that's some kind of DLL strong name problem.
Have you tried this one? http://shfb.codeplex.com/workitem/32819
It says:
I had such an issue and it is discussed somewhere in the discussions. What you need to do is to open the project properties > Paths and there delete all paths (just leave them empty)
I managed to get everything working now. Thanks for your help anyway!
The problem with VSPackages at runtime is basically that they load third party assemblies from another location on the computer called the Private Assembly folder. (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies) So all you have to do is to copy the referenced assemblies into this folder (requires admin rights). In order to be able to use the assemblies when developing just add those references to your project as you do normally and the compiler will use them the usual way. The only difference is at runtime since Visual Studio loads those references from the Private Assemblies folder.
More information about this can be found here: Managed VSPackage File Location Keys
Other solution:
Add your library as an Assembly Asset to your .vsixmanifest file:
Add this code to your package initialization code.
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
AppDomain.CurrentDomain.AssemblyResolve += LoadFromSameFolder;
static Assembly? LoadFromSameFolder(object sender, ResolveEventArgs args)
{
var executingAssemblyPath = Assembly.GetExecutingAssembly().Location;
var folderPath = Path.GetDirectoryName(executingAssemblyPath) ?? string.Empty;
var assemblyPath = Path.Combine(folderPath, $"{new AssemblyName(args.Name).Name}.dll");
return File.Exists(assemblyPath)
? Assembly.LoadFrom(assemblyPath)
: null;
}
// your code
}

Assign the content of xaml file to App.ViewModel property?

I defined my complete viewmodel using XAML:
<local:TestViewModel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:icColors"
SampleProperty="Sample Text Property Value">
<local:TestViewModel.Questions>
....
</local:TestViewModel.Questions>
</local:TestViewModel>
How can parse this XAML at runtime and set as a property of my application, App.TestViewModel?
You can parse XAML at runtime using the XAMLReader class. Simply parse your XAML using the XamlReader.Load method, then assign it (remembering to cast the result). Here is some example code:
System.Windows.Resources.StreamResourceInfo streamInfo = System.Windows.Application.GetResourceStream(uri);
if ((streamInfo != null) && (streamInfo.Stream != null))
{
using (System.IO.StreamReader reader = new System.IO.StreamReader(streamInfo.Stream))
{
TestViewModel vm = System.Windows.Markup.XamlReader.Load(reader.ReadToEnd()) as TestViewModel;
}
}