Exception ContentControl is not registered in the region adapter mappings - prism

I have a region name attached it a ContentControl via a static string binding in the Shell,xaml file as shown below:
<ContentControl prism:RegionManager.RegionName="{x:Static infrastructure:RegionNames.ContentRegion}"
Margin="1,3,3,3" />
The static string is defined as shown:
namespace IGOutlook.Infrastructure
{
public static class RegionNames
{
public const string RibbonTabRegion = "RibbonTabRegion";
public const string OutlookBarGroupRegion = "OutlookBarGroupRegion";
public const string ContentRegion = "ContentRegion";
}
}
When I run the application and Exception is thrown complaining about the ContentControl not being registered.
Any reason you can think of while this occurrs?
I have two custom controls also in the Shell.xaml file and they use the two remaining static strings in the RegionNames class
to bind to the RegionName and no exception occurs on either of the two controls:
<dxn:NavBarControl prism:RegionManager.RegionName="{x:Static infrastructure:RegionNames.OutlookBarGroupRegion}"
DockPanel.Dock="Left"
Width="200"
/>
<dxr:RibbonControl
RibbonStyle="Office2010"
DockPanel.Dock="Top"
prism:RegionManager.RegionName="{x:Static infrastructure:RegionNames.RibbonTabRegion}">
I am using Devexpress controls for the custom controls.
-ContentControl Exception Not Registered In Region Adapter
'ContentRegion'. The exception was: System.Collections.Generic.KeyNotFoundException:
The IRegionAdapter for the type System.Windows.Controls.ContentControl is not registered in the region adapter mappings.
You can register an IRegionAdapter for this control by overriding the ConfigureRegionAdapterMappings method in the bootstrapper.
at Prism.Regions.RegionAdapterMappings.GetMapping(Type controlType) in C:\Prism-7.2.0.1422\Prism-7.2.0.1422\Source\Wpf\Prism.Wpf\Regions\RegionAdapterMappings.cs:line 63
at Prism.Regions.Behaviors.DelayedRegionCreationBehavior.CreateRegion(DependencyObject targetElement, String regionName) in C:\Prism-7.2.0.1422\Prism-7.2.0.1422\Source\Wpf\Prism.Wpf\Regions\Behaviors\DelayedRegionCreationBehavior.cs:line 127.
Below is the name portions of the Shell.xaml code
<dxr:DXRibbonWindow Title="Shell" Height="300" Width="500">
<Grid>
<dxb:BarManager Name="barManager1">
<dxb:BarManager.Items>
<dxb:BarButtonItem x:Name="barButtonItem1"
Content="BarButtonItem"
Glyph="{dxc:DXImage 'Images/Actions/Apply_16x16.png'}"
LargeGlyph="{dxc:DXImage'Images/Actions/Apply_32x32.png'}"/>
</dxb:BarManager.Items>
<DockPanel>
<dxr:RibbonControl
RibbonStyle="Office2010"
DockPanel.Dock="Top"
prism:RegionManager.RegionName="{x:Static
infrastructure:RegionNames.RibbonTabRegion}">
.....
</dxr:RibbonControl>
<DockPanel LastChildFill="True">
<dxn:NavBarControl prism:RegionManager.RegionName="
{x:Static infrastructure:RegionNames.OutlookBarGroupRegion}"
DockPanel.Dock="Left"
Width="200"
/>
<ContentControl
prism:RegionManager.RegionName="{x:Static
infrastructure:RegionNames.ContentRegion}"
Margin="1,3,3,3" />
</DockPanel>
</DockPanel>
</dxb:BarManager>
</Grid>
</dxr:DXRibbonWindow>

In my case, the cause was that I tried to set up containerRegistry based on convention over configuration and accidentally registered a class that exists in a Prism assembly.

Related

Xamarin.Forms setting Image Source in shared project

I have a Xamarin.Forms solution with Android and iOS projects.
I want the same images to be in both applications.
I know I can add the same image to both projects code seperately, but that doesn't sound very elegant.
I want to have only one image referred to by both projects, from the code in the shared project.
My images are in the shared project, and are Embedded Resource
SharedProject
|
- Resources
|
- icon192.png
My XAML (shared project)
<ContentPage xmlns:myExtensions="clr-namespace:LibraryApp.Extensions;assembly=App">
<Image x:Name="image2"
VerticalOptions="Center" HeightRequest="50" Aspect="AspectFit" />
<Image Source="{myExtensions:ImageResourceExtension Resources.icon192.png}"
VerticalOptions="Center" HeightRequest="50" Aspect="AspectFit" />
</ContentPage>
My code-behind (shared project)
public partial class AboutPage : ContentPage
{
public AboutPage()
{
InitializeComponent();
image2.Source = ImageSource.FromResource("LibraryApp.AndroidClient.Resources.icon192.png");
}
}
The first Image, image2 works perfectly when set from code-behind.
But I don't want to have to deal with images in code-behind, because they are a presentation detail, not implementation.
So my extension method to try and solve this
namespace LibraryApp.Extensions
{
[Preserve(AllMembers = true)]
[ContentProperty(nameof(Source))]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null) return null;
// do some work to get the name of the correct assembly/project, here
// var stream = Assembly.GetCallingAssembly().GetManifestResourceStream(Source);
// just for testing, return something we know that works
return ImageSource.FromResource("LibraryApp.AndroidClient.Resources.icon192.png");
}
}
}
This extension method does get called from the other image (with the correct Source parameter).
Even though the code is effectively exactly the same, I cannot get an image using the extension method.
I get a FileImageSourceHandler: Could not find image or image file was invalid.
How would you solve this problem?
Typical, after spending literally a day on this, I figure it out 20m after asking the question...
One solution is to add an IValueConverter
public class ResourceConverter : IValueConverter
{
public Object Convert(Object value, Type targetType, Object parameter, System.Globalization.CultureInfo culture)
{
if (value is not String filename) throw new ArgumentNullException(nameof(value));
var nameOfAssembly = Assembly.GetExecutingAssembly().GetName().Name;
return ImageSource.FromResource($"{nameOfAssembly}.{filename}");
}
public Object ConvertBack(Object value, Type targetType, Object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
And then in the XAML
<ContentPage xmlns:converter="clr-namespace:GPS4Flight.Converter">
<ContentPage.Resources>
<converter:ResourceConverter x:Key="ResourceConverter" />
</ContentPage.Resources>
<Image Source="{Binding Source=Resources.icon192.png, Converter={StaticResource ResourceConverter}}"
VerticalOptions="Center" HeightRequest="50" Aspect="AspectFit" />
</ContentPage>
Notice I needed {Binding Source=[nameOfFile] for it to work.

If I use ContentPage.BindingContext in XAML to define my binding context then how do I access that in the C# back end?

In my XAML I define the binding context like this:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Japanese;assembly=Japanese"
             xmlns:template="clr-namespace:Japanese.Templates"
             xmlns:viewModels="clr-namespace:Japanese.ViewModels; assembly=Japanese"
             x:Class="Japanese.Cards" Title="{Binding Title}">
<ContentPage.BindingContext>
     <viewModels:CardsViewModel />
    </ContentPage.BindingContext>
</ContentPage>
What I would like to know is how can I access this context in my C# back end.
public partial class Cards : ContentPage
{
public Cards()
    {
     InitializeComponent();
        NavigationPage.SetBackButtonTitle(this, "Back");
    }
    protected override void OnAppearing()
    {
     // I want to set some properties of the view here
It seems you cannot give it an x:Name attribute like other elements in your XAML. In that case, your options are limited to declaring the object for your binding context in the code-behind, or referencing it from the BindingContext property.
For the latter approach, do it like this:
protected override void OnAppearing()
{
var cardsViewModel = BindingContext as CardsViewModel;
if (cardsViewModel == null)
return;
cardsViewModel.Property = Value;
}
Earlier answer for reference:
You should be able to give it a name like so:
<ContentPage.BindingContext>
<viewModels:CardsViewModel x:Name="cardsViewModel" />
</ContentPage.BindingContext>
This will effectively just create a declaration like this in generated code:
private CardsViewModel cardsViewModel;
You can now access it in your code-behind:
protected override void OnAppearing()
{
cardsViewModel.Property = Value;
}

Value Converter not working in Xamarin

A bit confused here, I seem to have followed the steps that would allow me to make use of value converters.
I have my converter defined with a key, as such:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage Title="Article"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:XamarinMobile.Controls;assembly=XamarinMobile"
xmlns:converters="clr-namespace:XamarinMobile.Converters;assembly=XamarinMobile"
x:Class="XamarinMobile.ArticlePage">
<ContentPage.Resources>
<ResourceDictionary>
<converters:FontSizeConverter x:Key="FontSizeMapper"></converters:FontSizeConverter>
</ResourceDictionary>
</ContentPage.Resources>
I then make use of my converter in my XAML, as such:
<ContentView Padding="10,-10,10,0" Grid.Row="2" Grid.Column="0">
<StackLayout>
<Label x:Name="LabelAuthor" FontSize="{Binding 20, Converter={StaticResource FontSizeMapper}, ConverterParameter=20}" />
<Label x:Name="LabelPublishDate" FontSize="{Binding 10, Converter={StaticResource FontSizeMapper}, ConverterParameter=10}"/>
</StackLayout>
</ContentView>
And here is my actual converter code:
namespace XamarinMobile.Converters
{
public class FontSizeConverter : Xamarin.Forms.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(value is double)
{
return App.NormalizeFontSize((double)value);
} else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I then put a break point in my value converter, but it never hits. Is there something obvious that I'm missing here? I'm pretty sure I followed the directions to a tee.
Your breakpoint isn't being hit due to what Gerald Versluis said. Your binding is broken. What your binding is saying is: Bind to the property named "10" on the BindingContext, and use the Converter FontSizeMapper, passing it an extra ConverterParameter of 10. "10" isn't a valid property name, so the binding is breaking. If you look in your logs, you should see a message similar to: "Binding: '10' property not found on ..."
One way to fix it would be to remove the "Path" you're trying to bind to and only make use of the ConverterParameter (assuming you don't need to bind to any real properties):
FontSize="{Binding Converter={StaticResource FontSizeMapper}, ConverterParameter=20}"
Note that you'll need to make use of the parameter in the converter, rather than the value (eg. if (parameter is double)).
If you don't need to bind to any properties, another way to fix it would be to use a custom markup extension instead.
[ContentProperty("FontSize")]
public class FontSizeMapperExtension : IMarkupExtension
{
public double FontSize { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
return App.NormalizeFontSize(FontSize);
}
}
Then you could use it in your XAML like:
FontSize="{converters:FontSizeMapper FontSize=10}
Edit
An example of binding to a property on an object:
public class YourViewModel
{
public double VMFontSize { get; set; }
}
public partial class ArticlePage : ContentPage
{
public ArticlePage()
{
InitializeComponent();
// NOTE: You'd probably get your view-model another way
var viewModel = new YourViewModel { VMFontSize = 10 };
BindingContext = viewModel;
}
}
Now that your view-model is set as the binding context, you can set the binding like:
FontSize="{Binding VMFontSize, Converter={StaticResource FontSizeMapper}}"
What this says is: Bind the FontSize property on the label to the VMFontSize property on the current BindingContext (your view-model), using the converter to map between the view-model's VMFontSize and the Label's FontSize. I left the ConverterParameter off here as it isn't really needed in this example, but you could pass one if you need it.
I would do this a different way, using a custom attached property, see more on attached properties here https://developer.xamarin.com/guides/xamarin-forms/xaml/attached-properties/
Here is a sample for your scenario, first we need to define an attached property, it can be in any class, I called mine FontHelper
namespace App23
{
public static class FontHelper
{
public static readonly BindableProperty FontSizeProperty =
BindableProperty.CreateAttached("FontSize", typeof(double), typeof(FontHelper), 0d, propertyChanging:OnPropertyChanging);
public static bool GetFontSize(BindableObject view)
{
return (bool)view.GetValue(FontSizeProperty);
}
public static void SetFontSize(BindableObject view, bool value)
{
view.SetValue(FontSizeProperty, value);
}
private static void OnPropertyChanging(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is Label)
{
var label = bindable as Label;
double fontSize = (double)newValue;
// normalize your font size here
label.FontSize = fontSize;
}
}
}
}
Then to use it in XAML, it looks like this:
<?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:App23"
x:Class="App23.MainPage">
<Label Text="Welcome to Xamarin Forms!"
VerticalOptions="Center"
HorizontalOptions="Center" local:FontHelper.FontSize="50"/>
</ContentPage>

Why does the data binding source need an explicit getter?

If I don't add explicit accessors to a String property, then data binding doesn't work. Why is that?
Here is a simple example where a text box is hooked up to a String property.
MainPage.xaml:
<Grid Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Text="{Binding Message} />
</Grid>
And the code behind:
public String Message;
public MainPage()
{
InitializeComponent();
Message = "Hello World";
DataContext = this;
}
This does not work, the text box is empty.
However, add property accessors;
public String Message { get; set; };
And now it works.
I can't see this explained in MSDN Data Binding. Can someone explain it? Don't properties have an implict set/get accessors? Even so, why can't data binding just access the property?
Thanks,
public String Message;
This is a field, not a property. Adding getters and setters creates an auto property.
Only properties can be bound to.
The { get; set; } is what makes the compiler generate those implicit accessors. If you leave that out, you're not creating a property at all, but a simple field.
I'm not into XAML that much so I can't say for sure, but maybe it just doesn't support binding to plain fields.
public String Message; is a field, not a property.
Databinding only works with properties.

Binding to Global variable in Silverlight for WP7

Let's say that I have a global variable defined in App.xaml.cs as follows:
public static MyClass GlobalInstance = new MyClass()
And then in MainPage.xaml I would like to bind to a property of this class like follows:
<TextBlock Text="{Binding App.GlobalInstance.Property1}" VerticalAlignment="Top" Height="31" HorizontalAlignment="Left" Width="80">
Is there something I am missing here? For some reason it does not appear to be properly bound.
Any advice here would be greatly appreciated.
Thanks!
You need to assign your App to DataContext of the page
First way is do this in page constructor:
public MainPage()
{
InitializeComponent();
DataContext = App.Current;
}
And your binding will be
{Binding GlobalInstance.Property1}
The second way is to make a reference to App class in page resources
Also, edit your field implementation to something like this:
public static MyClass GlobalInstance {get; private set; }
...
GlobalInstance = new MyClass();

Resources