What causes a Value Converter to fire? - wpf

I am setting an IsEnabled property of a control based on whether or not a SelectedIndex >= 0 in a ListBox. I can do this in the code behind, but I wanted to create a value converter for this behavior since it is something I do frequently.
I created this Value Converter to handle the task and bound it to the IsEnabled property:
[ValueConversion(typeof(Selector), typeof(bool))]
public class SelectorItemSelectedToBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || !(value is Selector))
return null;
var control = value as Selector;
return control.SelectedIndex >= 0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
The Converter is only called once, when the application is loaded. It does not fire when the SelectedIndex changes.
My question is therefore what causes a Value Converter to fire? I assume it is when the bound data changes, so is there a way to force the converter to fire in different circumstances? Am I even asking the right question?

It won't fire because you've bound it to the Selector itself, not the SelectedIndex property of the Selector. WPF will monitor every property in the path you bind to, and update values if any of those properties changes. The Selector isn't changing, the SelectedIndex is.

I think a converter might be the wrong way to go about this. A better solution would be to use a RoutedCommand, and the command's CanExecuted method checks to see if your SelectedIndex is greater than or equal to 0.
That much being said, if you still want to use your value converter, you should know that the converter fires whenever the binding source updates. You can change the behavior of the update using the UpdateSourceTrigger property on the Binding. By default this is set to PropertyChanged, but for textboxes it is set to LostFocus (whenever the textbox loses focus the binding is updated).

Related

Pass control to binding by value

I have a converter that I use to determine the size of a child element based on the size of its container (parent).
<Setter Property="Width" Value="{Binding ActualWidth, ElementName=rowsContainer,
Converter={StaticResource sizer}}"/>
public class CellSizeConverter : IValueConverter
{
private readonly int _maxNum = 7;
private readonly int _margin = 0;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
double width = (double)value;
return (width / _maxNum) - _margin;
}
}
This works, and when I resize my window, the size of the components change. Hooray!
I want to factor in the height as well, so I've tried passing in the control itself and getting its properties inside the converter:
<Setter Property="Width" Value="{Binding ElementName=rowsContainer, Converter={StaticResource sizer}}"/>
public class CellSizeConverter : IValueConverter
{
private readonly int _maxNum = 7;
private readonly int _margin = 0;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
ItemsControl rowsContainer = (ItemsControl)value;
return (rowsContainer.ActualWidth / _maxNum) - _margin;
}
}
However, when I do this the size never changes. It seems like the control gets passed to the converter once but never updates. I imagine it has something to do with passing by reference rather than passing by value. Or something like that.
I know I can pass multiple values into the converter using a MultiBinding and an IMultiValue Converter but since all that values I'd want are already wrapped up in a nice little object (the control) it would be so much cleaner in my xaml to just bind it this way.
How can I pass the control such that it updates?
The converter only gets called when the property that you actually bind to is set to a new value.
This means that you cannot bind to the rowsContainer control itself as it never gets when the ActualWidth or ActualHeight property is updated.
A MultiBinding to both ActualWidth or ActualHeight is the way to go here.
How can I pass the control such that it updates?
You can't for the reason already mentioned.
It seems like the control gets passed to the converter once but never updates. I imagine it has something to do with passing by reference rather than passing by value.
Property updates in WPF aren't magic, code has to explicitly call into WPF to tell it a value has been updated.
When you bind ActualWidth, that's actually a dependency property that has a changed event WPF can hook into and be notified by the code changing it. However, when you bind the control itself, that is not a dependency property, and the control doesn't implement INotifyPropertyChanged, so WPF never knows its properties have changed.
I know I can pass multiple values into the converter using a MultiBinding and an IMultiValue Converter but [...]
A MultiBinding allows you to bind to multiple dependency properties and have WPF listen to all their property change notifications at once. I stopped the quote at "but" because it doesn't matter what comes after, that's what you need to do.

How to know if binding has no target in case an object doesn't have that property

I have some classes. Some of them has Color Property, but one does not. I'm using same ListBox User Control for them. I want to hide ColorPicker for those classes that have no such property. I know, I can do a workaround and hide it if DataContext is of certain type, but I want to know if there is a way to check if the binding target isn't just null at a moment, but doesn't exist at all.
I used the proposed converter (returning true/false) with no result, but #mm8 proposal to set FallbackValue to false worked well.
You could specify a FallbackValue for a specific binding that the target property will be set the when the source property is not found: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.fallbackvalue(v=vs.110).aspx
Use a ValueConverter for the binding, and in the Convert method, check for UnsetValue:
<FrameworkElement Property="{Binding SomeProperty, Converter={StaticResource BindingExists}/>
and
public class BindingExists : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == DependencyProperty.UnsetValue)
{
// perhaps do something
return Binding.DoNothing;
}
else if (value == null)
{
// perhaps do something else
}
return value
}
// ...
You can then use DataTriggers to display different templates or whatever, in case of null vs. non-existent value.

Hiding columns from datagrid when autogenerate=false

I create columns in codebehind as such.. each column is bound to a property.
Dim column_selected As New DataGridCheckBoxColumn()
column_selected.Header = "Selected"
column_selected.Binding = New Binding("IsChecked")
dgvResults.Columns.Add(column_selected)
I want to be able to hide a column, based on a checkbox or something of that nature, where I can hide/show them at will.
I've heard about binding visibility to a property Boolean, but i'm not sure how to do that when the columns are created in code behind.
Any idea on how to accomplish this? Say I want to have a single checkbox that hides a specific column, if you unchecked it, it shows it.
If logic of setting column visibility doesn't contains any business logic and this is clear UI operation. Then I think you can just put it in the code-behind, what is wrong with that?
XAML
<CheckBox Checked="HideColumn" Unchecked="UnhideColumn"/>
Code-behind
Protected Sub HideColumn()
'your code
End Sub
Protected Sub UnhideColumn()
'your code
End Sub
You can set Binding normally from code-behind, but since System.Visibilty is enum, you have to use Converter (an instance of IValueConverter interface) to set Binding correctly. There are a lots of possible implementation, the following is an example:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? vis = value as bool?;
return (vis.HasValue && vis.Value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
After that, the only thing you should do is to set the Converter property of your Binding to the new instance of BoolToVisibilityConverter as follows:
column_selected.Binding.Converter = new BoolToVisibilityConverter()
And that's all.

Pass view to viewmodel with datatemplate

I have a window named ParameterEditorView with a ParameterEditorViewModel as DataContext. In the ParameterEditorViewModel I have a list of ParameterViewModel. In the ParameterEditorView I have an ItemsControl whose ItemsSource is binded to the list of ParameterViewModel in the ParameterEditorViewModel. I need the ParameterViewModel to have a reference to the ParameterView (more on that later). In the Resources section of the ParameterEditorView I add the DataTemplate:
<DataTemplate DataType="{x:Type my:ParameterViewModel}" >
<my:ParameterView HorizontalAlignment="Left"/>
</DataTemplate>
So, how can I pass a reference of the ParameterView that is created to show the ParameterViewModel to it?
The reason I need the ParameterView in the ParameterViewModel is the following:
I have a TextBox whose Text property is binded to the PropertyModelView.Name property. But I want to display a default string when the Name is empty or Null. I've tried to set the property value to the default string I want when that happens but the TextBox.Text is not set in this scenario. I do something like this:
private string _name;
public string Name
{
get { return _name; }
set
{
if (value == null || value.Length == 0)
Name = _defaultName;
else
_name = value;
}
}
I've also tried to specifically set the TextBox.Text binding mode to TwoWay without success.
I think this is a defense mechanism to prevent an infinite loop from happening but I don't know for sure.
Any help on this front would also be highly appreciated.
Thanks,
José Tavares
{Binding } has a FallbackValue, btw.
Your question, it confuses me. I'd assume your PVM has a collection of PV's as a public property, which is bound within the UI. Also, I think you're mixing terms. Its Model-View-ViewModel where the ViewModel is the DataContext of the View, and the Model is exposed by the ViewModel via a public property. Sounds like if you're binding the window to a collection of ViewModels they are actually Models. It may seem pedantic, but getting your terms correct will help you research and ask questions.
Another solution would be to add a Converter to your Binding in combination with FallbackValue (I've had to do this, IIRC). That converter would be an IValueConverter that returns "DependencyProperty.UnsetValue" if the string is null or empty. I think this works sometimes because the TextBox will set the bound property to the empty string rather than null if the TB is empty. Here's a little sample to whet your whistle (not guaranteed to work; you need to debug this and tweak it):
public class ThisMightWorkConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
var temp = value as string;
if(string.IsNullOrWhiteSpace(temp))
return System.Windows.DependencyProperty.UnsetValue;
return temp;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return value; // you might need to change this
}
}

WPF Property Data binding to negate the property

Is there any way to change the value of property at runtime in WPF data binding. Let's say my TextBox is bind to a IsAdmin property. Is there anyway I can change that property value in XAML to be !IsAdmin.
I just want to negate the property so Valueconverter might be an overkill!
NOTE: Without using ValueConverter
You can use an IValueConverter.
[ValueConversion(typeof(bool), typeof(bool))]
public class InvertBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool original = (bool)value;
return !original;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool original = (bool)value;
return !original;
}
}
Then you'd setup your binding like:
<TextBlock Text="{Binding Path=IsAdmin, Converter={StaticResource boolConvert}}" />
Add a resource (usually in your UserControl/Window) like so:
<local:InvertBooleanConverter x:Key="boolConvert"/>
Edit in response to comment:
If you want to avoid a value converter for some reason (although I feel that it's the most appropriate place), you can do the conversion directly in your ViewModel. Just add a property like:
public bool IsRegularUser
{
get { return !this.IsAdmin; }
}
If you do this, however, make sure your IsAdmin property setter also raises a PropertyChanged event for "IsRegularUser" as well as "IsAdmin", so the UI updates accordingly.
If you specifically want to do this at XAML end (I am not sure the reason for that, unless you have 100s of similar operation of negate) there are only two ways 1) Using IValueConverter 2)write a XAML Markup Extension (Way too much work for this small task :))
Then the other obvious way is to write another property in your ViewModel , which can return the Negative of the IsAdmin property.
You can't bind to !Property, but you could create a new Binding with an appropriate IValueConverter and change out the entire Binding at runtime. The key is the BindingOperations class, which allows you to change the binding on a particular DependencyProperty.
public static void InvertBinding(DependencyObject target, DependencyProperty dp)
{
//We'll invert the existing binding, so need to find it
var binding = BindingOperations.GetBinding(target, dp);
if (binding != null)
{
if (binding.Converter != null)
throw new InvalidOperationException("This binding already has a converter and cannot be inverted");
binding.Converter = new InvertingValueConverter(); //This would be your custom converter
//Not sure if you need this step, but it will cause the binding to refresh
BindingOperations.SetBinding(target, dp, binding);
}
}
This should give you a general idea; I wouldn't use this for production code, as you'd probably want to generalize it to toggle the converter or whatever else you need to change out at runtime. You could also avoid changing the binding entirely by creating a new property you bind to that encapsulates this 'switching' logic. The last option is probably the best.
You can write a ValueConverter that automatically negates the input before returning it. Have a look at BenCon's blog for a short reading on value converters.

Resources