How to bind 2 textboxes to one property? - c#

I have a propery PhoneNumber and in the UI, I have 2 textboxes, one is the prefix, and the other one is the postfix, how can I bind it to the property? (The property inside the DataContext).
<TextBox Grid.Column="0" MaxLength="3" /> //Prefix
<TextBlock Grid.Column="1" Text="-" />
<TextBox Grid.Column="2" /> //Postfix
The only way I see it work is with code behind using textbox1.Text + textbox2.Text... Is there a better way?
Thanks in advance :)

Just use two more properties in the data context
code is not complied or tested
public string PhoneNumber { get; set; }
public string Prefix
{
get
{
return PhoneNumber.Substring(0, 3);
}
set
{
// replace the first three chars of PhoneNumber
PhoneNumber = value + PhoneNumber.Substring(3);
}
}
public string Postfix
{
get
{
return PhoneNumber.Substring(3);
}
set
{
// replace the chars of starting from index 3 of PhoneNumber
PhoneNumber = PhoneNumber.Substring(0, 3) + value;
}
}

I think uou can use Converter for this purpose, the example going one way can look like this:
In this my Number is a string 000-000000, but you can surely change it.
In XAML:
<Window.Resources>
<conv:PostixConverter x:Key="PostfixConv" xmlns:conv="clr-namespace:Example.Converters"/>
<conv:PrefixConverter x:Key="PrefixConv" xmlns:conv="clr-namespace:Example.Converters"/>
</Window.Resources>
<StackPanel>
<TextBox MaxLength="3" Text="{Binding Number, Converter={StaticResource PrefixConv}}"/>
<TextBlock Text="-" />
<TextBox Text="{Binding Number, Converter={StaticResource PostfixConv}}"/>
</StackPanel>
And in code behind:
namespace Example.Converters
{
public class PrefixConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return null;
else return ((string)value).Substring(0, 3);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class PostixConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return null;
else return ((string)value).Substring(4);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Related

How to update view element display based on property`s value change by VM method?

Need some help. Hello everyone. I really have no idea how to update view elements, when property value is updated with my Egzecute method inside of MsgViewModel, called with public ICommand Start. For example, I want to make one button STOP visible, another one START collapsed, when a property Status changes its value, from Stopped to Sending. Please also be noted, that visibility is updated corrctly when property Status is changed with ViewModels constructor by (default on start for me)Status = Models.SendingStatus.Stopped; or Status = Models.SendingStatus.Sending;.
View:
<!--START, to be collapsed-->
<Button Grid.Row="0"
Grid.Column="4"
Background="#80B584"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Mode=OneWay,
Converter={StaticResource boolStart}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="START" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
<!--STOP, to be viewed-->
<Button Grid.Row="0"
Background="#FF8A8A"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Mode=OneWay,
Converter={StaticResource boolStop}}" Margin="0,145,443.667,-0.333">
<TextBlock Text="STOP" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
ViewModel:
private Models.MsgModel message= new Models.MsgModel (); //model instance
public MsgViewModel() //constructor, by default makes staus "Stopped"
{
Status = Models.SendingStatus.Stopped;
}
public Models.SendingStatus Status
{
get
{
return message.Status;
}
set
{
message.Status = value;
}
}
private ICommand start;
public ICommand Start //command called by START button, supposed to collapse it, and show STOP button
{
get
{
if (start == null)
start = new RelayCommand(
o =>
{
Egzecute();
});
return start;
}
}
public void Egzecute() //method called by the command
{
Status = Models.SendingStatus.Sending;
var openDialog = new Powiadomienie();
openDialog.ShowPowiadomienie(Status.ToString(), "Powiadomienie"); //shows updated SendingStatus, but the View is not updating to it
}
Model:
public enum SendingStatus: byte { Sending, Waiting, Stopped} //enum for Status property
public class MsgModel : INotifyPropertyChanged
private SendingStatus status;
public SendingStatus Status //Status model property
{
get
{
return status;
}
set
{
status = value;
OnPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Converters:
public class BooleanStart : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ViewModels.MsgViewModel mvm = new ViewModels.MsgViewModel();
bool bvalue = (bool)value;
if (mvm.Status == Models.SendingStatus.Sending|| mvm.Status == Models.SendingStatus.Waiting)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanStop : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ViewModels.MsgViewModel mvm = new ViewModels.MsgViewModel();
bool bvalue = (bool)value;
if (mvm.Status == Models.SendingStatus.Sending|| mvm.Status == Models.SendingStatus.Waiting)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
My question is, how to have updated View after calling the method by command?
Ok, after several hours I figured out my mistake. Construction of Converter was wrong. Binding supposed to be different, and ViewModel updated with property change notification.
Converters:
public class BooleanStart : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Models.SendingStatus sendingStatus = (Models.SendingStatus)value;
if (sendingStatus == Models.SendingStatus.Sending || sendingStatus == Models.SendingStatus.Waiting)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanStop : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Models.SendingStatus sendingStatus = (Models.SendingStatus)value;
if (sendingStatus == Models.SendingStatus.Sending || sendingStatus == Models.SendingStatus.Waiting)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
For binding:
<!--START-->
<Button Grid.Row="0"
Grid.Column="4"
Background="#80B584"
Visibility="{Binding Path=Status, Converter={StaticResource boolStart}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="START" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
<!--STOP-->
<Button Grid.Row="0"
Background="#FF8A8A"
Visibility="{Binding Path=Status, Converter={StaticResource boolStop}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="STOP" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
ViewModel` method:
public void Egzecue()
{
Status = Models.SendingStatus.Sending;
OnPropertyChanged("Status");
var openDialog = new Powiadomienie();
openDialog.ShowPowiadomienie(Status.ToString(), "Powiadomienie");
}

How to pass Static Resource String to ConverterParameter in UWP

I m doing an UWP project and I wan't to format a string using a converter and Static resource String because the application is in mulitple languages.
Here is my converter :
public class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
Here the string in my Resource Strings.Xaml file :
<x:String x:Key="nbItems">You have {0} items...</x:String>
Here the element where I wan't to pass this formatter :
<TextBlock Text="{x:Bind NbItems, Converter={StaticResource StringFormatConverter}, ConverterParameter={StaticResource nbItems}, Mode=OneWay}"/>
It's not working but if I do like this it works :
<TextBlock Text="{x:Bind NbItems, Converter={StaticResource StringFormatConverter}, ConverterParameter='You have {0} items..', Mode=OneWay}"/>
The parameters is always null in my converter, why it's not working ?
Not exactly certain why the parameter is null, however I have come up with a workaround. Move your strings into a Resource file (see here).
Then change the parameter you pass to your converter to the String Name, like so:
<TextBlock Text="{x:Bind NbItems, Converter={StaticResource StringFormatConverter}, ConverterParameter='FORMAT', Mode=OneWay}" />
Finally change your converter to load the the resource using the parameter like so:
public object Convert(object value, Type targetType, object parameter, string language) {
if (value == null)
return null;
var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
var str = loader.GetString((string)parameter);
return string.Format(str, value);
}
Hope this helps.

C# ConvertBack from a binding column of textbox to a JObject

GoodDay!
I'm not good with wpf and binding, i need your help. I have already bind a Json Object (JObject) to a Column of TextBox.
<TextBox Width="250" Text="{Binding Path=Property, Converter={StaticResource jPropertyConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
I can show properly the data of the Jobject when i start my wpf window, now i need to ConvertBack the data modified when i modify one of the textbox of the columns, from that TextBox to the JObject, and the related JValue.
public class JPropertyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is JProperty)
{
JToken valoreProperty = (value as JProperty).Value;
if ((valoreProperty is JValue))
return (valoreProperty as JValue).Value;
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
??
}
}
I can change the Value of the "leaves" of this JObject tree like:
(valoreProperty as JValue).Value = "Hello!";
How can i change the "leaves" of this JObject tree in the convert back?
Sorry for my english.
Thanks and bye
EDIT:
Thanks dbc! It works, many thanks!
Now i need to show in another column, every lenght of the value inside of the textbox, obviously if i change the value in the texbox the relative lenght value will change too.
I tried:
<DataGridTextColumn Header="Lunghezza2" IsReadOnly="True" Width="50" Binding="{Binding Path=Property.Value.Value.toString().Length, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
or in another way with a converter, passing a JProperty
public class LengthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is JProperty)
{
JToken jValue = (value as JProperty).Value;
if ((jValue is JValue) && (jValue as JValue).Value != null)
return (jValue as JValue).Value.ToString().Length.ToString();
else
return "0";
}
else
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
But every try didn't works when i change the textbox value, any tips?
Many thanks again!
You don't need a converter for this purpose. Assuming your Property property returns a JProperty, you can bind directly to JProperty.Value.Value
<TextBox Name="PropertyTextBox"
Text="{Binding Path=Property.Value.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
If you want to disable the TextBox if the bound Property is not a "simple" JProperty (one with just a JValue for its value), you can do:
<TextBox Name="PropertyTextBox"
Text="{Binding Path=Property.Value.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=Property, Converter={StaticResource IsSimpleJPropertyConverter}, Mode=OneWay}"
/>
Using the converter
public class IsSimpleJPropertyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is JProperty)
{
JToken jValue = (value as JProperty).Value;
if (jValue is JValue)
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Converting Seconds to Minutes:Seconds

I am trying to bind a TextBlock's Text property by converting the Total Seconds i.e
1004 to Minutes:Seconds and I can successfully pull my Seconds from the XML but I dont know how to work with Getters and Setters so I can convert my Seconds to Minutes:Seconds
I had a look at TimeSpan and I know it can do what I ask but I dont know how to write the getter and setter so it will convert the integer values (seconds) to a Minute:Seconds format.
This is what I have in my Class so far
public class Stats
{
public TimeSpan Time {get;set;}
}
any help would be greatly appreciated,
thanks
John
To do it as a property you can do:
public class Stats {
public TimeSpan Time { get; set; }
public string TimeFormated { get { return Time.TotalMinutes + ":" + Time.Seconds; } }
}
Although you really should do that in your XAML since the what are doing is layout:
<StackPanel Orientation="Horizontal">
<TextBlock Text={Binding Time.TotalMinutes}" />
<TextBlock Text=":" />
<TextBlock Text=={Binding Time.Seconds}" />
</StackPanel>
Would recommend this converter instead (since the two previous answers will give you 2:1 when you really want 2:01 -
public class FriendlyTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
TimeSpan ts = TimeSpan.FromSeconds((int)value);
return String.Format("{0}:{1:D2}", ts.Minutes, ts.Seconds);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Note the :D2 specifier for format strings.
And to use it, you specify it at the same time as your binding:
<phone:PhoneApplicationPage.Resources>
<util:FriendlyTimeConverter x:Key="FriendlyTimeConverter"/>
</phone:PhoneApplicationPage.Resources>
...
<TextBlock Text="{Binding timeRemaining, Converter={StaticResource FriendlyTimeConverter}}" Name="TimerDisplay" Grid.Column="4" HorizontalAlignment="Right" Margin="12,0" Style="{StaticResource PhoneTextTitle2Style}"></TextBlock>
Use a converter.
XAML:
<phone:PhoneApplicationPage.Resources>
<classes:TimeSpanConverter x:Key="c" />
</phone:PhoneApplicationPage.Resources>
...
<TextBlock Text="{Binding Time, Converter={StaticResource c}}" />
C#:
public class TimeSpanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var time = (TimeSpan) value;
return time.TotalMinutes + ":" + time.Seconds;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}

C# WPF IsEnabled using multiple bindings?

I have a WPF xaml file describing a section of a GUI and I'd like the enabling/disabling of a particular control to be dependent on two others. The code looks something like this at the moment:
<ComboBox Name="MyComboBox"
IsEnabled="{Binding ElementName=SomeCheckBox, Path=IsChecked}"/>
But I'd like it to be dependant on another checkbox as well so something like:
<ComboBox Name="MyComboBox"
IsEnabled="{Binding ElementName=SomeCheckBox&AnotherCheckbox, Path=IsChecked}"/>
What's the best way to go about that? I can't help feeling I'm missing something obvious or going about this the wrong way?
You can use a MultiBinding with a converter which implements IMultiValueConverter.
Just to give an answer you can (almost) copy&paste:
Static resource needed:
<converterNamespace:BooleanAndConverter x:Key="booleanAndConverter" />
The ComboBox:
<ComboBox Name="MyComboBox">
<ComboBox.IsEnabled>
<MultiBinding Converter="{StaticResource booleanAndConverter}">
<Binding ElementName="SomeCheckBox" Path="IsChecked" />
<Binding ElementName="AnotherCheckbox" Path="IsChecked" />
</MultiBinding>
</ComboBox.IsEnabled>
</ComboBox>
The code for the converter:
namespace ConverterNamespace
{
public class BooleanAndConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
foreach (object value in values)
{
if ((value is bool) && (bool)value == false)
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("BooleanAndConverter is a OneWay converter.");
}
}
}
You can also try shorter version of the same:
public class BooleanAndConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values.OfType<IConvertible>().All(System.Convert.ToBoolean);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
public class BooleanOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values.OfType<IConvertible>().Any(System.Convert.ToBoolean);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
and, of course, you may need the converters for visibility, too:
public class BooleanOrToVisibilityConverter : IMultiValueConverter
{
public Visibility HiddenVisibility { get; set; }
public bool IsInverted { get; set; }
public BooleanOrToVisibilityConverter()
{
HiddenVisibility = Visibility.Collapsed;
IsInverted = false;
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool flag = values.OfType<IConvertible>().Any(System.Convert.ToBoolean);
if (IsInverted) flag = !flag;
return flag ? Visibility.Visible : HiddenVisibility;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanAndToVisibilityConverter : IMultiValueConverter
{
public Visibility HiddenVisibility { get; set; }
public bool IsInverted { get; set; }
public BooleanAndToVisibilityConverter()
{
HiddenVisibility = Visibility.Collapsed;
IsInverted = false;
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool flag = values.OfType<IConvertible>().All(System.Convert.ToBoolean);
if (IsInverted) flag = !flag;
return flag ? Visibility.Visible : HiddenVisibility;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I believe you may have to use a MultiBinding with a MultiValueConverter. See here: http://www.developingfor.net/wpf/multibinding-in-wpf.html
Here is a directly related example: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5b9cd042-cacb-4aaa-9e17-2d615c44ee22
As extension to qqbenq's answer:
Added the function to handle the Count of a Collection for example if you want to check if some item of a ListView is selected.
Converter:
public class IsEnabledConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (var value in values)
{
switch (value)
{
case bool b when !b:
case int i when i == 0:
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
Namespace <theNamespace:IsEnabledConverter x:Key="IsEnabledConverter"/>
Button
<Button x:Name="MyButton">
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource IsEnabledConverter}">
<Binding ElementName="MyListView" Path="SelectedItems.Count"/>
<Binding ElementName="MyCheckBox" Path="IsChecked"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
When you don't want to use MultiBinding
public class AndEnabledTextBox : TextBox
{
public static readonly DependencyProperty AndEnabled1SubProperty =
DependencyProperty.Register(nameof(AndEnabled1), typeof(bool), typeof(AndEnabledTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnAndEnabledPropertyChanged)));
public static readonly DependencyProperty AndEnabled2SubProperty =
DependencyProperty.Register(nameof(AndEnabled2), typeof(bool), typeof(AndEnabledTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnAndEnabledPropertyChanged)));
public bool AndEnabled1 { get { return (bool)GetValue(AndEnabled1SubProperty); } set { SetValue(AndEnabled1SubProperty, value); } }
public bool AndEnabled2 { get { return (bool)GetValue(AndEnabled2SubProperty); } set { SetValue(AndEnabled2SubProperty, value); } }
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
IsEnabled = AndEnabled1 && AndEnabled2;
}
protected static void OnAndEnabledPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
AndEnabledTextBox textBox = (AndEnabledTextBox)obj;
textBox.IsEnabled = textBox.AndEnabled1 && textBox.AndEnabled2;
}
}
XMAL is more simplified when you use inherited controls.
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
<StackPanel>
<ToggleButton x:Name="switch1" Content="{Binding ElementName=switch1, Path=IsChecked}"/>
<ToggleButton x:Name="switch2" Content="{Binding ElementName=switch2, Path=IsChecked}"/>
<local:AndEnabledTextBox Text="TEXT"
AndEnabled1="{Binding ElementName=switch1, Path=IsChecked, Mode=OneWay}"
AndEnabled2="{Binding ElementName=switch2, Path=IsChecked, Mode=OneWay}"/>
</StackPanel>
</Window>

Categories

Resources