I am making a ProgressBar with a TextBlock on top of it to notify the user about the download, I need the TextBlock to be bound to the value of the ProgressBar and formatted through XAML.
Like this:
<TextBlock x:Name="TxtBlock_Download" Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="3"
TextAlignment="Center"
Foreground="White" Padding="0,2,0,0">
<!--Updates the textbox by using multibinding-->
<TextBlock.Text>
<!--TODO fix the StringFormat -->
<MultiBinding Converter="{StaticResource ResourceKey=kk}" StringFormat="{}{}">
<Binding ElementName="ProgressBar_Download" Path="Value"/>
<Binding ElementName="ProgressBar_Download" Path="Maximum"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
I am also using a separate class to connect the two value and maximum
Like this:
class Binding : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0] + "/" + values[1].ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return (value as string).Split('/');
}
}
But every time I try to format the string using StringFormat="" it freezes the UI until the download is complete (The download still works in the background but the program is frozen).
Since you can't format numbers that have already been converted to string I recommend you simply rewrite your converter (since you already have one) to take care of all the formatting you need:
class MyAwesomeProgressConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return ((double)values[0]).ToString("f2") + "/" + ((double)values[1]).ToString("f2");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
Enumerable.Repeat(DependencyProperty.UnsetValue, targetTypes.Length).ToArray()
}
}
Also:
You don't need ConvertBack. Throw an Exception or return an Array of DependencyProperty.UnsetValue or Binding.DoNothing.
Don't name your Converter Binding - that might cause ambiguities
Related
Can i simply add characters without binding this characters to my ViewModel?
I need these to show physical units like cm, mm, m, cm^3, ...
XAML:
<TextBox
Text="{Binding value_top}" //sth. like + "cm"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="50"/>
VW:
value_top = 22.9
Output aim:
22.9 cm
I know, I can overlay a Label, but i would like to know if there is another possibility.
Create a converter which will take the double and return a string. Here is an untested example to get you started:
public class DoubleToString: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var dbl = (double) value;
return $"{dbl} cm";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Double.Parse(Regex.Match(value.ToString(), "[\d.]+").Value);
}
}
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();
}
}
I need to determine the StringFormat of some bound TextBlocks at runtime based on the unit system identified in the object to be bound.
I Have a converter with a Dependency Property that I would like to Bind to. The Bound value is used in determining the conversion process.
public class UnitConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty IsMetricProperty =
DependencyProperty.Register("IsMetric", typeof(bool), typeof(UnitConverter), new PropertyMetadata(true, ValueChanged));
private static void ValueChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((UnitConverter)source).IsMetric = (bool)e.NewValue;
}
public bool IsMetric
{
get { return (bool)this.GetValue(IsMetricProperty); }
set { this.SetValue(IsMetricProperty, value); }
}
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (IsMetric)
return string.Format("{0:0.0}", value);
else
return string.Format("{0:0.000}", value);
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I declare the converter
<my:UnitConverter x:Key="Units" IsMetric="{Binding Path=IsMetric}"/>
and bind the TextBlock
<TextBlock Text="{Binding Path=Breadth, Converter={StaticResource Units}}" Style="{StaticResource Values}"/>
Never the less, I get the following error:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsMetric; DataItem=null; target element is 'UnitConverter' (HashCode=62641008); target property is 'IsMetric' (type 'Boolean')
I guess this is initialising before I set the datacontext and therefore there is nothing to bind the IsMetric property to. How can I achieve the desired result?
Provided that Breadthand IsMetric are properties of the same data object, you might use a MultiBinding in conjunction with a multi value converter:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource UnitMultiValueConverter}">
<Binding Path="Breadth" />
<Binding Path="IsMetric" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
with a converter like this:
public class UnitMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double value = (double)values[0];
bool isMetric = (bool)values[1];
string format = isMetric ? "{0:0.0}" : "{0:0.000}";
return string.Format(format, value);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The problem with your approach is that when the UnitConverter is declared as resource it does not have a DataContext, and it will never get one later on.
And one more important thing: the ValueChanged callback for UnitConverter.IsMetric is nonsense. It sets the same property again which was just changed.
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();
}
}
Q: Why does my custom TextBox UserControl using a MultiBinding and IMultiValueConverter gets its Convert() method called only once (during instanciation) ??
I have defined a UserControl that requires a MultiBinding and a IMultiValueConverter in order to change its behavior/presentation upon 2 indenpendant DependencyProperty.
<proj:MyControl Value="10" Digits="1" />
UserControl:
<UserControl x:Class="MyControl"
x:Name="uc"
...>
<UserControl.Resources>
<conv:DecimalToStringMultiConverter x:Key="DecToString" />
</UserControl.Resources>
[...]
<Grid>
<ctrl:VTextBox x:Name="vTb" Grid.Column="0" Margin="0,0,2,0">
<ctrl:VTextBox.Text>
<MultiBinding Converter="{StaticResource DecToString}" UpdateSourceTrigger="LostFocus" Mode="TwoWay">
<Binding ElementName="uc" Path="Value" Mode="TwoWay" />
<Binding ElementName="uc" Path="Digits" Mode="TwoWay" />
</MultiBinding>
</ctrl:VTextBox.Text>
</ctrl:VTextBox>
</Grid>
</UserControl>
When executing the application, the UserControls are all correctly instanciated. However, the IMultiValueConverter.Convert() method gets called only once.
Using an simple Binding + IValueConverter with a constant ConvertParameter worked great: the converter's Convert() method would get called everytime the TextBox contained inside the UserControl had its Text property changed.
Design changed and I had to resort to using a MultiBinding + IMultiValueConverter, and now the Convert() method only gets called once, and the TextBox.Text property is never updated upon the LostFocus() event.
What gives?
The MultiValueConverter is defined as below. I just wrap the IMultiValueConverter upon the IValueConverter to reuse existing code.
[ValueConversion(/*sourceType*/ typeof(Decimal), /*targetType*/ typeof(string))]
public class DecimalToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return "0.00";
int? digits = parameter as int?;
if(digits == null)
digits = 2;
NumberFormatInfo nfi = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone();
nfi.NumberGroupSeparator = " ";
nfi.CurrencyDecimalSeparator = ".";
nfi.NumberDecimalDigits = (int)digits;
return ((decimal)value).ToString("n", nfi);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return 0.00m;
decimal d;
return decimal.TryParse((string)value, out d) ? d : 0.00m;
}
}
[ValueConversion(/*sourceType*/ typeof(Decimal), /*targetType*/ typeof(string))]
public class DecimalToStringMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
DecimalToStringConverter conv = new DecimalToStringConverter();
return conv.Convert(values[0], targetType, values.Length > 1 ? values[1] : null, culture);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
DecimalToStringConverter conv = new DecimalToStringConverter();
return new[] { conv.ConvertBack(value, targetTypes[0], null, culture) };
}
}
It seems like you have some conflicting expectations about the updating behavior of the Binding and TextBox. The only reason Convert will be called multiple times is if the values of Digits or Value change multiple times, and there is nothing in your posted code to indicate that will happen. Changes to TextBox.Text won't cause calls to Convert, but should instead be calling ConvertBack on every change+LostFocus. Are you seeing that when you run your code?
You also need to return two values, instead of the one there now, from your ConvertBack method in order to supply both of the Bindings used in the MultiBinding with values.