XamlParseException Failed to assign to property. Binding not working with attached property - xaml

I want to create custom text box with attached property for Windows Store app. I am following this solution. Now it uses hard coded value as property value but I want to set value using binding, but it's not working. I tried to search a lot but didn't helped me any solution.
The exception details is like this
An exception of type 'Windows.UI.Xaml.Markup.XamlParseException'
occurred in CustomTextBox.exe but was not handled in user code
WinRT information: Failed to assign to property
'CustomTextBox.Input.Type'.
MainPage.xaml
<!-- local:Input.Type="Email" works -->
<!-- local:Input.Type="{Binding SelectedTextboxInputType}" not working -->
<TextBox x:Name="txt" local:Input.Type="{Binding SelectedTextboxInputType}" Height="30" Width="1000" />
<ComboBox x:Name="cmb" ItemsSource="{Binding TextboxInputTypeList}" SelectedItem="{Binding SelectedTextboxInputType}" Height="30" Width="200"
Margin="451,211,715,527" />
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new ViewModel();
}
}
Input.cs
//InputType is enum
public static InputType GetType(DependencyObject obj)
{
return (InputType)obj.GetValue(TypeProperty);
}
public static void SetType(DependencyObject obj, InputType value)
{
obj.SetValue(TypeProperty, value);
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.RegisterAttached("Type", typeof(InputType), typeof(TextBox), new PropertyMetadata(default(InputType), OnTypeChanged));
private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is InputType)
{
var textBox = (TextBox)d;
var Type = (InputType)e.NewValue;
if (Type == InputType.Email || Type == InputType.URL)
{
textBox.LostFocus += OnLostFocus;
}
else
{
textBox.TextChanged += OnTextChanged;
}
}
}
ViewModel.cs
public class ViewModel : BindableBase
{
public ViewModel()
{
TextboxInputTypeList = Enum.GetValues(typeof(InputType)).Cast<InputType>();
}
private InputType _SelectedTextboxInputType = InputType.Currency;
public InputType SelectedTextboxInputType
{
get { return _SelectedTextboxInputType; }
set { this.SetProperty(ref this._SelectedTextboxInputType, value); }
}
private IEnumerable<InputType> _TextboxInputTypeList;
public IEnumerable<InputType> TextboxInputTypeList
{
get { return _TextboxInputTypeList; }
set { this.SetProperty(ref this._TextboxInputTypeList, value); }
}
}

This is a pretty common mistake. The problem is, binding targets cannot be CLR properties in XAML. It's just the rules. A binding source can be a CLR property, just fine. The targets simply must be dependency properties.
We all get the error! :)
I describe the whole thing here: http://blogs.msdn.com/b/jerrynixon/archive/2013/07/02/walkthrough-two-way-binding-inside-a-xaml-user-control.aspx
Best of luck.

Incorrect
public static readonly DependencyProperty TypeProperty =
DependencyProperty.RegisterAttached("Type", typeof(InputType), typeof(TextBox), new PropertyMetadata(default(InputType), OnTypeChanged));
Correct
public static readonly DependencyProperty TypeProperty =
DependencyProperty.RegisterAttached("Type", typeof(InputType), typeof(Input), new PropertyMetadata(default(InputType), OnTypeChanged));

Related

Image source based on properties value in Xamarin.Forms

I've got a custom MyCachedImage that inherits from FFImageLoading.Forms.CachedImage, which is used in a ListView to display images.
The source of this image is composed by 2 properties: a custom object as entity and an integer as size.
Let's say if entity is a "city" object and size is 10 then the image source will be "http://..../city/10/image.png"
Image source must be setted only when both properties are valorized.
So, my answer is, how and when create the source url?
MyCachedImage.vb
public class MyCachedImage : CachedImage
{
public static readonly BindableProperty EntityProperty =
BindableProperty.Create(nameof(Entity), typeof(MyObject), typeof(MyCachedImage));
public MyObject Entity
{
get { return (MyObject)GetValue(EntityProperty); }
set { SetValue(EntityProperty, value); }
}
public static readonly BindableProperty SizeProperty =
BindableProperty.Create(nameof(Size), typeof(int), typeof(MyCachedImage), defaultValue: 0);
public int Size
{
get { return (int)GetValue(SizeProperty); }
set { SetValue(SizeProperty, value); }
}
public MyCachedImage()
{
??? set source here?
}
protected override void OnBindingContextChanged()
{
??? set source here?
}
}
MyPage.xaml
<ListView ....>
....
<control:MyCachedImage Size="10"
Entity="{Binding MyObject}"
WidthRequest="40"
HeightRequest="40" />
....
</ListView>
I was wondering on when create that string and I found the right solution.
The OnBindingContextChanged is called when all properties are setted, so:
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (_source == string.Empty)
{
Source = Helpers.ImageHelper.UriFromEntity(Entity, ImageSize);
}
}

Xamarin.Forms change UI language at runtime (XAML)

I am using Strings.resx, Strings.de.resx, etc. to localize Xamarin.Forms app.
I need to be able to change interface language at run time, and it (allmost) works.
Xamarin generates static class Strings in namespace MyProject.Resources from resource files, and I use those values to display strings on UI.
When doing it from code, it works flawlessly:
await DisplayAlert(Strings.lblConfirmDelete, Strings.lblDeleteMessage, Strings.lblOK, Strings.lblCancel));
Problem is - not all attributes defined this way from XAML are updated when I change UI culture during runtime.
Buttons, Labels, Entry properties (Placeholder etc.) change as they should, but PageTitle, Toolbaritems, and some other properties remain in previous language.
I presume that some of these are populated when Page is first created, and are not updated on culture (and UI culture) change.
So, basically, I need a way to combine {DynamicResource ...} with values from resources.
I know that DynamicResource is ment to be used with Resource dictionary, but that is not a good way to store language translations for localization.
I tried
Text="{DynamicResource {x:Static lr:Strings.lblAddNew}}"
also not working.
Is there a way of refreshing page dynamicaly?
I also tried calling
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(MainListPage));
from Appearing event for that page, but that also does not work.
Any ideas?
Part of XAML file
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyProject.View"
xmlns:rs="clr-namespace:MMPI"
x:Class="MyProject.MainListPage"
xmlns:lr="clr-namespace:MyProject.Resources"
Title="{x:Static lr:Strings.appName}"
>
<ContentPage.ToolbarItems>
<ToolbarItem
Name="New"
Order="Primary"
Priority="0"
Text="{x:Static lr:Strings.lblAddNew}"
Clicked="New_Clicked"
>
When i encountered that challenge in a project I resolved it by using a simple class ResourceLoader and making use of INotifyPropertyChanged.
You can access the Instanceproperty from anywhere and change the culture. All String that are bound to the index would update.
The ResourceManager instance injected into the constructor must be set up appropriately.
public class ResourceLoader : INotifyPropertyChanged
{
private readonly ResourceManager manager;
private CultureInfo cultureInfo;
public ResourceLoader(ResourceManager resourceManager)
{
this.manager = resourceManager;
Instance = this;
this.cultureInfo = CultureInfo.CurrentUICulture;
}
public static ResourceLoader Instance { get; private set; }
public string GetString(string resourceName)
{
string stringRes = this.manager.GetString(resourceName, this.cultureInfo);
return stringRes;
}
public string this[string key] => this.GetString(key);
public void SetCultureInfo(CultureInfo cultureInfo)
{
this.cultureInfo = cultureInfo;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
public event PropertyChangedEventHandler PropertyChanged;
}
To display the localized strings in your application you need to bind through the indexer like so:
<Label Text="{Binding [Test], Source={x:Static ResourceLoader.Instance}}" />
Since it is now bound it should update when you call ResourceLoader.SetCultureInfo because the Item[] 'PropertyName' is causing bound controls to re-fetch the values to their bound keys.
Update
I just tested it if i was talking bogus and for some reason the property changed didn't work. I've added a different approach below, which is close to what i'm using in production i urge you to add some kind of weak reference 'caching' instead of the simple list holding all the string resources (otherwise they will be kept forever)
I'm keeping above for reference.
public class ResourceLoader
{
public ResourceLoader(ResourceManager resourceManager)
{
this.manager = resourceManager;
Instance = this;
this.cultureInfo = CultureInfo.CurrentUICulture;
}
private readonly ResourceManager manager;
private CultureInfo cultureInfo;
private readonly List<StringResource> resources = new List<StringResource>();
public static ResourceLoader Instance { get; private set; }
public StringResource this[string key] {
get { return this.GetString(key); }
}
public StringResource GetString(string resourceName)
{
string stringRes = this.manager.GetString(resourceName, this.cultureInfo);
var stringResource = new StringResource(resourceName, stringRes);
this.resources.Add(stringResource);
return stringResource;
}
public void SetCultureInfo(CultureInfo cultureInfo)
{
this.cultureInfo = cultureInfo;
foreach (StringResource stringResource in this.resources) {
stringResource.Value = this.manager.GetString(stringResource.Key, cultureInfo);
}
}
}
StringResource:
public class StringResource : INotifyPropertyChanged
{
public StringResource(string key, string value)
{
this.Key = key;
this.Value = value;
}
private string value;
public string Key { get; }
public string Value {
get { return this.value; }
set {
this.value = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML Binding
<Label Text="{Binding [ResourceKey].Value, Mode=OneWay, Source={x:Static local:ResourceLoader.Instance}}"
/>
Update 2
Came across this link where they implemented it similarly to my first approach. Maybe you can give it a try.
Update 3
Fixed the first approach. Both are working now. What was needed was this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null)); instead of this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Item[]));
I solved it very similar to #woelliJ . I just wanted to have key as strongly types from static class and binding should be in code behind.
ITranslationService is singleton from static variable. It is very close like #woelliJ .
[ContentProperty("Text")]
public sealed class TranslateExtension : IMarkupExtension<BindingBase>
{
private readonly ITranslationService? _translationService;
public TranslateExtension()
{
_translationService = Mobile.App.TranslationService;
}
public string? Text { get; set; }
public BindingBase ProvideValue(IServiceProvider serviceProvider)
{
var translationItem = _translationService[Text];
var binding = new Binding
{
Mode = BindingMode.OneWay,
Path = $"Value",
Source = translationItem,
};
return binding;
}
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
{
return (this as IMarkupExtension<BindingBase>).ProvideValue(serviceProvider);
}
}
[AddINotifyPropertyChangedInterface]
public class TranslationItem
{
public string? Key { get; set; }
public string? Value { get; set; }
}
Then label would be like this
<Label FontSize="Title" Text="{services:Translate Text={x:Static models:M.AboutTestInfoTitle}}" />

XAML Binding - App.cs

Question about binding in XAML with WP8.
In my App.cs I declare a public property for class Setting. In other xaml pages I need to access that propery and pass that property to a ConverterParameter. I can't say I've found a clean way of doing this. Below is my current method of how I accomplish this, but it just feels dirty. Any other ways out there?
So what's happening with code below? In app the settings data gets loaded. Any time the settings gets loaded or the a setting changes it Removes/Adds App.Current.Resource. This then allows me to data bind it {StaticResource {resourceName}}
Again, this works 100%...but is there a better/another way to accomplish this?
App.cs
private static Settings _settings = null;
public static Settings Settings
{
get { return _settings; }
private set { _settings = value; }
}
private async void Application_Launching(object sender, LaunchingEventArgs e)
{
if (Settings == null)
Settings = await FlightPath.Core.Data.LoadSettingsAsync();
App.Current.Resources.Add("Settings", App.Settings);
Settings.SettingsChanged += Settings_SettingsChanged;
}
private void Settings_SettingsChanged(object sender, EventArgs e)
{
if (App.Current.Resources["Settings"] == null)
App.Current.Resources.Add("Settings", App.Settings);
else
{
App.Current.Resources.Remove("Settings");
App.Current.Resources.Add("Settings", App.Settings);
}
}
Application Page XAML using Converter / ConverterParameter
<TextBlock Text="{Binding observation_time,
Converter={StaticResource ZuluToLocalTimeConverter},
ConverterParameter={StaticResource Settings}}"
Style="{StaticResource PhoneTextNormalStyle}"
Margin="-4,0,0,0"/>
if you are using MVVM you can Create a SettingManager class which having a Singleton instance. Then declare its propert in ViewModelBase class. Finally use it into your xaml code
XAML
C#
class ViewModelBaseClass: InotifyPropertyChanged
{
public SettingManager Settings{get{return SettingManager.Instance;}}
}
class SettingManager
{
public static Instance{get{...}}
public string this[string sName]
{
return "whatever you need";
}
}
class MYViewModel: ViewModelBase
{
}

How to bind a string from singleton in xaml (WinRT / C#)

i've created a Singleton which contains a string.
Now i want to bind this string to a TextBlock an Xaml.
<TextBlock Visibility="Visible" Text="{Binding singleton.Instance.newsString, Mode=TwoWay}"/>
When i run the WinRT App, the TextBlock-Text-String is empty.
EDIT 1:
Now it runs. But when i change the string in the singleton the TextBlock does not update.
Here is the c# code from my singleton
namespace MyApp
{
public sealed class singleton : INotifyPropertyChanged
{
private static readonly singleton instance = new singleton();
public static singleton Instance
{
get
{
return instance;
}
}
private singleton() { }
private string _newsString;
public string newsString
{
get
{
if (_newsString == null)
_newsString = "";
return _newsString;
}
set
{
if (_newsString != value)
{
_newsString = value;
this.RaiseNotifyPropertyChanged("newsString");
}
}
}
private void RaiseNotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
In my code behind the xaml i do this
singleton.Instance.newsString = "Breaking news before init";
this.Resources.Add("newsStringResource", singleton.Instance.newsString);
this.InitializeComponent();
singleton.Instance.newsString = "Breaking news AFTER init";
and in the xaml i bind the Resource with
<TextBlock Visibility="Visible" Text="{StaticResource newsStringResource}" />
With this code the TextBlock shows "Breaking news before init".
Whats wrong now?
Add your singleton to app resources using code behind before the TextBlock is constructed and reference the singleton by key.

Any WinRT iCommand/CommandBinding implementaiton samples out there?

Abstracting commands into the View Model is a valuable practice with XAML/MVVM projects. I get that. And, I see ICommand in in WinRT; but, how do we implement it? I haven't found a sample that actually works. Anyone know?
My all time favorite has to be the DelegateCommand provided by the Microsoft Patterns and Practices team. It allows you to create a typed command:
MyCommand = new DelegateCommand<MyEntity>(OnExecute);
...
private void OnExecute(MyEntity entity)
{...}
It also provides a way to raise the CanExecuteChanged event (to disable/enable the command)
MyCommand.RaiseCanExecuteChanged();
Here's the code:
public class DelegateCommand<T> : ICommand
{
private readonly Func<T, bool> _canExecuteMethod;
private readonly Action<T> _executeMethod;
#region Constructors
public DelegateCommand(Action<T> executeMethod)
: this(executeMethod, null)
{
}
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#endregion Constructors
#region ICommand Members
public event EventHandler CanExecuteChanged;
bool ICommand.CanExecute(object parameter)
{
try
{
return CanExecute((T)parameter);
}
catch { return false; }
}
void ICommand.Execute(object parameter)
{
Execute((T)parameter);
}
#endregion ICommand Members
#region Public Methods
public bool CanExecute(T parameter)
{
return ((_canExecuteMethod == null) || _canExecuteMethod(parameter));
}
public void Execute(T parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged(EventArgs.Empty);
}
#endregion Public Methods
#region Protected Methods
protected virtual void OnCanExecuteChanged(EventArgs e)
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, e);
}
}
#endregion Protected Methods
}
Check out RelayCommand class (only METRO code). The NotifyPropertyChanged class can be found here. The NotifyPropertyChanged class is only used to allow bindings on CanExecute and update it with RaiseCanExecuteChanged.
The original relay command class can be found here
Unfortunately there does not seem to be a native class that implements it for you. The interface is not overly complicated if you want to implement it yourself, and the popular MVVM Lite toolkit includes its own version of RelayCommand. You can add MVVM Lite to your project by right-clicking on References and choosing "Manage NuGet Packages". If you don't have this option, enable Nuget under Tools -> Extensions and Updates.
I've been looking for a minimal end-to-end implementation of a XAML-MVVM command, and not found it yet.
So, following #Rico's answer I ended up with the following as a minimal RelayCommand which works. I use a similar minimal version in a large project.
public class RelayCommand : System.Windows.Input.ICommand {
readonly Action<object> execute;
public RelayCommand(Action<object> execute) {
this.execute = execute;
}
public bool CanExecute(object parameter) {
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) {
this.execute(parameter);
}
}
The larger RelayCommand class seems to provide more control over CanExecute and CanExecuteChanged, but you don't need that to get started - and you may not need it at all.
To use it in a view model:
class ViewModel : INotifyPropertyChanged {
<< ... snip VM properties and notifications ...>>
public RelayCommand DoSomethingCommand {
get {
return new RelayCommand(param => {
this.DoSomething(param as AType);
Debug.WriteLine("Command Executed");
});
}
}
}
(We don't need the INotifyPropertyChanged for the Command, but any view model typically implements it.)
Finally, the XAML...
<Grid>
<!-- Set the data context here, for illustration. -->
<Grid.DataContext>
<local:ViewModel/>
</Grid.DataContext>
<!-- A sample control bind to a property -->
<TextBlock
Text="{Binding AProp}"/>
<!-- Bind a command -->
<Button Command="{Binding DoSomethingCommand}" CommandParameter="foo">Change!</Button>
</Grid>
I found this really good example at
https://code.msdn.microsoft.com/windowsapps/Working-with-ICommand-690ba1d4
<Page.Resources>
<local:MyCommandsCollection x:Key="MyCommands" />
</Page.Resources>
<Button Width="280"
Height="59"
Margin="513,280,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Command="{Binding MyFirstCommand}"
CommandParameter="{Binding Text,
ElementName=myTextBox}"
Content="Execute Command" />
public class MyCommandsCollection
{
public MyCommand MyFirstCommand
{
get { return new MyCommand(); }
}
}
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public async void Execute(object parameter)
{
MessageDialog message = new MessageDialog(
"The command is executing, the value of the TextBox is " + parameter as String);
await message.ShowAsync();
}
}
I tried this out with x:Bind and it works nicely. All I need is to expose a property in my ViewModel that returns a new Instance of the "MyCommand" class and it's all good.
Since I'm setting the DataContext in my XAML, I didn't need to mess with any of the "MyCommandCollection" stuff. Yay compiled binding.