Dynamic binding with User Control does not work as Static is working in Silverlight and MVVM - data-binding

I have created sample User Control
RestrictedBox.xaml
<UserControl.Resources>
<Converters:EnumToVisibilityConverter x:Key="enumToVisConverter" />
<Converters:EnumToVisibilityConverterReverse x:Key="enumToVisConverterReverse" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" Width="Auto">
<StackPanel Margin="0">
<TextBox Text="{Binding Value}" Visibility="{Binding Type,Converter={StaticResource enumToVisConverter}}" BorderBrush="Green" />
<PasswordBox Password="{Binding Value}" Visibility="{Binding Type,Converter={StaticResource enumToVisConverterReverse}}" BorderBrush="Red" />
</StackPanel>
</Grid>
RestrictedBox.xaml.cs
public partial class RestrictedBox : UserControl
{
public RestrictedBox()
{
InitializeComponent();
//If i bind static value and uncomment following line then it is working.
//If i bind static value and comment following line then it is not working
this.DataContext = this;
//Dynamic binding does not work using this code.
}
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(RestrictedBox), new PropertyMetadata("", ValueChanged));
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public Mode Type
{
get { return (Mode)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type", typeof(Mode), typeof(RestrictedBox), new PropertyMetadata(TypeChanged));
private static void TypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
LoginViewModel.cs
public class LoginViewModel : INotifyPropertyChanged, IRegionMemberLifetime
{
private string _UserName = "Imdadhusen Sunasara";
public string UserName
{
get { return _UserName; }
set
{
_UserName = value;
OnPropertyChanged("UserName");
}
}
}
LoginView.xaml (This following line does not work with binding)
<control:RestrictedBox Value="{Binding UserName}" Type="Text" />
This is working (with static binding)
<control:RestrictedBox Value="Imdadhusen" Type="Text" />
Thanks,
Imdadhusen

Actually It should work. Can you please verify that the DataContext of parent container of below control doesn't refering to any other property of viewmodel.
<control:RestrictedBox Value="Imdadhusen" Type="Text" />
eg. Something like below.
<StackPanel DataContext={Binding CurrentUser}>
<control:RestrictedBox Value="{Binding UserName}"
Type="Text" />
</StackPanel>
May be this help you....

I have got solution from following
http://forums.silverlight.net/t/250206.aspx/1?Dynamic+binding+with+User+Control+does+not+work+as+Static+is+working+in+Silverlight+and+MVVM
Thanks everybody who trying to help me.
Imdadhusen

Related

Xamarin Forms: How can i correctly bind data from two view models to a single view?

This is the short code for testing purpose. The problem is that the UI is not displaying the Text from the Label which is binded with ViewModelB. In debugging when I hover the mouse in xaml over the Text from the Label I see the right binding data is there, but the UI simply won't display. With ViewModelA there are no problems.
In XAML:
<StackLayout>
<StackLayout>
<StackLayout.BindingContext>
<testbinding:ViewModelA/>
</StackLayout.BindingContext>
<Button Command ="{Binding Get}"/>
</StackLayout>
<StackLayout>
<StackLayout.BindingContext>
<testbinding:ViewModelB/>
</StackLayout.BindingContext>
<Label Text="{Binding Metadata}"/>
</StackLayout>
ViewModelA: where BaseViewModel is a INotifyPropertyChanged interface
public ViewModelA:BaseViewModel
{
public ViewModelA()
{
Get = new Command(SendText);
vmB = new ViewModelB();
}
ViewModelB vmB;
public ICommand Get { get; }
private void SendText()
{
string data = "someText";
vmB.GetMetadata(data);
}
}
ViewModelB is like this:
class ViewModelB:BaseViewModel
{
private string _metadata = string.Empty;
public string Metadata
{
get { return _metadata; }
set
{
_metadata = value;
OnPropertyChanged();
}
}
GetMetadata()
{
Metadata = "Some text";
}
}
In ViewModelA there are more properties which I need and in ViewModelB is just one property which gets data from a function. I could make just one ViewModel from both of them which works fine, but I'm trying to keep them smaller and organized. I already tried so many scenarios and is getting really frustrating.
Thanks for helping.
In the second StackLayout in your xaml file you're not binding it's BindingContext property to the ViewModelB instance from ViewModelA but instead you are creating a new one.
Here's a working solution for you:
public class ViewModelA : BaseViewModel
{
public ViewModelB ViewModelB { get; }
public ICommand GetMetadataCommand { get; }
public ViewModelA()
{
ViewModelB = new ViewModelB();
GetMetadataCommand = new Command((_) => GetMetadata());
}
private void GetMetadata()
{
string data = "someText";
ViewModelB.GetMetadata(data);
}
}
public class ViewModelB : BaseViewModel
{
private string _metadata;
public string Metadata
{
get { return _metadata; }
set
{
_metadata = value;
OnPropertyChanged();
}
}
public void GetMetadata(string data)
{
Metadata = data;
}
}
XAMl:
<StackLayout>
<StackLayout x:Name="StackLayout1">
<StackLayout.BindingContext>
<local:ViewModelA />
</StackLayout.BindingContext>
<Button Command ="{Binding GetMetadataCommand}"/>
</StackLayout>
<StackLayout BindingContext="{Binding Source={x:Reference StackLayout1}, Path=BindingContext.ViewModelB}">
<Label Text="{Binding Metadata}" />
</StackLayout>
</StackLayout>

ViewCell execue command from ViewModel

I want to bind my MenuItem from my ViewCell which has a ViewModel.
This is my View cell, Do any one know the proper way do this type of binding?
I have an ObservableCollection<object> that populate my ListView, this the cell I need to bind.
<?xml version="1.0" encoding="UTF-8" ?>
<ViewCell
x:Class="DataTemp.Controls.DeviceViewCell"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="this">
<ViewCell.ContextActions>
// I've been trying these two ways
<MenuItem Command="{Binding Source={x:Reference this}, Path=BindingContext.DeviceCommand}" Text="Call" />
<MenuItem Command="{Binding DeviceCommand}" Text="Call" />
</ViewCell.ContextActions>
<ViewCell.View>
<StackLayout Padding="10">
<Label Text="{Binding Name}" />
<Label Text="{Binding Number}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
Here is my view model
I'm trying to catch the event with a a breakpoint, but i never get into the method OnDeviceCommnad.
public class DeviceViewCellViewModel : BaseViewModel, IDevice
{
public string Number
{
get
{
return _numberDevice;
}
set
{
_numberDevice = value;
OnPropertyChanged("Number");
}
}
public string Name
{
get
{
return _nameDevice;
}
set
{
_nameDevice = value;
OnPropertyChanged("Number");
}
}
public ICommand DeviceCommand { get; set; }
private string _nameDevice;
private string _numberDevice;
public DeviceViewCellViewModel()
{
DeviceCommand = new Command(OnDeviceCommnad);
}
private void OnDeviceCommnad()
{
}
}
If you not need to pass a parameter, the second way should work.
<MenuItem Command="{Binding DeviceCommand}" Text="Call" />
And in Model sample code could as follows:
public Command DeviceCommand { get; private set; }
public DeviceViewCellViewModel()
{
DeviceCommand = new Command(() =>
{
// Execute logic here
});
}
=============================Update==================================
From shared sample, I found that the binded MainViewModel not using DeviceViewCellViewModel, therefore the command will not be invoked.
Modify the folloing code inside MainViewModel:
PaymentsAndDevices.Add(new Devices()
{
Number = rdn.Next().ToString(),
Name = "I'm a device"
});
as follows:
PaymentsAndDevices.Add(new DeviceViewCellViewModel()
{
Number = rdn.Next().ToString(),
Name = "I'm a device"
});
Then OnDeviceCommnad method will be invoked.

Steps to create a behavior (how create a behavior)

i need to create a behavior from a view, i am using a class, also namespace to call to behavior but it's not working for me, i do not know what is my wrong, because i am doing all of step to create a behavior.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Layouts.Commands.BasketView"
xmlns:local="clr-namespace:Layouts;assembly=Layouts"
Title="Cart">
<StackLayout Padding="10,60,10,0">
<Label Text="Red when the number isn't valid" FontSize="Small" />
<Entry Placeholder="Enter a System.Double"
local:NumericValidationBehavior.AttachBehavior="true" />
</StackLayout>
</ContentPage>
in this line i get the error local:NumericValidationBehavior.AttachBehavior="true" />
i have my behavior, but it cannot be instanciate in the view (see code up), there are other step to be instanciate?
namespace AttachedNumericValidationBehavior
{
public static class NumericValidationBehavior
{
public static readonly BindableProperty AttachBehaviorProperty
= BindableProperty.CreateAttached("AttachBehavior", typeof(bool), typeof(NumericValidationBehavior), false, propertyChanged: OnAttachBehaviorChanged);
public static bool GetAttachBehavior(BindableObject view)
{
return (bool)view.GetValue(AttachBehaviorProperty);
}
public static void SetAttachBehavior(BindableObject view, bool value)
{
view.SetValue(AttachBehaviorProperty, value);
}
static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
{
var entry = view as Entry;
if (entry == null)
{
return;
}
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{
entry.TextChanged += OnEntryTextChanged;
}
else
{
entry.TextChanged -= OnEntryTextChanged;
}
}
static void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
}
}
}
the error is: "AttachBehavior is not in type NumericValidationBehavior"
You class NumericValidationBehavior belongs to namespace AttachedNumericValidationBehavior, so when you want to use NumericValidationBehavior in xaml, the namespace local should be:
xmlns:local="clr-namespace:AttachedNumericValidationBehavior;"
And use the namespace:
<StackLayout Padding="10,60,10,0">
<Label Text="Red when the number isn't valid" FontSize="Small" />
<Entry Placeholder="Enter a System.Double"
local:NumericValidationBehavior.AttachBehavior="true" />
</StackLayout>
For more details, see document: Declaring Namespaces for Types

Xamarin Forms how to add behaviors to custom control

I have created a custom control,which is a ContentView with a Label and an Entry
The xaml of the custom controls looks like this:
<Label Text="{Binding Source={x:Reference ValidationControl}, Path=Caption}"/>
<Entry Text="{Binding Source={x:Reference ValidationControl}, Path=Value, Mode=TwoWay}" />
The code behind of the custom control looks like this:
public static readonly BindableProperty CaptionProperty = BindableProperty.Create(
nameof(Caption), typeof(string), typeof(ValidationEntry), default(string));
public string Caption
{
get => (string)GetValue(CaptionProperty);
set => SetValue(CaptionProperty, value);
}
public static readonly BindableProperty ValueProperty = BindableProperty.Create(
nameof(Value), typeof(string), typeof(ValidationEntry), default(string));
public string Value
{
get => (string)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
I’m using the custom control in the following way
<controls:ValidationEntry Caption=”Name:” Value="{Binding FullName, Mode=TwoWay}" />
My question is how to add behaviors to the custom control?
I would like to add them in the place that I’m using the control. i.e.
<controls:ValidationEntry Caption="Name:"
Value="{Binding FullName, Mode=TwoWay}">
<controls:ValidationEntry.EntryBehaviors>
<behaviors:EntryLengthValidatorBehavior IgnoreSpaces="True"/>
</controls:ValidationEntry.EntryBehaviors>
</controls:ValidationEntry>
You can create a behaviors directly, I add a NumericValidationBehavior in my custom entry to check the data if it is double.If type of the data is not double, the color of text will be set to red.
Here is xaml code.
<StackLayout>
<local:MyEntry local:NumericValidationBehavior.AttachBehavior="true">
</local:MyEntry>
</StackLayout>
Here is NumericValidationBehavior.cs
public static class NumericValidationBehavior
{
public static readonly BindableProperty AttachBehaviorProperty =
BindableProperty.CreateAttached(
"AttachBehavior",
typeof(bool),
typeof(NumericValidationBehavior),
false,
propertyChanged: OnAttachBehaviorChanged);
public static bool GetAttachBehavior(BindableObject view)
{
return (bool)view.GetValue(AttachBehaviorProperty);
}
public static void SetAttachBehavior(BindableObject view, bool value)
{
view.SetValue(AttachBehaviorProperty, value);
}
static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
{
var entry = view as Entry;
if (entry == null)
{
return;
}
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{
entry.TextChanged += OnEntryTextChanged;
}
else
{
entry.TextChanged -= OnEntryTextChanged;
}
}
static void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
}
}
Update
I create a custom view with ContentView
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BeHavDemo.MyView">
<ContentView.Content>
<StackLayout>
<Label Text="xxxx"/>
<Entry Text="eeeee" />
</StackLayout>
</ContentView.Content>
</ContentView>
Then I create a behavior.
public class MyBeha : Behavior<MyView>
{
protected override void OnAttachedTo(BindableObject view)
{
base.OnAttachedTo(view);
var myview=view as MyView;
StackLayout stackLayout = (StackLayout)myview.Content;
Label label = (Label)stackLayout.Children[0];
Entry entry=(Entry) stackLayout.Children[1];
}
}

Listbox binding in Silverlight

I have a listbox on silverlight page, the datacontext of the page is set to an instance -- TestQuestions, please see the code below. The listbox uses a DataTemplate and its ItemSource is 'Answers' which is a property of the page's DataContext, everything works fine until I try to bind to 'ShowAnswer', a property on the page's DataContext within the DataTemplate. No matter what I tried it won't pick the property's value.
Thank you all for your help.
Xaml:
<ListBox ItemsSource="{Binding Path=Answers, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton IsChecked="{Binding Path=Correct, Mode=TwoWay}" >
<Grid>
<StackPanel Visibility="{Binding Path=ShowAnswer}">
<Rectangle Fill="Blue" Visibility="{Binding Path=Correct}" />
</StackPanel>
<TextBlock Text="{Binding Path=AnswerText, Mode=TwoWay}" TextWrapping="Wrap" />
</Grid>
</RadioButton>
</DataTemplate>
</ListBox.ItemTemplate>
C#:
public class Answer
{
private bool correct = false;
public bool Correct
{
get { return correct; }
set
{
correct = value;
NotifyPropertyChanged("Correct");
}
}
private string answerText = false;
public string AnswerText
{
get { return answerText; }
set
{
answerText = value;
NotifyPropertyChanged("AnswerText");
}
}
}
public class TestQuestions
{
private ObservableCollection<Answer> answers = new ObservableCollection<Answer>();
public ObservableCollection<Answer> Answers
{
get { return answers; }
set
{
if (answers != value)
{
answers = value;
NotifyPropertyChanged("Answers");
}
}
}
private bool showAnswer = false;
public bool ShowAnswer
{
get { return showAnswer; }
set
{
showAnswer = value;
NotifyPropertyChanged("ShowAnswer");
}
}
}
It looks to me like you're trying to bind the UIElement.Visibility to a boolean value. Change your 'ShowAnswer' property from a boolean to a Visibility property and you should be set.
private Visibility showAnswer = Visibility.Collapsed;
public Visibility ShowAnswer
{
get { return showAnswer; }
set
{
showAnswer = value;
NotifyPropertyChanged("ShowAnswer");
}
}
EDIT:
If you are trying to bind to a property on the DataContext of the parent control, you could do this:
Name your UserControl
Example:
<UserControl x:Class="MvvmLightProto.MainPage"
x:Name="mainProtoPage"
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"
Height="700"
Width="1200">
In the above example, the UserControl has the name of "mainProtoPage", now in your XAML, you could do this:
<StackPanel Visibility="{Binding DataContext.ShowAnswer, ElementName=mainProtoPage}">

Resources