MVVM Light Toolkit: Is it possible to Send a message in the xaml via a trigger? - mvvm-light

Is it possible to Send a message in the xaml via a trigger?
thanks.

I assume you're referring to MVVM Light Toolkit's messaging. If so, then No, this isn't possible.
However, it is not the responsibility of the View to do things like sending messages. The view's responsibility is to provide a view to the data and interaction with the user. Instead, you should have your ViewModel sending the message. You can "trigger" the message with a command in your view, but the command execute is what actually sends the message.
Here's an example View (MainPage.xaml) that has a couple of different ways to execute a command.
<UserControl x:Class="MvvmLight5.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4" mc:Ignorable="d"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<StackPanel x:Name="LayoutRoot">
<TextBlock Text="Click Here to Send Message">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<cmd:EventToCommand Command="{Binding Path=SendMessageCommand, Mode=OneWay}"
CommandParameter="Sent From TextBlock" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
<Button Content="Or Click Here"
Command="{Binding Path=SendMessageCommand, Mode=OneWay}"
CommandParameter="Sent From Button" />
</StackPanel>
</UserControl>
And here's the MainViewModel.cs that sends the message.
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
namespace MvvmLight5.ViewModel
{
public class MainViewModel : ViewModelBase
{
public RelayCommand<string> SendMessageCommand { get; private set; }
public MainViewModel()
{
SendMessageCommand = new RelayCommand<string>(SendMessageCommandExecute);
}
private void SendMessageCommandExecute(string sentFrom)
{
Messenger.Default.Send(new NotificationMessage(sentFrom));
}
}
}
Also, I should note that these were created in Silverlight 4.

Related

Relay Command doesn't execute

I've decided to use the mvvm architecture in my project and tried converting my code to it.
But it looks like I'm misunderstanding something, as the properties I'd like to set in an relay command are either not changed or the changing has no effect. Either way this behavior is not desired.
My code is very simple and looks like this:
MainViewModel.cs
public partial class MainViewModel : ObservableRecipient
{
[ObservableProperty] private string testText;
[RelayCommand]
private void Toggle()
{
testText = "pressed";
}
}
Mainview.xaml
<Page
x:Class="Calendarium.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:viewModels="using:Calendarium.ViewModels"
d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}">
<Grid>
<TextBlock Text="{Binding TestText}">
<Button Command="{Binding ToggleCommand}">
</Grid>
</Page>
You seem to have set the design time DataContext only.
You also need to set the actual DataContext that is used at runtime:
<Page
x:Class="Calendarium.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:viewModels="using:Calendarium.ViewModels"
d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}">
<Page.DataContext>
<viewModels:MainViewModel />
</Page.DataContext>
<Grid>
...
</Grid>
</Page>
Besides, you should set the value of the generated property in your Toggle method:
[RelayCommand]
private void Toggle()
{
TestText = "pressed";
}

How to create a custom control that user can add any kinds of view into it in Xamarin.Forms?

I want to create a custom control that can let users add any view to a specific location, like below:
MyCustomControl.xaml
<ContentView ...>
<!-- My customizations ... -->
<StackLayout>
<!-- Everything added by user should goes here -->
</StackLayout>
<!-- My other customizations ... -->
</ContentView>
When using it:
<ContentPage>
<controls:MyCustomControl>
<Label />
<Image />
...
<controls:MyCustomControl>
</ContentPage>
How can I do this?
I'm going to post one way of doing this here but there are others. From your question, I'm assuming you want to reuse a ContentView in various places and be allowed to change a part of that content view when you instantiate it. I'm going to assume you are going to specify the custom part in XAML. You can use the following pattern to achieve this:
Create your ContentView and add a bindable property for the custom part:
ContentViewWithCustomPart.xaml
<ContentView.Content>
<StackLayout>
<Label Text="I'm always here!" />
<StackLayout x:Name="StackToHoldCustomStuff">
<!-- Stuff goes here injected by bindable property -->
</StackLayout>
</StackLayout>
</ContentView.Content>
ContenViewWithCustomPart.xaml.cs
public partial class ContentViewWithCustomPart : ContentView
{
public static readonly BindableProperty CustomViewProperty =
BindableProperty.Create(nameof(CustomView), typeof(View), typeof(ContentViewWithCustomPart), propertyChanged: OnViewChanged);
static void OnViewChanged(object bindable, object oldValue, object newValue)
{
var page = (ContentViewWithCustomPart)bindable;
page.StackToHoldCustomStuff.Children.Clear();
if (newValue != null) page.StackToHoldCustomStuff.Children.Add((View)newValue);
}
public View CustomView { get => (View)GetValue(CustomViewProperty); set => SetValue(CustomViewProperty, value); }
public ContentViewWithCustomPart()
{
InitializeComponent();
}
}
Usage in a demo page:
<ContentPage.Content>
<StackLayout>
<cv:ContentViewWithCustomPart>
<cv:ContentViewWithCustomPart.CustomView>
<Label Text="I'm a custom bit!" />
</cv:ContentViewWithCustomPart.CustomView>
</cv:ContentViewWithCustomPart>
</StackLayout>
</ContentPage.Content>
Result:

Xamarin.Forms: Tabs with Sharnado.presentation.forms do not respond to tap

I'm trying to use Sharpnado's awesome tab functionality to create what they call "Fixed tabs". But it is not doing what I want it to - and the problem may lay somewhere else... Basically, the tabs are not responding to a tap. I have narrowed it down to just that with this simple exampl.
The context is an app that uses an ordinary tabbed page (with navigation pages for each tab). On one of these tabs I have tried to put the TabHostViewcontrol - even without binding as I originally thought that was the problem. This is a screenshot of the test page:
Nothing happens when I tap the two tabs ("Personlig" and "Udforsk"). The XAML 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:tabs="clr-namespace:Sharpnado.Presentation.Forms.CustomViews.Tabs;assembly=Sharpnado.Presentation.Forms"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true"
xmlns:views="clr-namespace:Angler.Views.Partials"
x:Class="Angler.Views.StatisticsPage">
<NavigationPage.TitleView>
<Label Text="{Binding Title}" HorizontalOptions="Center"
VerticalOptions="Center" Style="{StaticResource PageTitle}" />
</NavigationPage.TitleView>
<StackLayout VerticalOptions="FillAndExpand" BackgroundColor="LightYellow">
<Label Text="{Binding ShowSelectedTab}" Margin="20,10" />
<Grid RowSpacing="0" ColumnSpacing="0" Margin="0"
BackgroundColor="LightPink" VerticalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<tabs:TabHostView ShadowType="Bottom" Grid.Row="0">
<tabs:TabHostView.Tabs>
<tabs:UnderlinedTabItem Label="Personlig" />
<tabs:UnderlinedTabItem Label="Udforsk" IsSelected="True" />
</tabs:TabHostView.Tabs>
</tabs:TabHostView>
</Grid>
</StackLayout>
</ContentPage>
The viewmodel looks like this (although the above has been narrowed down to not using the property for the tabindex. But I did also investigate if the binding context for the viewmodel was Ok - and it seems to be):
public class StatisticsPageViewModel : ViewModelBase
{
public int SelectedTabIndex { get; set; }
public string ShowSelectedTab { get { return $"Index: {SelectedTabIndex}"; } }
public StatisticsPageViewModel(INavigationService navigationService, IPageDialogService pageDialogService,
IDeviceService deviceService) : base(navigationService, pageDialogService,
deviceService)
{
Title = AppResources.StatisticsPageTitle;
}
public override void OnAppearing()
{
//SelectedTabIndex = 1;
base.OnAppearing();
Analytics.TrackEvent("Navigate to Statistics");
}
}
I'm using VisualStudio for Mac 8.3.4 (build 8), Sharpnado.presentations.forms 1.3.0 and Prism.forms 7.2.0.1367.
I have tried to add InputTransparent="True" to some of the container objects - with no effect.
Any good ideas as to how to solve this is most appreciated!
Ok, this was just stupid (and a waste of two days!)...
In trying to solve other issues I had stepped back in my source repo - and once too far back... So the initialization of Sharpnado in the platforms projects were not there. That kind of made a difference.
So you will see this kind of behavious if you have not remembered to add this line to your platform projects:
SharpnadoInitializer.Initialize()
Just leaving it here in the case that someone else might end up with the same symptom :-)

How to instantiate xamarin android/ios native custom control class in xamarin forms xaml

Custom control class:
public class CustomTextInput : UITextField
{
}
Xamarin.Forms xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Custom="clr-namespace:SampleApp.CustomControls;assembly=SampleApp"
x:Name="SampleAppView">
<ContentPage.Content>
<StackLayout Orientation="Horizontal">
<Image Source="Sample_ic.png" WidthRequest="29" HeightRequest="29"/>
<StackLayout Orientation="Vertical">
<Label Text="Sample Text" TextColor="#717073" FontSize="Small" FontFamily="Helvetica"/>
<!--Accessing IOS Custom control class-->
<Custom:CustomTextInput Text="50 Gal" BackgroundColor="Transparent" TextColor="#838288" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Can I access android/IOS custom control class in xamarin.forms xaml? For sample I used UITextField control. But I need to access android/IOS platform specific custom controls which is not available in xamarin.forms.
If you're using Xamarin.Forms, you're working with the platform independent controls. UITextField doesn't exist, but is instead mapped to an Entry.
Your best bet is to make a custom Entry subclass:
public class MyEntry : Entry
{
}
And use it in your 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="MyApp.SomePage"
xmlns:local="clr-namespace:MyApp;assembly=MyApp">
<local:MyEntry />
</ContentPage>
Then, on each platform, you can provide a platform specific Renderer.
[assembly:ExportRenderer (typeof(MyApp.MyEntry), typeof(MyApp.iOS.MyEntryRenderer))]
namespace MyApp.iOS
{
public class MyEntryRenderer : EntryRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged (e);
if (null == Control)
return;
var textField = Control as UITextField;
// do stuff with textField
}
}
}
If you find that rendering the custom controls individually does not provide the exact level of control you need, you can also make custom Page Renderers, as seen here.

AppBarButton is not binding to MVVM Relay command in ListView

Hi I am trying to add AppBarButton in a ListView and binded command to RelayCommand in ViewModel here is my xaml code
<DataTemplate x:Key="MyTemplage" >
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<AppBarButton Grid.Column="1" Command="{Binding DoCommand,Mode=OneWay}">
<AppBarButton.Icon>
<BitmapIcon UriSource="ms-appx:///assets/doIcon.png"></BitmapIcon>
</AppBarButton.Icon>
</AppBarButton>
</DataTemplate>
<ListView HorizontalAlignment="Left" Margin="0,45,0,0" Background="Khaki" VerticalAlignment="Top" ItemsSource="{Binding AList, Mode=TwoWay}"
ItemTemplate="{StaticResource MyTemplage}">
</ListView>
here is my Relay command code in VM
private RelayCommand _DoCommand;
/// <summary>
/// Gets.
/// </summary>
public RelayCommand DoCommand
{
get
{
return _DoCommand
?? (_DoCommand = new RelayCommand(
() =>
{
DoSomething();
}));
}
}
DoCommand is not raising in ViewModel. If I register click event handler in code behind it works fine. also AppBarButton is wokring fine with MVVM if I use it in Page.Bottombar.
any ideas?
The problem is that the binding inside the ListView DataTemplate is not to the ViewModel object, but to a different DataContext, in your case it's binding to a list called AList which is inside the ViewModel and contains a list of model classes - so the binding engine is essentially looking for DoCommand inside that model class.
In order to get the binding to work, you must make sure that the binding is pointing to the whole ViewModel DataContext, where the RelayCommand actually is. One way you could do that is to bind to some element in your page which has the DataContext set to the whole ViewModel:
Command="{Binding DataContext.DoCommand, ElementName=pageRoot, Mode=OneWay}"
In this case, I'm binding to pageRoot, where pageRoot is the name of the root page element of your page which has the proper DataContext set - ViewModel where the RelayCommand actually is.

Resources