Reverse bool value in binding - c#

I have a TextBlock like this:
<TextBlock Visibility="{Binding IsOnline, Converter={StaticResource boolToVisibilityConverter}}">
boolToVisibility returns Visible if IsOnline is true. But in a situation I want textblock be Collapsed if IsOnline is true.
I can make another converter which acts in reverse, but I want to know isn't it possible to do that in XAML with current converter?

You could potentially use a ConverterParameter value to determine whether to invert the output, for instance:
<TextBlock Visibility="{Binding IsOnline, ConverterParameter=true, Converter={StaticResource boolToVisibilityConverter}}" />
And in the converter itself:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool invertOuput = false;
if (parameter != null) {
bool.TryParse((string)parameter, out invertOuput)
}
// TODO: Converter logic
}

As far as I'm concerned, you'll have to make another converter, although, your converter (not reverese) already exists:
http://msdn.microsoft.com/pl-pl/library/system.windows.controls.booleantovisibilityconverter(v=vs.110).aspx

Related

Bind a Converter Parameter in re-usable User Control

I am trying to create a re-usable user control (for data entry) in which there are two text boxes and they are linked to each by an IValueConvertor.
The following XAML is the original, normal code. This is what I am trying to reproduce in a user control.
<WrapPanel>
<TextBlock Text="Length of Fence"/>
<TextBox Name="Metric" Width="50" Text="{Binding Path=LengthFence, Mode=TwoWay}"/>
<TextBlock Text="Meters"/>
<TextBox Text="{Binding ElementName=Metric, Path=Text, Converter={StaticResource MetersToInches}, StringFormat=N8}"/>
<TextBlock Text="Inches"/>
</WrapPanel>
and the code-behind for the IValueConvertor (in MainWindow.xaml) is
public class MetersToInches : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.ToString() == "")
return 0.0;
try
{
double meters = System.Convert.ToDouble(value);
var result = meters * 39.3701;
return result;
}
catch
{
// Catch errors when users type invalid expressions.
return 0.0;
}
}
public object ConvertBack(object value, Type targettype, object parameter, CultureInfo culture)
{
if (value.ToString() == "")
return 0.0;
try
{
double inches = System.Convert.ToDouble(value);
var result = inches * 0.0254;
return result;
}
catch
{
// Catch errors when users type invalid expressions.
return 0.0;
}
}
}
This is what this XAML looks like:
Now I have made a re-usable UserControl with three dependency properties Label for label string, Value for binding a property inside the ViewModel, and Units - a string property to show the input units.
<UserControl ...
x:Name="parent">
<StackPanel DataContext="{Binding ElementName=parent}">
<TextBlock Text="{Binding Path=Label}"/>
<TextBox Text="{Binding Path=Value}"/>
<TextBlock Text="{Binding Path=Units}"/>
</StackPanel>
However, this re-usable control can only tackle the first TextBox of the input. I do not know how to bind the IValueConvertor in the second TextBox. I need to do this because I want to bind other converters such as meters to feet, kg to pound, etc.
I have read that ConvertorParameter cannot be bound because it is not a dependency property and I am not sure if I can use multi-binding, mostly because I do not know how to use it properly Binding ConverterParameter.
I would be very grateful if you could show me how to do this or direct me to the appropriate link on StackOverflow or elsewhere that solves this problem. Or if there is a better way of doing this.
Many many thanks in advance.
First, don't bind the TextBoxes to each other (as in your original code at the begining of the question), instead, bind each TextBox to the same backing property, which, in your UserControl, is Value.
As for how to implement multiple bindings, you probably don't need a MultiBinding.
We have to pick a "standard" unit of measure to begin with- this will be the unit that will be actually stored in the property and in any database or file. I'll assume this standard unit will be meters (m). An IValueConverter can be used to convert between meters and some other unit of distance and back, using the ConverterParameter to specify which other unit to convert to/from.
Here's a good example to get you started.
public enum DistanceUnit { Meter, Foot, Inch, }
public class DistanceUnitConverter : IValueConverter
{
private static Dictionary<DistanceUnit, double> conversions = new Dictionary<DistanceUnit, double>
{
{ DistanceUnit.Meter, 1 },
{ DistanceUnit.Foot, 3.28084 },
{ DistanceUnit.Inch, 39.37008 }
};
//Converts a meter into another unit
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return conversions[(DistanceUnit)parameter] * (double)value;
}
//Converts some unit into a meter
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) { return 0; }
double v;
var s = value as string;
if (s == null)
{
v = (double)value;
}
else
{
if (s == string.Empty) { return 0; }
v = double.Parse(s);
}
if (v == 0) { return 0; }
return v / conversions[((DistanceUnit)parameter)];
}
}
The above has a few problems. I never check if parameter really is a DistanceUnit before using it, for example. But it works.
Here's an example of how I used it:
<StackPanel>
<StackPanel.Resources>
<local:DistanceUnitConverter x:Key="DistCon"/>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Distance, Converter={StaticResource DistCon}, ConverterParameter={x:Static local:DistanceUnit.Meter}}" MinWidth="20"/>
<TextBlock>m</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Distance, Converter={StaticResource DistCon}, ConverterParameter={x:Static local:DistanceUnit.Foot}}" MinWidth="20"/>
<TextBlock>ft</TextBlock>
</StackPanel>
</StackPanel>
The DistanceUnit enum and the internal conversions dictionary can be expanded with more units of measure. Alternatively, you can use a 3rd party library that already has all these included, like UnitsNet.
Not sure how you would like to bind mulitple converters in one single control. If i'm not wrong, you would like to build a control where when a user enters a particular value, you need to display it in different units. If this is the case, you can create a single converter with converterparameter as "m","cm","inch" etc and based on this you can return the result. Then in this case, you will have 4,5 controls and each will have same converter binding but different converter values. If this is not clear and you need further direction, please let know.
Multi Value binding
To answer your point 6, please see a sample multi binding converter and its implementation in xaml below. I have built a simple RolesFilter which will take different inputs from the xaml as object[] and since I already know what data is expected, i'm converting them in the converter.
public class RolesFilter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
try
{
FlipperObservableCollection<Role> _roles = (FlipperObservableCollection<Role>)values[0]; //Input
Department _dept_param = values[1] as Department;
bool _filter = (bool)values[2];
string _id = "NA";
if (values.Count() == 4 && values[3] is string) _id = (string)values[3] ?? "NA";
//If we need a filter, then without department, it should return empty results
if (!_filter) return _roles; //If no filter is required, then don't worry, go ahead with input values.
if (_dept_param == null) return new FlipperObservableCollection<Role>(); //If department is null, then
List<Role> _filtered_list = _roles.ToList().Where(p => p.department.id == _dept_param.id && p.id != _id)?.ToList() ?? new List<Role>();
return new FlipperObservableCollection<Role>(_filtered_list);
}
catch (Exception)
{
throw;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I'm using the multi value converter in the xaml as below. Here, i'm filtering an itemsource of a combo box based on another combobox and a check box. This is just an example and in your case, you can create a combo box with different Units values. Based on user selection, you can use the converter and return value to the textbox.
<ComboBox Height="30" SelectedItem="{Binding reports_to, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource roles_filter}">
<Binding Source="{StaticResource SingletonData__}" Path="roles" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="department" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="cbx_filter" Path="IsChecked"/>
<Binding Path="id" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding department.name}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding name}"/>
</WrapPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Converter only firing once and not every time label content changes

I have label with a width of auto, that is bound to a property of type string.
<Label x:Name="ExampleLabel" Content="{Binding ExampleProperty}"Height="30" Width="Auto" >
I then have a property that is bound to the width of that label. The converter should convert the width to a negative value.
<UserControl.Resources>
<c:PositiveToNegativeConverter x:Key="PositiveToNegativeConverter"/>
</UserControl.Resources>
"{Binding ElementName=ExampleLabel, Path=Width, Converter={StaticResource PositiveToNegativeConverter}}"
I want the converter to execute when the label content changes, but it is only fired once, when the application loads.
Here is my converter
public class PositiveToNegativeConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (double)value * -1;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Math.Abs((double) value);
}
}
Any help is much appreciated. Thanks.
Does it work if you bind to ActualWidth instead of Width? Width is just whatever value you last assigned to the Width property, while ActualWidth is a read-only live-updated runtime value for how wide the thing really is in the UI.
I would expect this to update when you want it to:
"{Binding ElementName=ExampleLabel, Path=ActualWidth, Converter={StaticResource PositiveToNegativeConverter}}"

How to check or uncheck checkboxes inside of a List Box WPF

I have a combobox which data source comes from a table in my DataBase. So, each item in my combo is an Object from the table. This Object have an attribute which corresponds to a string full of "1"s or "0"s. On the other hand I have a list of checkboxes inside of a ListBox with this template:
<ListBox Height="150" MinHeight="100" HorizontalAlignment="Center" Name="lstEstudios" VerticalAlignment="Top" Width="200"
ItemsSource="{Binding}" SelectionMode="Multiple" Margin="0,20,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="chkEstudios" Width="Auto" Content="{Binding Path=Nom_estudio}"
Checked="chkEstudios_Checked" Unchecked="chkEstudios_Unchecked"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I don´t know if it's possible but, that I want to do is, for each "1" or "0" in the attribute set the checkbox checked or unchecked depending if there is a "1" check the checkbox or if is "0" uncheck the checkbox, and so on... with all the checkboxes in the ListBox, how to do that ?
I tried the same thing with my own sample having a CustomTask class.
<ListBox ItemsSource="{Binding CustomTasks}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding TaskStatus, Converter={x:Static testApp:StatusToBooleanConverter.Instance}}" Content="{Binding TaskStatus}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where the TaskStatus is a boolean of two values, i.e Completed and Pending.
and here is the code for the converter
public class StatusToBooleanConverter : IValueConverter
{
public static StatusToBooleanConverter Instance = new StatusToBooleanConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Status)
{
switch ((Status)value)
{
case Status.Completed:
return true;
case Status.Pending:
return false;
}
return null;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Just try this out. Hope that helps.
This is a good place to use a converter. If you have a property with 1's and 0's in it and you want to translate those to true/false for the checked attribute then try making converters to do the work. The basic idea behind a converter is to take an input value (from a bound property) and, as the name implies, convert it to a different value. You can make them as simple or complicated as you would like, and turning "1" into true and "0" into false should be pretty quick.
You will bind the IsChecked attribute of the checkbox to your source of 1's and 0's and in the binding also use the converter.
If you haven't made a value converter before here is a nice tutorial on making one: http://wpftutorial.net/ValueConverters.html

WPF DataBinding does not update property (just only one time at startup)

I´m having some trouble binding some items attributes.
I have comboboxes and a buttons in a itemscontrol. The combobox is for searching Localities by name. Thats why when the combobox is created, the property IsEditable is true, in order to let the user enter a name, and then press left-control to search that string in the database via WCF.
Then, when the combobox ItemSource.Count is al least 1, I block the combobox by setting IsEditable = false (using the DataBinding of the button). Thats when the button have to change the visibility from hidden to visible, because pressing the button set IsEditable to true again, and ables the user to input a name to search.
To achieve this, I have binded the combobox IsEditable with the button Visibility attribute, and used the following converter, which works:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
public class VisibilityToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (Visibility)value == Visibility.Visible ? false : true;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
As I said, the left control button search the localities, for that I´m using the keydown event:
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl)
{
ComboBox cbx = sender as ComboBox;
LocationServiceClient locationService = new LocationServiceClient();
if (cbx != null)
{
cbx.ItemsSource = locationService.SeachLocalities(new SearchLocalitiesRequest { Search = cbx.Text, MaxItems = 20 }).Localities;
cbx.DisplayMemberPath = "LocalityName";
localityCombobox = cbx;
cbx.IsDropDownOpen = true;
}
}
}
As the Items of the Combobox changed, wouldn´t that have to affect the binding of the Button visibility?
The binding uses this converter, which works too, but only executes once, when I run the app. Thats the problem I´m having, it just does not update the button visibility, and leaves it on Hidden:
public class ItemsSourceCountToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var b = (int)value > 0 ? Visibility.Visible : Visibility.Hidden;
return b;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
This the image of the control, it might help you to get what I say:
Just in case, this is the xaml i used:
<ComboBox Name ="cbxLocality" Width="200" DisplayMemberPath="{Binding LocalityName}" IsEditable="{Binding ElementName= btnRemoveLocality, Path=Visibility, Converter={StaticResource VisibilityToBooleanConverter}}" KeyDown="ComboBox_KeyDown">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding LocalityName}"/>
<TextBlock FontSize="10">
<Run Text="CP: "/>
<Run Text="{Binding ZipCode}"/>
<Run Text=" | "/>
<Run Text="{Binding Province.ProvinceName}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Name ="btnRemoveLocality" Content="x" Visibility="{Binding ElementName= cbxLocality, Path=Items.Count, Converter={StaticResource ItemsSourceCountToVisibilityConverter}}" Click="Button_Click_3"></Button>
Does it work to call .DataBind() on cbx when changing ItemSource?
Edit: I would bind the visibility for path Items.Count, instead of just Items, and make the converter handle the integer instead of the Item-list. Because the Count-property triggers the PropertyChanged-event, and the list itself will not if an element is added/removed.
Edit 2: Declare the ObservableCollection of you items as a public property outside the method itself, so it will have public scope. And set it as ItemsSource. Then you won't have to change the ItemSource-property, only the collection itself.

Unable to apply string formatting in Silverlight DataGrid column

I have a Data Grid. Its Item source is set to a List. My problem is that Iam unable to apply string formatting . This is formats Ive tried . Am I missing some thing ?
StringFormat='MM/dd/yyyy'
StringFormat={0:dd-MMM-yyyy}
Attached the resultant grid
<sdk:DataGridTemplateColumn Header="Recieved Date" Width="Auto" >
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=RecievedDate, StringFormat=\{0:dd-MMM-yyyy\} }" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<sdk:DatePicker Name="dtpFinancialAndComplianceLog" Text="{Binding Path=RecievedDate,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
If I understand what you're trying to do correctly, you have a DataGrid column which you want to display a DateTime object in a certain format. Ordinarily a DateTime object will sort out its own formatting depending on the System.Threading.Thread.CurrentUICulture.
Easiest way I know of to force any object into a certain format is to use a custom IValueConverter:
namespace MyProject.Converters
{
public class FormatConverter : IValueConverter
{//Suitable only for read-only data
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return string.Empty;
if(string.IsNullOrEmpty(parameter.ToString()))
return value.ToString();
return string.Format(culture, parameter.ToString(), value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
After adding a namespace to your xaml: xmlns:conv="clr-namespace:MyProject.Converters" and declaring your converter in the control's resources <conv:FormatConverter x:Key="Formatter" />, you will need to bind your column's data using your new converter:
<TextBlock Text="{Binding Path=RecievedDate, Converter={StaticResource Formatter}, ConverterParameter=\{0:dd-MMM-yyy\} }" />

Categories