Is it correct that it is not currently possible to bind to any Nullable<T> in Universal XAML Apps?
I found this link from 2013:
https://social.msdn.microsoft.com/Forums/en-US/befb9603-b8d6-468d-ad36-ef82a9e29749/textbox-text-binding-on-nullable-types?forum=winappswithcsharp
Stating that:
Binding to nullable values isn't supported in Windows 8 Store apps. It just didn't make it into this release. We've already got bugs on this behavior for v.Next.
But can it really be that this has not been fixed yet?
My Binding:
<TextBox Text="{Binding Serves, Mode=TwoWay}" Header="Serves"/>
My Property:
public int? Serves
{
get { return _serves; ; }
set
{
_serves = value;
OnPropertyChanged();
}
}
And the error I get in my output:
Error: Cannot save value from target back to source.
BindingExpression:
Path='Serves'
DataItem='MyAssembly.MyNamespace.RecipeViewModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'; target element is 'Windows.UI.Xaml.Controls.TextBox' (Name='null'); target property is 'Text' (type 'String').
Seems like it's not fixed. As XAML is using a build-in converter, in this case you can probably exchange it with your own, dealing with nullables:
XAML:
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel.Resources>
<local:NullConverter x:Key="NullableIntConverter"/>
</StackPanel.Resources>
<TextBox Text="{Binding Serves, Mode=TwoWay, Converter={StaticResource NullableIntConverter}}" Header="Serves"/>
</StackPanel>
Code behind:
public class NullConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{ return value; }
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
int temp;
if (string.IsNullOrEmpty((string)value) || !int.TryParse((string)value, out temp)) return null;
else return temp;
}
}
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private int? _serves;
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
public int? Serves
{
get { return _serves; }
set { _serves = value; RaiseProperty("Serves"); }
}
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
}
Related
I have a decimal property called TG
public class Dados_Pessoa : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public decimal TG { get; set; }
// ...
}
public class Pessoa : INotifyPropertyChanged
{
public Pessoa Propriedades { get; set; }
// ...
}
I put on the XAML:
<TextBox Header="TG" HorizontalAlignment="Left" Margin="145,416,0,0" VerticalAlignment="Top" Width="224"
Text="{Binding Path=Pessoa.Propriedades.TG, Mode=TwoWay}"
/>
When I change the TextBox value and move to other field, this error appears in Visual Studio 2017 output:
Error: Cannot save value from target back to source.
BindingExpression: Path='Pessoa.Propriedades.TG'
DataItem='Entidades.Formularios.FormFichaCadastro'; target element is
'Windows.UI.Xaml.Controls.TextBox' (Name='null'); target property is
'Text' (type 'String').
If I change the decimal to double it works fine, as expected.
I want to use decimal to have more precision in the numbers.
Why is this behaviour and how to fix this?
I solved it by creating a Converter for these fields that I Bind to Decimal data type.
public class DecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
Decimal.TryParse((string)value, out decimal result);
return result;
}
}
Then I declared it
<Page.Resources>
<local:DecimalConverter x:Key="DecimalConverter" />
</Page.Resources>
and used :
<TextBox Header="TG" HorizontalAlignment="Left" Margin="145,416,0,0" VerticalAlignment="Top" Width="224"
Text="{Binding Path=Pessoa.Propriedades.TG, Mode=TwoWay, Converter={StaticResource DecimalConverter}}"
/>
I found some silimar questions, but these are not exactly what i need.
I want to bound stackpanel "IsEnabled" value to bool "!IsIterrupted" value of my Items. Here is my XAML file:
<ListView ItemsSource="{Binding Path=Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<Button Command="{Binding Path=StopThreadCommand, Source={StaticResource viewModel}}" CommandParameter="{Binding Id}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is how items looks like:
public class ThreadDecorator : BaseThread , INotifyPropertyChanged
{
...
public event PropertyChangedEventHandler PropertyChanged;
private bool _is_interrupted;
public bool IsInterrupted
{
get { return _is_interrupted; }
set
{
_is_interrupted = value;
OnPropertyChanged("IsInterrupted");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
}
And my ViewModel:
public class ThreadsViewModel : DependencyObject
{
private ThreadsModel _model;
public ThreadsModel Model
{
get { return _model; }
set
{
_model = value;
}
}
public ICollectionView Items
{
get { return (ICollectionView)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ICollectionView), typeof(ThreadsViewModel), new PropertyMetadata(null));
public StopThreadCommand StopThreadCommand { get; set; }
public ThreadsViewModel()
{
this.Model = new ThreadsModel();
Items = CollectionViewSource.GetDefaultView(Model.Threads);
this.StopThreadCommand = new StopThreadCommand(this);
}
public void InterruptThread(int id)
{
_model.InterruptThread(id);
}
}
StopThreadCommand:
public class StopThreadCommand : ICommand
{
public ThreadsViewModel ViewModel {get; set;}
public StopThreadCommand(ThreadsViewModel viewModel)
{
this.ViewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
this.ViewModel.InterruptThread((int)parameter);
}
}
When I am clicking on Stop button, IsInterrupted value is changing from false to true, and stackpanel have to become disabled, but UI does not update. Help please!
The default property of Binding is Path, which is a path to a property/sub-property of the DataContext. It's not an arbitrary C# expression. So you're setting Binding.Path to "!IsInterrupted". !IsInterrupted won't evaluate to the boolean inverse of IsInterrupted; it won't evaluate to anything. It'll get you this in the debug output stream:
System.Windows.Data Error: 40 : BindingExpression path error: '!IsInterrupted' property not found on 'object' 'ThreadDecorator' blah blah blah
<StackPanel
IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
One way to do this is to write a boolean-inverse value converter (stolen verbatim from Chris Nicol's answer at the other end of that link):
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
Usage:
<UserControl.Resources>
<local:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>
<!-- stuff etc. -->
IsEnabled="{Binding Path=IsReadOnly, Converter={StaticResource InverseBooleanConverter}}"
You could also write a Style with a DataTrigger that sets IsEnabled to False if IsInterrupted is true.
In WPF project I use MVVM pattern.
I try to bind an item in a collection to UserControl but everything gets default value of DependcyProperty.
The Window xaml:
<ListView VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
ItemsSource="{Binding Sessions}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="DebugTextBlock" Background="Bisque"
Text="{Binding Connection}"/>
<usercontrol:SessionsControl Model="{Binding Converter=
{StaticResource DebugConverter}}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Where Sessions is
private ObservableCollection<SessionModel> _sessions;
public ObservableCollection<SessionModel> Sessions
{
get { return _sessions; }
set
{
if (Equals(value, _sessions)) return;
_sessions = value;
OnPropertyChanged("Sessions");
}
}
The SessionModel:
public class SessionModel:ViewModelBase
{
private string _connection;
public string Connection
{
get { return _connection; }
set
{
if (value == _connection) return;
_connection = value;
OnPropertyChanged("Connection");
}
}
}
In the SessionsControl I create DependencyProperty:
//Dependency Property
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(SessionModel),
typeof(SessionsControl), new PropertyMetadata(new SessionModel("default_from_control")));
// .NET Property wrapper
public SessionModel Model
{
get { return (SessionModel)GetValue(ModelProperty); }
set { if (value != null) SetValue(ModelProperty, value); }
}
and use this xaml to display connection in form:
<TextBlock Name="DebugControlTextBlock" Background="Gray" Text="{Binding Connection}"/>
So, when I run application
var windowModel = new WindowsModel();
var window = new SessionWindow(windowModel);
window.ShowDialog();
I always get default_from_control value in DebugControlTextBlock, but in DebugTextBlock get the_real_connection
Even if I set breakpoint in DebugConverter I see that value is default.
The DebugConverter is simply wrapper to check correct binding:
public class DebugConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("DebugConverter: " + (value!=null?value.ToString():"null"));
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
See solution on github.
So, what happend when I Binding model to DependcyProperty?
I would suggest that you try to make you Sessions property a DependencyProperty too. Otherwise you might have to manually Raise PropertyChanged for Sessions property.
I have the below visibility binding that is throwing:
System.Windows.Data Error: 40 : BindingExpression path error: 'Vis'
property not found on 'object' ''LoginViewModel' (HashCode=22943289)'.
BindingExpression:Path=Vis; DataItem='LoginViewModel'
(HashCode=22943289); target element is 'Login' (Name=''); target
property is 'Visibility' (type 'Visibility')
Can't see what I've done wrong, the property does exist in the MainViewModel. Maybe I'm going about showing and hiding this the wrong way.
<Window x:Class="Bt.MainWindow"
xmlns:vm="clr-namespace:Bt"
xmlns:ctrls="clr-namespace:Bt.Controls">
<Window.DataContext>
<vm:MainViewModel x:Name="MWin" />
</Window.DataContext>
<Grid>
<ctrls:Login Visibility="{Binding Vis}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ctrls:Login>
</Grid>
</Window>
ViewModel:
namespace Bt
{
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
Vis = "Collapsed";
}
private string _vis = "Collapsed";
public string Vis
{
get { return _vis; }
set
{
_vis = value;
RaisePropertyChanged("Vis");
}
}
}
}
[EDIT] Capturing inside the User Control, when the User Control's visibility is changed in the Main window.
I realize that the converter is not being called correctly, so may need some help there as well. As for the rest hopefully you can see what I'm trying to achieve.
View:
<UserControl x:Class="Bt.Controls.Login"
xmlns:app="clr-namespace:Bt"
xmlns:viewmodel="clr-namespace:Bt.Controls"
mc:Ignorable="d"
Visibility="{Binding Visi,Converter={StaticResource BooleanToVisibilityConverter}}"
>
</UserControl>
View Model:
namespace Bt.Controls
{
class LoginViewModel : INotifyPropertyChanged
{
public LoginViewModel(){}
private bool _visi = true;
public bool Visi
{
get { return _visi; }
set
{
_visi = value;
RaisePropertyChanged("Visi");
MessageBox.Show("Visi set");
reset_timer(_visi);
}
}
}
[ValueConversion(typeof(bool), typeof(Visibility))]
public class VisibilityConverter : IValueConverter
{
public const string Invert = "Invert";
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException("The target must be a Visibility.");
bool? bValue = (bool?)value;
if (parameter != null && parameter as string == Invert)
bValue = !bValue;
return bValue.HasValue && bValue.Value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
}
#endregion
}
}
As evident from error BindingEngine is looking for property Vis in LoginViewModel and not in MainViewModel. (You must have set DataContext for your Login UserControl to LoginViewModel).
You need to get Window's DataContext which you can get using RelativeSource:
<ctrls:Login Visibility="{Binding DataContext.Vis,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
Also you should create Vis as bool and use BooleanToVisibility converter in your binding.
<Grid>
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="BooelanToVisibilityConverter"/>
</Grid.Resources>
<ctrls:Login Visibility="{Binding DataContext.Vis,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}}",
Converter={StaticResource BooelanToVisibilityConverter}
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
I would discourage use of Visibility property in ViewModel because it's View thing which should not be there in ViewModel. Having bool is perfectly fine which you can always convert using converter.
Keep Vis as type of Visibility
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
}
private Visibility _vis = Visibility.Collapsed;
public Visibility Vis
{
get { return _vis; }
set
{
_vis = value;
RaisePropertyChanged("Vis");
}
}
}
Also specify the Source for the binding,
<Window x:Class="Bt.MainWindow"
xmlns:vm="clr-namespace:Bt"
xmlns:ctrls="clr-namespace:Bt.Controls">
<Window.Resources>
<vm:MainViewModel x:Key="MWin" />
</Window.Resources>
<Grid>
<ctrls:Login Visibility="{Binding Vis, RelativeSource={StaticResource MWin}}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ctrls:Login>
</Grid>
</Window>
As Rohit says, you could use BooleanToVisibility instead of changing the property as Visibility..
I'm currently learning how to develop and building an app for windows phone 7.
If a certain value is true, I need to add a TextBlock to the ListBox before a TextBlock (say its name is x:Name="dayTxtBx").
I am currently using
dayListBox.Items.Add(dayTxtBx);
to add the text box.
Any help very much appreciated!
Thanks
This is pretty easy to do if you're using a DataTemplate and a ValueConverter and passing the whole object into the ListBox (rather than just a string). Assuming you have some object that looks like:
public class SomeObject: INotifyPropertyChanged
{
private bool mTestValue;
public bool TestValue
{
get {return mTestValue;}
set {mTestValue = value; NotifyPropertyChanged("TestValue");}
}
private string mSomeText;
public string SomeText
{
get {return mSomeText;}
set {mSomeText = value; NotifyPropertyChanged("SomeText");}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if ((name != null) && (PropertyChanged != null))
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
You can make a converter that looks like:
public class BooleanVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && (bool)value)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And add the converter to your XAML like so:
<UserControl x:Class="MyProject.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<UserControl.Resources>
<local:BooleanVisibilityConverter x:Key="BoolVisibilityConverter" />
<UserControl.Resources>
Then you could have the ListBox defined in XAML like so:
<Listbox>
<Listbox.ItemTemplate>
<DataTemplate>
<StackPanel Orentation="Horizontal" >
<TextBlock Text="Only Show If Value is True" Visibility={Binding TestValue, Converter={StaticResource BoolVisibilityConverter}} />
<TextBlock Text="{Binding SomeText}" />
</StackPanel>
</DataTemplate>
</Listbox.ItemTemplate>
</Listbox>
Might seem like a lot, but it's really pretty simple once you get started. A great way to learn more about data binding and converters is at Jesse Liberty's blog ( http://jesseliberty.com/?s=Windows+Phone+From+Scratch ).