I have created a class named "BoundProperty" which contains a property "Value".
Binding to a property which is an instance of that class looks this way
(Age is a BoundProperty):
<TextBox Text="{Binding MyModel.Age.Value, Mode=TwoWay}" />
Is there a way to make the binding look this way and additionally still retain it two-way?
<TextBox Text="{Binding MyModel.Age, Mode=TwoWay}" />
I can't use implicit/explicit conversion operators, for this "BoundProperty" initialization requires special parameters, which need to be copied from original object.
Thanks,
AD
If Value is public you can use ValueConverter:
public class BoundPropertyConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var age = value as BoundProperty;
if (age == null)
return string.Empty;
return age.Value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int age;
if (int.TryParse(value.ToString(), out age))
return new BoundProperty() {Value = age};
return null;
}
}
Then in xaml define namespace to this converter
xmlns:converters="clr-namespace:Your.Namespace"
And then in Resources area write something like this:
<converters:BoundPropertyConverter x:Key="BoundPropertyConverter"/>
And last but not least:
<TextBox Text="{Binding MyModel.Age, Mode=TwoWay, Converter={StaticResource BoundPropertyConverter}" />
Related
I'm trying to implement the MVVM design pattern in my WPF application but I have some problems to bind my Views with my ViewModels.
In one of my ViewModels, I have the following property :
public IPEndPoint EndPoint
{
get { return _serverInfos.EndPoint; }
private set
{
_serverInfos.EndPoint = value;
RaisePropertyChanged("EndPoint");
}
}
I want to bind this property in the related View like that :
<TextBox Text="{Binding EndPoint.Address}" />
<TextBox Text="{Binding EndPoint.Port}" />
The EndPoint.Port binding works as expected but the other one doesn't because EndPoint.Address is not a string (it's an IPAddress). Of course, I could define two string properties instead of one IPEndPoint but I think that it's not a good solution.
I also have the same problem using Enums when I want to convert them into int :
<ComboBox SelectedIndex="{Binding MyEnumProperty}" />
How could I solve these problems ?
Thank you for your help.
Normally you want a view model to take things from the model and expose them in a way that the view can consume. As such
MyEnumProperty should be an System.Int32 (int) for ComboBox.SelectedIndex to consumer
You should probably implement two separate properties for EndPointPort and EndPointAddress, and EndPointAddress should be a string that converts to an IPAddress when working with the model
You can use IValueConverters for both of those, but then you are reducing some of the utility of a separate view model in the first place if all it does is act like the model.
A converter you can use for converting between IPAddress and string.
public class IPAddressConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var ipAddress = value as IPAddress;
if (ipAddress != null)
{
return ipAddress.ToString();
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var text = value as string;
IPAddress ipAddress;
if (text != null && IPAddress.TryParse(text, out ipAddress))
{
return ipAddress;
}
return DependencyProperty.UnsetValue;
}
}
And then in a ResourceDictionary or a Resources collection of a FrameworkElement
<IPAddressConverter x:Key="IpAddressConverter" />
And in the binding:
<TextBox Text="{Binding EndPoint.Address, Converter={StaticResource IpAddressConverter}}" />
As far as the IPEndPoint type, this is a perfect case for a type converter (IValueConverter). It would look something like this, assuming your type has a valid ToString implementation:
public class IPEndPointConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
IPEndPoint endPoint = (IPEndPoint)value;
return endPoint.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You could then add the converter to your XAML file with a ResourceDictionary as follows (assuming you've added the relevant namespace, here called "converters", to your XAML):
<Window.Resources>
<converters:IPEndPointConverter x:Key="ipEndPointConverter" />
</Window.Resources>
You would then simply use this anywhere you need it within your XAML by adding it to the binding:
<TextBox Text="{Binding Path=EndPoint.Address, Converter={StaticResource ResourceKey=ipEndPointConverter}}" />
I Silverlight5 with mvvm pattern i have one doubt.
In xaml i have used one textblock and i bind some id in it.
If the textblock content value is 1 or 2 means
yet another textbox is visible or else that is collapsed.. how to acheive that..
my code:
<TextBlock Name="textBlock1" Text="{Binding id}" Loaded="textBlock1_Loaded" Visibility="Collapsed" />
<TextBox Text="{Binding name,Mode=TwoWay}" x:Name="t1" Visibility="{Binding IsVisible,Converter={StaticResource visibilityconverter}}" />
in view model i had created the property for id and raised the event and bind the value to textblock.
to convert the value to visible i have a visibilityconverter class in one separate folder named "Converters"
public class visibilityconverter:IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (id==1 && id==2)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
in the above visibleconverter class how i cna get the id value from viewmodel and check it..
If i got the value from viewmodel to visibilityconverter means i will proceed further.
tell me if u can..!
Hi i have found the solution..
In xaml give the following:
<TextBox Text="{Binding name,Mode=TwoWay}" x:Name="t1" Visibility="{Binding id,Converter={StaticResource visibilityconverter}}" />
In visibilityConverter class:
public class visibilityconverter:IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
string v = value.ToString();
if (v =="1" || v=="2")
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Problem Solved... !
Public Class View
Public Property Items As String() = {"One", "Two", "Three"}
Public Property Index As Integer = 0
End Class
It's instance is set as DataContext of this XAML:
<Window>
<StackPanel>
<ListBox ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding Items[Index]}"/>
</StackPanel>
</Window>
But this doesn't work.
<Label Content="{Binding Items[{Binding Index}]}"/>
This neither.
<Label Content="{Binding Items[0]}"/>
This works.
Is there any solution except making extra property in view? Something directly in XAML?
I'm afraid it's not possible without some code-behind, but using reflection and dynamic, you can create a converter that can do this (it would be possible without dynamic, but more complex):
public class IndexerConverter : IValueConverter
{
public string CollectionName { get; set; }
public string IndexName { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Type type = value.GetType();
dynamic collection = type.GetProperty(CollectionName).GetValue(value, null);
dynamic index = type.GetProperty(IndexName).GetValue(value, null);
return collection[index];
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Put following into resources:
<local:IndexerConverter x:Key="indexerConverter" CollectionName="Items" IndexName="Index" />
and use it like this:
<Label Content="{Binding Converter={StaticResource indexerConverter}}"/>
EDIT: The previous solution doesn't update properly when the values change, this one does:
public class IndexerConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
return ((dynamic)value[0])[(dynamic)value[1]];
}
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
In resources:
<local:IndexerConverter x:Key="indexerConverter"/>
Usage:
<Label>
<MultiBinding Converter="{StaticResource indexerConverter}">
<Binding Path="Items"/>
<Binding Path="Index"/>
</MultiBinding>
</Label>
What you write in the binding markup extension is assigned to the Path property by default, this property is a string so any dynamic content you refer to inside it will not be evaluated. There is no simple XAML-only method to do what you try to do.
Why don't use this:
<StackPanel>
<ListBox Name="lsbItems" ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding ElementName=lsbItems, Path=SelectedItem}"/>
</StackPanel>
Well I was wondering how to bind a boolean property to a combobox.Combobox will be a yes/no combobox.
You could use a ValueConverter to convert the boolean value to a ComboBox index and back. Like this:
public class BoolToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value == true) ? 0 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value == 0) ? true : false;
}
}
}
Assuming Yes is on index 0 and No on index 1. Then you'd have to use that converter in binding to the SelectedIndex property. For this, you declare your converter in your resources section:
<Window.Resources>
<local:BoolToIndexConverter x:Key="boolToIndexConverter" />
</Window.Resources>
Then you use it in your binding:
<ComboBox SelectedIndex="{Binding YourBooleanProperty, Converter={StaticResource boolToIndexConverter}}"/>
I have found myself using the IsSelected property of the ComboBox items for this in the past. This method is entirely in xaml.
<ComboBox>
<ComboBoxItem Content="No" />
<ComboBoxItem Content="Yes" IsSelected="{Binding YourBooleanProperty, Mode=OneWayToSource}" />
</ComboBox>
First solution is to replace your 'Yes/No' combobox with a checkbox because, well, checkbox exists for a reason.
Second solution is to fill your combobox with true and false objects and then bind the 'SelectedItem' of your combobox to your Boolean property.
Here is an example (replace enabled/disabled with yes/no):
<ComboBox SelectedValue="{Binding IsEnabled}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={x:Static converters:EnabledDisabledToBooleanConverter.Instance}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.Items>
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
</ComboBox.Items>
</ComboBox>
Here is Converter:
public class EnabledDisabledToBooleanConverter : IValueConverter
{
private const string EnabledText = "Enabled";
private const string DisabledText = "Disabled";
public static readonly EnabledDisabledToBooleanConverter Instance = new EnabledDisabledToBooleanConverter();
private EnabledDisabledToBooleanConverter()
{
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Equals(true, value)
? EnabledText
: DisabledText;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//Actually won't be used, but in case you need that
return Equals(value, EnabledText);
}
}
And no need to play with indices.
please take a look at the following line
<TextBox Text="{Binding Price}"/>
This Price property from above is a Decimal? (Nullable decimal).
I want that if user deletes the content of the textbox (i.e. enters empty string, it should automatcally update source with null (Nothing in VB).
Any ideas on how I can do it 'Xamly'?
I am using .NET 3.5 SP1 so it's very simple:
<TextBox Text="{Binding Price, TargetNullValue=''}"/>
Which stands for (thanks Gregor for your comment):
<TextBox Text="{Binding Price, TargetNullValue={x:Static sys:String.Empty}}"/>
sys is the imported xml namespace for System in mscorlib:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Hope that helped.
This value converter should do the trick :
public class StringToNullableDecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
decimal? d = (decimal?)value;
if (d.HasValue)
return d.Value.ToString(culture);
else
return String.Empty;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
string s = (string)value;
if (String.IsNullOrEmpty(s))
return null;
else
return (decimal?)decimal.Parse(s, culture);
}
}
Declare an instance of this converter in the ressources :
<Window.Resources>
<local:StringToNullableDecimalConverter x:Key="nullDecimalConv"/>
</Window.Resources>
And use it in your binding :
<TextBox Text="{Binding Price, Converter={StaticResource nullDecimalConv}}"/>
Note that TargetNullValue is not appropriate here : it is used to define which value should be used when the source of the binding is null. Here Price is not the source, it's a property of the source...
You can try using a ValueConverter (IValueConverter)
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx
Of the back of my head here, something like:
public class DoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (double)value;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
var doubleValue = Convert.ToDouble(value);
return (doubleValue == 0 ? null : doubleValue);
}
}
(Might need some tweaking though)