is it possible to bind the Path property of a binding to another property?
I want to realize this code:
Text="{Binding Path={Binding Path=CurrentPath}}"
So I can adjust dynamically to which Property my actual binding is refering.
Thanks for your Help
Jonny
I worked it out on myself.
Heres the solution, I hope it might help anyone got the same problem like me.
public class CustomBindingBehavior : Behavior<FrameworkElement>
{
public bool IsBinding
{
get
{
return (bool)GetValue(IsBindingProperty);
}
set
{
SetValue(IsBindingProperty, value);
}
}
public string PropertyPath
{
get
{
return (string)GetValue(PropertyPathProperty);
}
set
{
SetValue(PropertyPathProperty, value);
}
}
public static DependencyProperty
PropertyPathProperty = DependencyProperty.Register("PropertyPath", typeof(string),
typeof(CustomBindingBehavior), null);
public static DependencyProperty
IsBindingProperty = DependencyProperty.Register("IsBinding", typeof(bool),
typeof(CustomBindingBehavior), null);
protected override void OnAttached()
{
if (AssociatedObject is TextBlock)
{
var tb = AssociatedObject as TextBlock;
tb.Loaded += new RoutedEventHandler(tb_Loaded);
}
}
private void tb_Loaded(object sender, RoutedEventArgs e)
{
AddBinding(sender as TextBlock, TextBlock.TextProperty);
}
private void AddBinding(DependencyObject targetObj, DependencyProperty targetProp)
{
if (IsBinding)
{
Binding binding = new Binding();
binding.Path = new PropertyPath(this.PropertyPath, null);
BindingOperations.SetBinding(targetObj, targetProp, binding);
}
else
{
targetObj.SetValue(targetProp, this.PropertyPath);
}
}
}
And heres the implementation in XAML:
<TextBlock >
<i:Interaction.Behaviors>
<behaviors:CustomBindingBehavior PropertyPath="{Binding Path=HeaderPropertyBinding}" IsBinding="{Binding Path=HeaderIsBinding}" />
</i:Interaction.Behaviors>
</TextBlock>
Greetings
Jonny
As other posters have mentioned, you can only set a binding on a dependency property - which path is not. The underlying reason is that xaml is source code that gets compiled. At compile time the compiler has no idea what the value of 'CurrentPath' is, and would not be able to compile. Essentially what you are looking to do is runtime reflection of a property value - which could be done using another property in the ViewModel you are binding to, or using a converter.
ViewModel:
public string CurrentValue
{
get
{
var property = this.GetType().GetProperty(CurrentPath);
return property.GetValue(this, null);
}
}
Using a converter:
public class CurrentPathToValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var viewModel = (ViewModel)value;
var property = viewModel.GetType().GetProperty(viewModel.CurrentPath);
var currentValue = property.GetValue(viewModel, null);
return currentValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Of couse these only work if you want to get a simple property of the object - if you want to get something more complex your reflection code is going to get a lot more complex.
Unless you are building something like a property grid, or for some other reason you actually want to introspect the objects running in your application, I would suggest you revisit your design, as reflection is really only suited to a few situations.
Path is not a dependency property, therefore the binding will not work.
Perhaps you could bind to a property that returns another property based on a switch statement and bind to that. Change the 'switch' property and you change the output of the other property.
Just don't forget to include your NotifyPropertyChanged stuff in the switch property for the bound property otherwise your view will not update.
e.g.
private int _mySwitch;
//Set this to determine what the other property will return.
public int SwitchProperty
{
get { return _mySwitch; }
set
{
_mySwitch = value;
NotifyPropertyChanged("MySwitchableProperty");
}
}
public String PropertyA { get; set; }
public String PropertyB { get; set; }
//Bind to this property
public String MySwitchableProperty
{
get
{
switch (SwitchProperty)
{
case 1:
return PropertyA;
break;
case 2:
return PropertyB;
break;
default :
return String.Empty;
break;
}
}
}
I think converter can helps your.
Expample
First control
Text="{Binding Path=CurrentPath}"
Second control
Text="{Binding Path=CurrentPath, Convertor={converters:MyConvertor}}"
Base converter
public abstract class ConvertorBase<T> : MarkupExtension, IValueConverter
where T : class, new()
{
public abstract object Convert(object value, Type targetType, object parameter,
CultureInfo culture);
public virtual object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
#region MarkupExtension members
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
_converter = new T();
return _converter;
}
private static T _converter = null;
#endregion
}
MyConverter
public class MyConverter: ConvertorBase<MyConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (string)value.Equals("blabla") ? "Yes" : "No"; // here return necessary parametr
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Related
When you drag the slider, you get a ConvertBack (expected), but why do I then get a "Convert" straight after that? I'd only expect Convert to be called when its first initialized, or if it was raising a property change notification, but it doesn't.
<Window x:Class="WpfApplication10.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication10"
Title="MainWindow">
<Slider Value="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={l:Converter}}"/>
</Window>
public class Converter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
public partial class MainWindow : Window
{
public double Value { get; set; }
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
}
This is likely occurring because you're using .NET 4, which changed the way bindings work a little bit : http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/
Whereas prior to 4 the binding wouldn't update back (assuming that it didn't need to), the new behavior is to do that by default. Rationale is explained in the linked blog.
Edit: I suppose I should ask if there is a reason you don't want it to convert back, or if it's just curiosity? If you need to prevent converting back, the clearest strategy is probably to keep track of your last converted value to parrot back :
public class Converter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object lastValue;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return lastValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
lastValue = value;
return value;
}
}
There's also the option of setting the binding to OneWayToSource, which would keep it from setting back to the slider value... assuming you don't need to push data back to the slider.
In my application I have 3 data grids in a single xaml file. Based on the User selection I want show one grid and hide other grids.
in my view model class I have Boolean property for each grid and based on the selection I am setting it to true or false.But all grids are visible .
<DataGrid Visibility="{Binding Path=IsGridVisible}" >
In my view model I am setting IsGridVisible value
public bool IsCapexGridVisible
{
get { return isCapexGridVisible; }
set { isCapexGridVisible = value; RaisePropertyChangedEvent("IsCapexGridVisible"); }
}
Please provide your ideas. Thanks
There is a BooleanToVisibilityConverter available to you that converts true to System.Windows.Visibility.Visible and false to System.Windows.Visibility.Collapsed.
So you can take help of this pre built converter and must add it to resources.
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
Create a property of type bool in your ViewModel
bool _dgVisibility;
public bool DataGridVisibility
{
get { return _dgVisibility; }
set
{
_dgVisibility = value;
OnPropertyChanged("DataGridVisibility");
}
}
and you can use it as below
<DataGrid Visibility="{Binding Path=DataGridVisibility, Converter={StaticResource BoolToVis}}"/>
Visibility property on UIElement is not a boolean. It is an enum with three values:
Collapsed Do not display the element, and do not reserve space for it in layout.
Hidden Do not display the element, but reserve space for the element in layout.
Visible Display the element.
So in order to set it properly from ViewModel you should:
- make your property type of Visibility (not best solution in the world)
- Use converter for the binding which will do the trick of translating boolean to visibility
public class BooleanToCollapsedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(Visibility) && value is bool)
{
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
throw new FormatException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Additional converter variant with visibility customization
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
[MarkupExtensionReturnType(typeof(IValueConverter))]
public class BoolToVisibilityConverter : MarkupExtension, IValueConverter
{
[ConstructorArgument("TrueValue")]
public Visibility TrueValue { get; set; }
[ConstructorArgument("FalseValue")]
public Visibility FalseValue { get; set; }
[ConstructorArgument("NullValue")]
public Visibility NullValue { get; set; }
public BoolToVisibilityConverter()
{
TrueValue = Visibility.Visible;
FalseValue = Visibility.Collapsed;
NullValue = Visibility.Collapsed;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return NullValue;
if (value is not bool boolValue)
return null;
return boolValue ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (Equals(value, TrueValue))
return true;
if (Equals(value, FalseValue))
return false;
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Usage:
<someControl ...
xmlns:converters="clr-namespace:ExampleNamespace.Converters;assembly=ExampleAssembly"
...
>
...
Visibility="{Binding IsSearchInProgress,
Mode=OneWay,
Converter={converters:BoolToVisibilityConverter}}"
Visibility="{Binding IsSearchInProgress,
Mode=OneWay,
Converter={converters:BoolToVisibilityConverter TrueValue=Collapsed, FalseValue=Visible}}"
I wanted to experiment with being able to have a converter whose arguments can be bound with the current data context. Can anyone tell me why when reaching the Convert() function, the Source property is always null?
namespace WpfApplication32
{
public class ConverterTest : DependencyObject, IValueConverter
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(DependencyObject), typeof(ConverterTest));
public DependencyObject Source
{
get { return (DependencyObject)this.GetValue(SourceProperty); }
set { this.SetValue(SourceProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
Value = 7;
InitializeComponent();
DataContext = this;
}
public float Value
{
get;
set;
}
}
}
<Window x:Class="WpfApplication32.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication32">
<Slider>
<Slider.Value>
<Binding Path="Value">
<Binding.Converter>
<local:ConverterTest Source="{Binding}"/>
</Binding.Converter>
</Binding>
</Slider.Value>
</Slider>
</Window>
One possible solution is to make your Converter inherit from Freezable instead (Hillberg Freezable trick). Then you can even define your Converter in your Resources and reference it in your binding as an attribute instead of an extra child element.
I have a DataTemplate that is used by a listbox:
<local:BooleanToFontColorConverter x:Key="boolToFontColor" />
<DataTemplate x:Key="ListBox_DataTemplateSpeakStatus">
<Label Width="Auto">
<TextBlock Name="MY_TextBlock" Text="Hello!" Foreground="{Binding Path=MY_COLOR, Converter={StaticResource boolToFontColor}}" />
</Label>
</DataTemplate>
MY_COLOR is the following bit of code:
public class Packet_Class : INotifyPropertyChanged
{
private bool _my_color = false;
public bool MY_COLOR { get { return _my_color; }
set { _my_color = value; RaisePropertyChanged("MY_COLOR"); } }
}
and then when appropriate I set the property, which I think would fire the RaisePropertyChanged function
myPacketClass.MY_COLOR = true;
while boolToFontColor is "trying" to use this bit:
public class BooleanToFontColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (value is Boolean)
{
return ((bool)value) ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Black);
}
return new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
When I change the value of MY_COLOR from true to false, or vice versa, I see no visible changes in my text foreground color during runtime. Is anyone able to give advice as to where I am going wrong? Much appreciated and thank you in advance.
EDIT:
Some additional information to attempt to provide more clarity. I am using my DataTemplate in a ListBox like this:
<ListBox x:Name="MyUserList" ItemTemplate="{StaticResource ListBox_DataTemplateSpeakStatus}" SelectionMode="Extended" />
And in my WPF Window element I set my local namespace to the namespace that my mainwindow.xaml.cs is encapsulated in:
xmlns:local ="clr-namespace:My_NameSpace"
the RaisePropertyChanged method should raise the PropertyChanged event define in the interface and look like:
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged (string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
the converter:
public class BooleanToFontColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (value is Boolean)
{
return ((bool)value) ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Black);
}
return new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
You have to use a SolidColorBrush to make it work.
It works on my environment, let me know if you encounter any trouble.
I have a textblock inside a list view that I need to hide or collapse when it is empty or null. I tried using a string converter but that does not do it.
Any other ideas?
Update # 1:
Here is the code inside the textblock:
Visibility="{Binding Converter={StaticResource StringConverter}}
Here is the converter:
public class StringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return string.IsNullOrEmpty(value.ToString()) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
I would recommend creating text and visibility bindings on your textbox.
Here's an example of the view model properties you'd have.
public String TextBoxText
{
get { return textBoxText; }
set
{
if (value != textBoxText)
{
textBoxText= value;
SetTextBoxVisibility();
OnPropertyChanged("TextBoxText");
}
}
}
private String textBoxText;
public Visibility TextBoxVisibility
{
get { return textBoxVisibility; }
set
{
if (value != textBoxVisibility)
{
textBoxVisibility= value;
OnPropertyChanged("TextBoxVisibility");
}
}
}
private Visibility textBoxVisibility;
public void SetTextBoxVisibility()
{
this.TextBoxVisibility = String.IsNullOrEmpty(this.TextBoxText) ? Visibility.Collapsed : Visibility.Visible;
}
The only thing you've not shown of your code is where you instantiate the converter class. Is this because you're not doing so?
Typically you'd add something like this to app.xaml:
<Application.Resources>
<ResourceDictionary>
<conv:StringConverter x:Key="StringConverter " />
</ResourceDictionary>
</Application.Resources>