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>
Related
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>
I am already doing things like this to make a new object called LineTemplate:
<?xml version="1.0" encoding="utf-8"?>
<BoxView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Ja.Templates.LineTemplate"
HeightRequest="1"
HorizontalOptions="FillAndExpand"
BackgroundColor="{DynamicResource LineSeparatorColor}" Margin="0"
/>
namespace Ja.Templates
{
public partial class LineTemplate : BoxView
{
public LineTemplate()
{
InitializeComponent();
}
}
}
But can I do something like this where I set the resource in the template XAML without having to code the setting of SetDynamicResource in C#
namespace Ja.Templates
{
public class Label : Label
{
public Label()
{
SetDynamicResource(Label.FontColorProperty, "TextColor");
}
}
}
Here's what I have implemented so far for iOS:
using System;
using Xamarin.Forms;
namespace Japanese
{
public class ExtCheckedTextCell: TextCell
{
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create(
"IsChecked", typeof(bool), typeof(ExtCheckedTextCell),
defaultValue: false);
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
}
}
and my renderer looks like this:
using System;
using System.ComponentModel;
using System.Diagnostics;
using Japanese;
using Japanese.iOS;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ExtCheckedTextCell), typeof(ExtCheckedTextCellRenderer))]
namespace Japanese.iOS
{
public class ExtCheckedTextCellRenderer : TextCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var nativeCell = base.GetCell(item, reusableCell, tv);
if (item is ExtCheckedTextCell formsCell)
{
SetCheckmark(nativeCell, formsCell);
SetTap(nativeCell, formsCell);
}
return nativeCell;
}
protected override void HandlePropertyChanged(object sender, PropertyChangedEventArgs args)
{
base.HandlePropertyChanged(sender, args);
System.Diagnostics.Debug.WriteLine($"HandlePropertyChanged {args.PropertyName}");
var nativeCell = sender as CellTableViewCell;
if (nativeCell?.Element is ExtCheckedTextCell formsCell)
{
if (args.PropertyName == ExtCheckedTextCell.IsCheckedProperty.PropertyName)
SetCheckmark(nativeCell, formsCell);
}
}
void SetCheckmark(UITableViewCell nativeCell, ExtCheckedTextCell formsCell)
{
if (formsCell.IsChecked)
nativeCell.Accessory = UITableViewCellAccessory.Checkmark;
else
nativeCell.Accessory = UITableViewCellAccessory.None;
}
}
For reference here's the XAML where it is used:
<TableSection>
<local:CheckedTextCell Text="{Binding [6].Name}" IsChecked="{Binding [6].IsSelected}" Tapped="atiSelectValue" />
<local:CheckedTextCell Text="{Binding [7].Name}" IsChecked="{Binding [7].IsSelected}" Tapped="atiSelectValue" />
<local:CheckedTextCell Text="{Binding [8].Name}" IsChecked="{Binding [8].IsSelected}" Tapped="atiSelectValue" />
</TableSection>
Does anyone have any ideas how can I implement this in Android using a custom renderer or if it is even possible to do it?
Here's an example (not mine) of what it looks like in iOS. What I am hoping for is the Android can show a similar tick mark on the right side.
Custom Renderer
You can build a custom renderer in Android (although, I think an easier approach is to create a custom ViewCell):
using System.ComponentModel;
using Android.Content;
using Android.Views;
using Android.Widget;
using Sof;
using Sof.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using AView = Android.Views.View;
[assembly: ExportRenderer(typeof(ExtCheckedTextCell), typeof(ExtCheckedTextCellRenderer))]
namespace Sof.Droid
{
public class ExtCheckedTextCellRenderer : TextCellRenderer
{
public const string CheckedText = "✓";
private TextView Check { get; set; }
protected override AView GetCellCore(Cell item, AView convertView, ViewGroup parent, Context context)
{
var view = base.GetCellCore(item, convertView, parent, context) as BaseCellView;
if (this.Check == null)
{
this.Check = new TextView(context);
this.Check.Gravity = GravityFlags.Center;
using (var lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.MatchParent))
{
view.AddView(this.Check, lp);
}
var paddingRight = context.Resources.GetDimension(Resource.Dimension.abc_list_item_padding_horizontal_material);
view.SetPadding(view.PaddingLeft, view.PaddingTop, (int)paddingRight, view.PaddingBottom);
}
return view;
}
protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
{
base.OnCellPropertyChanged(sender, args);
if (args.PropertyName.Equals(ExtCheckedTextCell.IsCheckedProperty.PropertyName) &&
sender is ExtCheckedTextCell extCheckedTextCell && this.Check != null)
{
this.Check.Text = extCheckedTextCell.IsChecked ? CheckedText : string.Empty;
}
}
}
}
Custom Xamarin.Forms.ViewCell (no platform-specific code needed)
For a simple layout like you want (label and checkmark), a custom ViewCell seems more appropriate and allows direct control over the style.
ExtCheckedTextCell2.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Sof.ExtCheckedTextCell2"
x:Name="this">
<ViewCell.View>
<StackLayout Orientation="Horizontal"
Padding="12, 0">
<Label HorizontalOptions="FillAndExpand"
Text="{Binding Text, Source={x:Reference this}}"
VerticalTextAlignment="Center" />
<Label IsVisible="{Binding IsChecked, Source={x:Reference this}}"
HorizontalOptions="End"
Text="✓"
VerticalTextAlignment="Center"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
ExtCheckedTextCell2.xaml.cs
public partial class ExtCheckedTextCell2 : ViewCell
{
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create(
nameof(IsChecked),
typeof(bool),
typeof(ExtCheckedTextCell2),
default(bool));
public static readonly BindableProperty TextProperty =
BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(ExtCheckedTextCell2),
default(string));
public ExtCheckedTextCell2()
{
InitializeComponent();
}
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
protected override void OnTapped()
{
base.OnTapped();
this.IsChecked = !this.IsChecked;
}
}
Result
<TableView>
<TableSection Title="Custom Renderer">
<local:ExtCheckedTextCell Text="Test1" Tapped="Handle_Tapped" />
<local:ExtCheckedTextCell Text="Test2" Tapped="Handle_Tapped" />
<local:ExtCheckedTextCell Text="Test3" Tapped="Handle_Tapped" />
</TableSection>
<TableSection Title="Custom Xamarin.Forms ViewCell">
<local:ExtCheckedTextCell2 Text="Test1" />
<local:ExtCheckedTextCell2 Text="Test2" />
<local:ExtCheckedTextCell2 Text="Test3" />
</TableSection>
</TableView>
But you can also do it in xaml ?
This is a xaml only solution :) should work for Android and Ios .
.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"
x:Class="StackoverflowQ.Views.MainPage"
Title="Driving & Navigation">
<ContentPage.Resources>
</ContentPage.Resources>
<ScrollView>
<StackLayout>
<StackLayout x:Name="Header" BackgroundColor="#efeff4" HorizontalOptions="FillAndExpand" HeightRequest="30" Padding="10">
<Label Text="NAVIGATION VOICE VOLUME" Margin="0, 0, 0, 5" VerticalOptions="EndAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal" Padding="10">
<Label Text="No Voice" TextColor="Black" />
<Image Source="checkboxchecker.png" IsVisible="{Binding IsCheckBoxVisible}" HorizontalOptions="EndAndExpand" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCheckBoxCommand}" NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
</StackLayout>
<BoxView HeightRequest="1" HorizontalOptions="FillAndExpand" BackgroundColor="#efeff4" />
</StackLayout>
</ScrollView>
</ContentPage>
ViewModel
namespace StackoverflowQ.ViewModels
{
public class MainPageViewModel : ViewModelBase
{
public DelegateCommand TapCheckBoxCommand { get; set; }
private bool _isCheckBoxVisible;
public bool IsCheckBoxVisible
{
get => _isCheckBoxVisible;
set => SetProperty(ref _isCheckBoxVisible, value);
}
public MainPageViewModel(INavigationService navigationService)
: base(navigationService)
{
Title = "Main Page";
TapCheckBoxCommand = new DelegateCommand(TapCheckBoxSelected);
}
public void TapCheckBoxSelected()
{
IsCheckBoxVisible = !IsCheckBoxVisible;
}
}
}
I am an new Xamarin Form. I created a simple xamarin forms project with mvvmcross (Hello World very simple for begin), but when i implemented binding command, and not effect change text of label. My Xaml code and ViewModel below.
<?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:vm="clr-namespace:MvvmCross.ViewModels;assembly=MvvmCross"
x:Class="MvvmCross.Views.HelloView">
<StackLayout>
<StackLayout.BindingContext>
<vm:HelloViewModel />
</StackLayout.BindingContext>
<Entry HorizontalOptions="Fill" VerticalOptions="Center" Text="{Binding Name, Mode=TwoWay }"/>
<Button Text="Hello" HorizontalOptions="Center" VerticalOptions="Center" Command="{Binding HelloCommand}" />
<Label HorizontalOptions="Fill" VerticalOptions="Center" FontSize="15" Text="{Binding Hello, Mode=TwoWay}" />
</StackLayout>
using MvvmCross.Core.ViewModels;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MvvmCross.ViewModels
{
public class HelloViewModel: Core.ViewModels.MvxViewModel
{
private string _name;
public HelloViewModel()
{
Hello = "Your name";
}
public string Name
{
get { return _name; }
set { _name = value; RaisePropertyChanged(() => Name); }
}
private string _hello;
public string Hello
{
get { return _hello; }
set { _hello = value; RaisePropertyChanged(() => Hello); }
}
private ICommand _helloCommand;
public ICommand HelloCommand
{
get { _helloCommand = _helloCommand ?? new MvxCommand(ShowHello); return _helloCommand; }
}
private void ShowHello()
{
// not change label text so sadly
Hello = Name.ToString();
Debug.WriteLine(Hello);
}
}
}
Thank for all helping
Even if late, it could help someone else.
if you have set up correctly MvvmCross on your Xamarin Forms project (review [Getting Started with MvvmCross][1]) you don't need to specifically set the BindigContext, neither in the view nor in the view model.
About the question, simple example of the use of the button's command binding:
view
<views:MvxContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr- namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
x:Class="TestProject.Pages.TestPage">
<ContentView>
<StackLayout>
<Button Text="Test first command!" Command="{Binding TestFirstCommand}"/>
<Button Text="Test second command!" Command="{Binding TestSecondCommand}"/>
<Label Text="{Binding AnyText}"/>
</StackLayout>
</ContentView>
view model
namespace TestProject.ViewModels
{
public class TestViewModel : MvxNavigationViewModel
{
private string _AnyTest;
public TestViewModel()
{
AnyText = "";
}
public string AnyText { get => _AnyTest; set => SetProperty(ref _AnyTest, value); }
public Command TestFirstCommand => new Command(TestFirstCommandMethod);
public Command TestSecondCommand => new Command(TestSecondCommandMethod);
private void TestFirstCommandMethod()
{
AnyText = "Hello!";
}
private void TestSecondCommandMethod()
{
AnyText = "How are you?";
}
}
}
Has u set the BindingContext?
In your HelloView.xaml.cs:
public HelloView() {
BindingContext = new HelloViewModel();
}
I'm on mobile, really hard to type..
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