Cannot assign a null value via a TextBox Binding to Int32?.
If the TextBox is empty then Int32Null Set is not called.
A red border is around the TexBox indicating a validation exception.
This just does not make sense as Int32? is nullable. If the user removes the integer value from the TextBox I want the Set called so the property is assigned to null.
When it starts int32Null = null and the TextBox is not red.
I tried implementing Validation and set validation = true if the TextBox is empty. But Set is still not called and TextBox is red indicating a validation error.
It seems like I should be able to assign a null value to a nullable via binding.
<Window x:Class="AssignNull.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=Int32Null, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
<TextBox Grid.Row="2" Grid.Column="0" Text="{Binding Path=StringNull, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
</Grid>
</Window>
public partial class MainWindow : Window
{
private Int32? int32Null = null;
private string stringNull = "stringNull";
public MainWindow()
{
InitializeComponent();
}
public Int32? Int32Null
{
get { return int32Null; }
set { int32Null = value; }
}
public string StringNull
{
get { return stringNull; }
set { stringNull = value; }
}
}
Set StringNull does get called and value passed is not null but rather string.empty.
Since Set is not called on Int32Null I don't know what is getting passed.
It was also passing a string.empty to Int32?. Had to convert an empty string to null.
[ValueConversion(typeof(Int32?), typeof(String))]
public class Int32nullConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Int32? int32null = (Int32?)value;
return int32null.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string strValue = value as string;
if(string.IsNullOrEmpty(strValue.Trim())) return null;
Int32 int32;
if (Int32.TryParse(strValue, out int32))
{
return int32;
}
return DependencyProperty.UnsetValue;
}
}
You make a false assumption on how the type converters ought to handle this. So if they do not do what you want, namely turn an empty string into null you'll either have to write your own or use a Binding.Converter that does the conversion for you.
Related
Enable button_A when button_B is enabled and image source has a specific .png icon
I have two Buttons and an Image object in a WPF application built with .NET Core and C#. What I want on the bottom line is to enable Button_A only when the Button_B is enabled and the Image has a specific .png icon of a checkmark. For those three objects an MVVM model exists. More details in the code below,
XAML file
<Window x:Class="MyApp.MainWindow"
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:MyApp"
mc:Ignorable="d"
Height="1080"
Width="1920"
ResizeMode="NoResize">
<Grid x:Name="MyGrid"
Background="White"
HorizontalAlignment="Center"
ShowGridLines="False">
<!--Grid Columns-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<!--Grid Rows-->
<Grid.RowDefinitions>
<RowDefinition Height="45"/>
<RowDefinition Height="45"/>
</Grid.RowDefinitions>
<Button
x:Name="Button_A"
Click="Button_A_Click"
Content="Execute"
IsEnabled="{Binding EnableButtonA}"
HorizontalAlignment="Left"
Width="80"
Height="25"
Margin="135,0,0,0"
Grid.Column="0"
Grid.Row="0"/>
<Button
x:Name="Button_B"
Click="Button_B_Click"
Content="Execute"
IsEnabled="{Binding EnableButtonB}"
HorizontalAlignment="Left"
Width="80"
Height="25"
Margin="135,0,0,0"
Grid.Column="1"
Grid.Row="1">
</Button>
<Image
x:Name="IconSymbol"
Source="{Binding Path=ImageChangeSource,UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1"
Grid.Column="1"
Width="{Binding Path=CalculationsImageWidth}"
Height="Auto"
HorizontalAlignment="Left"
Margin="190,0,0,0"
Visibility="Visible"
IsEnabled="True"/>
</Grid>
</Window>
.CS file - MVVM model
namespace MyApp
{
public class CustomViewModel : INotifyPropertyChanged
{
//Button B
private bool _enableButtonB;
public bool EnableButtonB
{
get
{
return _enableButtonB;
}
set
{
_enableButtonB = value;
OnPropertyChanged("EnableButtonB");
}
}
//Image
private ImageSource _imageChangeSource;
public ImageSource ImageChangeSource
{
get
{
return _imageChangeSource;
}
set
{
_imageChangeSource = value;
OnPropertyChanged("ImageChangeSource");
}
}
//Image width
private int _changeImageWidth;
public int ImageWidth
{
get
{
return _changeImageWidth;
}
set
{
_changeImageWidth= value;
OnPropertyChanged("ImageWidth");
}
}
//Button A
private bool _enableButtonA;
public bool EnableButtonA
{
get
{
return _enableButtonA;
}
set
{
//What to write here?
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
What I tried so far is based on this similar question I asked in the past. A relevant answer posted in the attached question is the use of IMultiValueConverter. However, I am not confident to figure out how to properly use the Converter for my task.
(The code below won't work)
public class EnableReportConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)values[0]=true, (ImageSource)values[1]=new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative)));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
<Button
x:Name="Button_A"
Click="Button_A_Click"
Content="Execute"
HorizontalAlignment="Left"
Width="80"
Height="25"
Margin="135,0,0,0"
Grid.Column="0"
Grid.Row="0">
<Button.IsEnabled>
<MultiBinding>
<MultiBinding.Converter>
<local:EnableReportConverter/>
</MultiBinding.Converter>
<Binding Path="EnableButtonB"/>
<Binding Path="ImageChangeSource"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
[EDIT]--example of an ICommand
public ICommand ButtonACommand
{
get { return new DelegateCommand<object>(FuncBrowseFileCommand); }
}
public void FuncBrowseFileCommand(object parameters)
{
var final_result = BrowseFile(FilesFilePath);
Nullable<bool> browse_result = final_result.browse_result;
FilesFilePath = final_result.filename;
//below are some MVVM object-- dont pay them attention
if (browse_result == true)
{
EnableFilesLoadButton = true;
EnableFilesBrowseButton = true;
EnableFilesViewButton = false;
FilesPanelVisibility = false;
}
else
{
FilesImageVisibility = true;
return;
}
}
Better approach:
I understand "WHAT" you need but , I am not sure "WHY" you need this. There are better ways to enable a button. I also notice that you are using button click which is obviously code behind. When you have MVVM model, try to use ICommand and attach to the "command" property of the button. If you do that , then you can easily assign a delegate to "CanExecute" to make the enabling of the button.
Solution to current problem: Regardless of the above suggestion, solution to your current problem is as below.
The below line in your converter is wrong. This returns object[] again. Basically, you receive an array from the XAML and return the same again. You need to receive the array, process it and return a result (which is "bool" in your case : to enable a button).
return ((bool)values[0]=true, (ImageSource)values[1]=new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative)));
So, do the validation like below..
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//INCOMING DATA
bool is_buttonB_enabled = (bool)values[0]; //This is the first value in the object array.
ImageSource _image = (ImageSource) values[1]; //This is the second value in the object array.
//YOUR EXPECTED IMAGE.
ImageSource _expected_image = new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative));
//VALIDATE
return (is_buttonB_enabled == true && _image == _expected_image );
}
UPDATE:
In case, you use an ICommand (and return a delegatecommand), then you can follow below approach.
public ICommand ButtonACommand
{
get { return new DelegateCommand<object>(FuncBrowseFileCommand,_canEnableButton); }
}
private bool _canEnableButton(object obj)
{
//YOUR EXPECTED IMAGE.
ImageSource _expected_image = new BitmapImage(new Uri("/Assets/checkmark.png", UriKind.Relative));
return (EnableButtonB == true && ImageChangeSource == _expected_image );
}
then, you don't need converter..
I'm trying to bind a custom class to a group of 3 Radiobuttons in my WPF app. There should be three possibilities of the class being returned, depending on which button of the group is selected. So i.e.
public class RadioButtonResult
{
public bool Istrue {get; set;}
public string WhichOne {get; set;}
}
should be bound to the 3 radiobuttons in the sense that Button 1 returns
new RadioButtonResult { Istrue = false, WhichOne = "First"}
second one returns an Instance with Istrue = true, etc... I need this because there are 3 possible situations and the element has to bind to both a boolean property and a string property.
I tried using a converter
public class RadioButtonConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch (parameter)
{
case "First":
return new RadioButtonResult(false, "First");
case "Second":
return new RadioButtonResult(true, "Second");
case "Third":
return new RadioButtonResult(true, "First");
default:
return new RadioButtonResult(false, "None")
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{ return null; }
}
The radiobuttons themselves should have no text, so I'm not quite sure how to pass the converter parameter to even try this. (I didn't try the ConvertBack yet as I couldn't get the Convert to work)
<RadioButton GroupName="Group1" IsChecked="{Binding TestStatus, Converter=RadioButtonConverter, ConverterParameter="First"}"/>
I tried something like this, but it won't accept text as the parameter. How could I make this converter work?
I don't know how to bind both a string and a bool to a group of 3 Radiobuttons
You can have 6 properties in view model or reuse RadioButtonClass class (consider to implement INotifyPropertyChanged if you want to change values dynamically)
public class ViewModel
{
public RadioButtonResult Button1 { get; } = new RadioButtonResult(false, "First");
... // more buttons
}
<RadioButton GroupName="Group1"
IsChecked="{Binding Button1.Istrue}"
Content="{Binding Button1.WhichOne}" />
public MainWindow()
{
InitializeComponents();
DataContext = new ViewModel();
}
Rather than having a separate boolean property for each radio button, I'd suggest having a single enum value for each radio button group.
To do the binding, you would then need a converter between the enum value and the boolean IsChecked property of the radio button.
public class perValueEqualsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return parameter != null && parameter.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value != null && value.Equals(true) ? parameter : Binding.DoNothing;
}
}
The enum property is defined in the ViewModel ...
public enum MyEnum
{
Value1,
Value2,
Value3
}
public class MainViewModel : ViewModelBase
{
private MyEnum _e = MyEnum.Value2;
public MyEnum E
{
get => _e;
set => Set(nameof(E), ref _e, value);
}
}
... and then used as the binding source in the View, via a converter instance
<Window
...>
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Window.Resources>
<conv:perValueEqualsConverter x:Key="ValueEqualsConverter" />
</Window.Resources>
<Grid Margin="24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="16" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0"
Width=200>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<RadioButton
Grid.Row="0"
Content="Value 1"
IsChecked="{Binding E, Converter={StaticResource ValueEqualsConverter}, ConverterParameter={x:Static local:MyEnum.Value1}}" />
<RadioButton
Grid.Row="2"
Content="Value 2"
IsChecked="{Binding E, Converter={StaticResource ValueEqualsConverter}, ConverterParameter={x:Static local:MyEnum.Value2}}" />
<RadioButton
Grid.Row="4"
Content="Value 3"
IsChecked="{Binding E, Converter={StaticResource ValueEqualsConverter}, ConverterParameter={x:Static local:MyEnum.Value3}}" />
</Grid>
<Border
Grid.Row="0"
Margin="-8"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="8" />
<TextBlock
Grid.Row="2"
Text="{Binding E}" />
</Grid>
</Window>
I think your viewmodel structure should probably be more like:
public class RadioButtonResult
{
public bool Istrue {get; set;}
public string WhichOne {get; set;}
public List<OptionVM> Options {get; set;}
}
OptionVM would have two properties:
public bool Istrue {get; set;}
public string Descriptor {get; set;}
The setter of IsTrue should initiate some logic.
Hence:
private bool isTrue = false;
public bool Istrue
{ get => isTrue;
set { isTrue = value;
SetTheParentValue();
}
}
SetTheParentValue should be an Action which you inject to the viewmodel. This takes a reference to RadioButtonResult and makes istrue there true or false and sets WhichOne. So you also want a public Action SetTheParentValue on that.
And you should implement inotifypropertychanged.
No converter.
And your logic goes in the action so the classes are re-usable for other groups of radiobuttons.
We don't know enough about your overall structure to give advice on other aspects.
However.
Sets of radiobuttons are a repeating group and that's handled in wpf by binding the itemssource of an itemscontrol to a list or observablecollection of viewmodels. Here that would be of OptionVM. The data is then data templated out into radio buttons.
The ischecked property of the radio button would be bound to IsTrue and the content to Descriptor.
I'm implementing a user control in WPF using MVVM pattern. I want the control to contain an ItemsControl, specially a ComboBox, that contains a list of People. I want the first menu item to be labelled 'No Person' and bind to null on the data source, while the remaining items are names of people that bind to Person objects.
The code for Person and the view model is as follows:
namespace NoValueItem
{
public class Person : IEquatable<Person>
{
public int Id { get; set; }
public string Name { get; set; }
public static bool Equals(Person a, Person b)
{
if (a == null)
return b == null;
return a.Equals(b);
}
public bool Equals(Person other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(null, other))
return false;
return this.Name.Equals(other.Name);
}
public override bool Equals(object obj)
{
return Equals(obj as Person);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
public class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<Person> ListOfPersons { get; } =
new ObservableCollection<Person> {
null,
new Person() { Id = 1, Name = "Alice" },
new Person() { Id = 2, Name = "Bob" },
new Person() { Id = 3, Name = "Charlie" }
};
private Person _SelectedPerson;
public Person SelectedPerson
{
get
{
return _SelectedPerson;
}
set
{
if (!Person.Equals(value, _SelectedPerson)) {
_SelectedPerson = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class NullSubstituteConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value ?? parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null)
return value;
return parameter.Equals(value) ? null : value;
}
}
}
And the view:
<Window x:Class="NoValueItem.MainWindow"
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:local="clr-namespace:NoValueItem"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid d:DataContext="{d:DesignInstance local:ViewModel}" VerticalAlignment="Center">
<Grid.DataContext>
<local:ViewModel/>
</Grid.DataContext>
<ComboBox ItemsSource="{Binding ListOfPersons}"
SelectedItem="{Binding SelectedPerson}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
At run-time, I get 4 items as I would expect: 1 blank menu item followed by 3 non-blank menu items, one for each Person in the ViewModel.ListOfPersons collection, with the item text bound to the Name property of the Person.
I would like the first blank item to instead show the text 'No Person'. How can I do this?
One thing I've tried is using the following data converter, that converts a null reference to the object specified in the converter parameter:
namespace NoValueItem
{
public class NullSubstituteConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value ?? parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null)
return value;
return parameter.Equals(value) ? null : value;
}
}
}
I then made the following changes to the view:
Added the NullSubstituteConverter from above as a static resource.
Added Person object as a static resources to represent the 'No Person' item and gave it the key 'NullPerson'.
Set the NullSubstituteConverter resource as the Converter for the binding for the SelectedItem property of the ComboBox.
Set the NullSubstituteConverter resource as the Converter for items in the data template for the ComboBox, so that the null item in the items source is converted to an the NullPerson object.
Here's the updated view:
<Window x:Class="NoValueItem.MainWindow"
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:local="clr-namespace:NoValueItem"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<Grid d:DataContext="{d:DesignInstance local:ViewModel}" VerticalAlignment="Center">
<Grid.Resources>
<local:Person x:Key="NullPerson">
<local:Person.Id>0</local:Person.Id>
<local:Person.Name>No Person</local:Person.Name>
</local:Person>
<local:NullSubstituteConverter x:Key="NullSubstituteConverter"/>
</Grid.Resources>
<Grid.DataContext>
<local:ViewModel/>
</Grid.DataContext>
<ComboBox ItemsSource="{Binding ListOfPersons}"
SelectedItem="{Binding SelectedPerson,
Converter={StaticResource NullSubstituteConverter},
ConverterParameter={StaticResource NullPerson}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding Converter={StaticResource NullSubstituteConverter},
ConverterParameter={StaticResource NullPerson}}"
Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
This is closer to what I want. The blank menu item is now showing 'No Person', but there are still 2 problems:
When the view is first loaded, the 'No Person' item isn't automatically selected by default.
It's not possible to select the 'No Person' item.
I welcome any suggestions on how I can get the 'No Person' menu item working. It can be based on my approach above, or completely different approach as long as it works!
I'm going crazy with converters. I know that I must use it to change the "exit value" of my values, when needed, but I don't know how to use right for my case.
I have my simple MVVM (3 fields only) and my main window with a list of my items. The first item is calculated depending on a function, and can show YES or NOT, the other values are binded directly.
This is working well, but I need to change the background and foreground colors depending on the YES or NOT value I have in the first calculated field. For example:
YES (must be blue) - ITEM 1
NO (must be grey) - ITEM 2
YES (must be blue) - ITEM 3
While the internal values in my database are (in this case the calc is modulus):
2 - ITEM 1
3 - ITEM 2
4 - ITEM 3
My ListBox code is this:
<phone:PhoneApplicationPage
x:Class="Pasti.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="My App" Style="{StaticResource PhoneTextNormalStyle}" />
<TextBlock Text="My List" Style="{StaticResource PhoneTextTitle1Style}" />
</StackPanel>
<ListBox x:Name="lstPills" Grid.Row="1" ItemsSource="{Binding AllItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Width="440">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="HERE MUST GO THE CONVERTER, I SUPOSE">
<TextBlock Text="{Binding IsPair, Mode=TwoWay}"/>
</Border>
<TextBlock
Text="{Binding Name}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Grid.Column="1"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</phone:PhoneApplicationPage>
And the CS code is this for this page:
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Set the page DataContext property to the ViewModel.
this.DataContext = App.ViewModel;
}
}
For the calculated field, I added this to the Model (_myNumber holds the value I must check):
// Define a custom field based on some database values
// Get is calculated, while set will force it to refresh by Notifying
public string IsPair
{
get
{
return _myNumber % 2 == 0 ? "YES" : "NO";
}
set
{
NotifyPropertyChanged("IsPair");
}
}
NOTE: Because I don't know other way to force the list to refresh, I put the set property to only notify and the TwoWay Mode, and I just do a IsPair = "" when I want it to recalculate. If there are other way to do it, will be welcome.
So, with this info, how can I made a Converter that, based on my IsPair value, set the Background property of the Border to Blue or Grey? I saw a lot of Converter examples, but still don't get the point to do exactly this.
I suppose I must put something like this in the MainPage.cs, under the MainPage Class:
// Converter for the YES-NO column on the list
public class IsPairConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (MY_CALCULATED_VALUE == "YES")
return "Blue";
return "Grey";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But how to get the MY_CALCULATED_VALUE, and how to set the converter in the Background value of the Border?
So close!
First, bind the background to IsPair and use the converter:
<Border Background="{Binding IsPair, Converter={StaticResource IsPairConverter}}">
<TextBlock Text="{Binding IsPair, Mode=TwoWay}"/>
</Border>
In your converter, create a brush depending on the value:
public class IsPairConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// You might want to add additional checks for type safety
var calculatedValue = (string)value;
var color = calculatedValue == "YES" ? Colors.Blue : Colors.Gray;
return new SolidColorBrush { Color = color };
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And you're done.
If you want the value to be computed only one time, instead of every time IsPair is called, you can do the computation in the setter of MyNumber and assign it to IsPair:
private int myNumber;
public string IsPair { get; protected set; }
protected int MyNumber
{
get
{
return this.myNumber;
}
set
{
this.myNumber = value;
this.IsPair = value % 2 == 0 ? "YES" : "NO";
this.NotifyPropertyChanged("IsPair");
}
}
Environment: WinRt / XAML / C#
I am trying to do a two way binding for a float type property. However if that is a nullable type (float? or Nullable) the binding doesnt work.
I have event applied converters for this, and it still doesnt show any sign of bindability.
C#: ViewModel
public class MyViewModel : INotifyPropertyChanged
{
private float _quantity1;
public float Quantity1
{
get
{
return this._quantity1;
}
set
{
this._quantity1 = value;
RaisePropertyChanged("Quantity1");
}
}
private float? _quantity2;
public float? Quantity2
{
get
{
return this._quantity2;
}
set
{
this._quantity2 = value;
RaisePropertyChanged("Quantity2");
}
}
private Nullable<float> _quantity3;
public Nullable<float> Quantity3
{
get
{
return this._quantity3;
}
set
{
this._quantity3 = value;
RaisePropertyChanged("Quantity3");
}
}
public MyViewModel()
{
this.Quantity1 = 100.01F;
this.Quantity2 = 200.02F;
this.Quantity3 = 300.03F;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
C# : Converter:
public sealed class NullableFloatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return 0F;
else
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value != null)
return value;
else
return 0;
}
}
XAML:
<Page
x:Class="Test_Binding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test_Binding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:NullableFloatConverter x:Key="nullConverter" />
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Quantity1: " Width="150" />
<TextBox Text="{Binding Quantity1, Mode=TwoWay}" />
<TextBlock Text="{Binding Quantity1}" />
</StackPanel>
<!-- the second text block doesnt get an updated value -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="Quantity2: " Width="150"/>
<TextBox Text="{Binding Quantity2, Mode=TwoWay, Converter={StaticResource nullConverter}}" />
<TextBlock Text="{Binding Quantity2, Converter={StaticResource nullConverter}}" />
</StackPanel>
<!-- the second text block doesnt get an updated value -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="Quantity2 (No Converter): " Width="150"/>
<TextBox Text="{Binding Quantity2, Mode=TwoWay}" />
<TextBlock Text="{Binding Quantity2}" />
</StackPanel>
<!-- the second text block doesnt get an updated value -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="Quantity3: " Width="150"/>
<TextBox Text="{Binding Quantity3, Mode=TwoWay}" />
<TextBlock Text="{Binding Quantity3}" />
</StackPanel>
</StackPanel>
</Grid>
Only the First text block gets updated (i.e. for Quantity1). I cant get the others (Quantity2 & Quantity3) to get updated.
Any suggestions ?
I think the convertBack method wasnt upto scratch, which i got from stackoverflow.com/questions/15406336/databind-a-nullable-type-in-xaml-windows-8-store-app
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
string s = value as string;
float result;
if (!string.IsNullOrWhiteSpace(s) && float.TryParse(s, out result))
{
return result;
}
return null;
}