This question already has answers here:
WPF Binding without path with converter, update
(3 answers)
Closed 5 years ago.
I have a binding:
<TextBlock Text="{Binding Converter={local:Converter}}" />
with converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var vm = (ViewModel)value;
return vm.SomeProperty;
}
Changing SomeProperty doesn't update the value on screen. Converter is not called.
How do I update such binding from within view model?
Note: in real project converter will be used to perform some calculation and returning result. In fact it will even be MultiBinding with similar bindings to different view models. I had problem with it and was able to narrow down the case to a simple Binding, where binding is not used to bind to a property, but like this.
MCVE xaml:
<StackPanel>
<TextBlock Text="{Binding Converter={local:Converter}}" />
<Button Content="..." Click="Button_Click" />
</StackPanel>
and code:
public partial class MainWindow : Window
{
ViewModel _vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
}
void Button_Click(object sender, RoutedEventArgs e) => _vm.SomeProperty += "b";
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
string _someProperty = "a";
public string SomeProperty
{
get { return _someProperty; }
set
{
_someProperty = value;
OnPropertyChanged(nameof(SomeProperty));
}
}
}
public class Converter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var vm = (ViewModel)value;
return vm.SomeProperty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
if the ViewModel is set to DataContext of the View. You can simply bind the Property by using <TextBlock Text="{Binding SomeProperty}" />
From the comments, if you would bind the whole viewModel, because the converter do more stuff, than only convert type, or some thing. have a look at this: answer
Related
I’m trying to learn XAML (without much success) – be gentle.
I have a toggle button, a slider and a button. This is what I am trying to do:
When the slider’s value is changed the toggle button should turn off – works fine.
When the button is pressed, set the slider value and keep the toggle button in whichever state it is – does not work; after I push the button, (1) does not work anymore. It behaves like the IsChecked binding was cleared.
Ideally I would like to write everything in XAML, but I guess it is not possible.
// MainWindow.xaml.cs
public class ValueToFalse : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var saved = toggle.IsChecked;
slider.Value = 2;
toggle.IsChecked = saved;
}
}
<!--App.xaml-->
<local:ValueToFalse x:Key="ValueToFalse"/>
<!--MainWindow.xaml-->
<ToggleButton
Name="toggle"
Content="toggle"
HorizontalAlignment="Left"
Margin="479,64,0,0"
VerticalAlignment="Top"
Height="108"
Width="192"
IsChecked="{Binding Value, Mode=OneWay, ElementName=slider, Converter={StaticResource ValueToFalse} }"
/>
<Slider
Name="slider"
HorizontalAlignment="Left"
Margin="167,105,0,0"
VerticalAlignment="Top"
Width="233"
Height="35"
/>
<Button
Content="Button"
HorizontalAlignment="Left"
Margin="346,340,0,0"
VerticalAlignment="Top"
Click="Button_Click"
/>
I didn't quite understand what you want to implement.
But nevertheless, you have an obvious mistake that #Clemens pointed out - if you need to assign a Dependency Property value and keep the binding from it, then the binding must be in the direction of the TwoWay or OneWaySource source.
Since you do not need to assign a value to the source, but you need to store the direction to the source in the binding, you can solve this by implementing a converter.
I show the full implementation of the converter with markup extension. This may be useful to you in the future.
[ValueConversion(typeof(object), typeof(bool))]
public class ValueToFalseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// This value tells the binding that the result of the conversion should not be assigned.
return Binding.DoNothing;
}
private ValueToFalseConverter() { }
public static ValueToFalseConverter Instance { get; } = new ValueToFalseConverter();
}
[MarkupExtensionReturnType(typeof(ValueToFalseConverter))]
public class ValueToFalseExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ValueToFalseConverter.Instance ;
}
}
<ToggleButton
----------
----------
IsChecked="{Binding Value,
Mode=TwoWay,
ElementName=slider,
Converter={local:ValueToFalse}}"
/>
how can i solve this using MVVM pattern, and I am using Devexpress MVVM. I have many textbox in form.
And i need to set the textbox text into "[blank]" when the user press the Ctrl+B and the current text of the textbox is null or ""
But i am looking for a way to use the IValueConverter if possible
I have a class similar to this
public class BlankText : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (string.IsNullOrEmpty(value.ToString()))
return "[blank]";
else
return value;
}
}
And i have this code in the resources
<UserControl.Resources>
<c:BlankText x:Key="BlankText"/>
</UserControl.Resources>
This is my TextBox
<TextBox Text="{Binding District}" >
<TextBox.InputBindings>
<KeyBinding Gesture="Ctrl+B">
</KeyBinding>
</TextBox.InputBindings>
</TextBox>
But my problem is how can i call it on keypress? Am i doing it right?
In order to perform actions using KeyBinding, you can't use IValueConverter. IValueConverters are for converting values, not performing actions. What you need is to define a class that implements ICommand, and then assign an instance of that class to KeyBinding.Command.
public class BlankCommand : ICommand
{
public MyViewModel ViewModel { get; }
public BlankCommand(MyViewModel vm)
{
this.ViewModel = vm;
}
public void Execute(object parameter)
{
// parameter is the name of the property to modify
var type = ViewModel.GetType();
var prop = type.GetProperty(parameter as string);
var value = prop.GetValue(ViewModel);
if(string.IsNullOrEmpty(value))
prop.SetValue(ViewModel, "[blank]");
}
public boolean CanExecute(object parameter) => true;
public event EventHandler CanExecuteChanged;
}
Then create an instance of this class and attach it to your ViewModel so that the KeyBinding can access it:
<TextBox Text="{Binding District}">
<TextBox.InputBindings>
<KeyBinding Gesture="Ctrl+B" Command="{Binding MyBlankCommand}" CommandParameter="District"/>
</TextBox.InputBindings>
</TextBox>
Changing the text to say "[blank]" when the user presses a keyboard shortcut is a weird UX pattern, however. I would suggest adding a placeholder to the text box instead.
In the following DataTemplate, the first binding doesn't work while the 2nd one works, and I would like to know why.
<local:IsEnabledConverter x:Key="isEnabled"/>
<local:Boolean2TextConverter x:Key="txtConverter"/>
<DataTemplate x:Key="fileinfoTemplate" DataType="{x:Type local:MyFileInfo}">
<StackPanel>
<Label x:Name="1stLabel" Content="{Binding Path=Filename}" IsEnabled="{Binding Path=., Converter={StaticResource isEnabled}}"/> <--- doesn't work
<Label x:Name="2ndLabel" Content="{Binding Path=IfPrint, Converter={StaticResource txtConverter}}" IsEnabled="{Binding Path=IsChecked, ElementName=ckBox}"/> <--- works
<CheckBox x:Name="ckBox" IsChecked="{Binding Path=IfPrint}" IsEnabled="{Binding Path=IsValid}" Style="{StaticResource printCkBox}"/>
</StackPanel>
</DataTemplate>
IsEnabledConverter:
class IsEnabledConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
MyFileInfo f = value as MyFileInfo;
return f.IsValid && f.IfPrint;
}
//... omit ConvertBack NotImplementedException stuff
}
Boolean2TextConverter:
class IsEnabledConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
Boolean b = (Boolean)value;
return b.ToString();
}
//similarly omit ConvertBack here
}
Code for MyFileInfo:
public class MyFileInfo {
public string IfPrint {
get;
set;
}
public string IsValid {
get;
set;
}
...
}
Problem: When the CheckBox is toggled, the 2nd Label grays out and shows "false", or becomes normal and shows "true", as it should. However, the first Label doesn't change at all; its IsEnabled state is supposed be the conjunction of two Booleans, one of which is changed by the CheckBox. What is wrong? (note that the IsEnabledConverter is called once upon GUI initialization, but not called again when its binding source changes.)
There are 2 issues here. First you have to implement INotifyPropertyChanged for the ViewModel MyFileInfo. Secondly you have to use MultiBinding here. Because I don't think we have some way to trigger updating the target (such as when toggling the CheckBox) if you bind the whole view model to the IsEnabled target. So here is how it should be done:
Your view model:
public class MyFileInfo : INotifyPropertyChanged {
bool _ifPrint;
bool _isValid;
public bool IfPrint {
get { return _ifPrint; }
set {
if(_ifPrint != value) {
_ifPrint = value;
OnPropertyChanged("IfPrint");
}
}
}
public bool IsValid {
get { return _isValid; }
set {
if(_isValid != value) {
_isValid = value;
OnPropertyChanged("IsValid");
}
}
}
//Implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string prop){
var handler = PropertyChanged;
if(handler != null) handler(this, new PropertyChangedEventArgs(prop));
}
//.... should do the same for the remaining properties....
//...
}
Here is the converter used for MultiBinding, which should implement IMultiValueConverter (instead of IValueConverter):
class IsEnabledConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture) {
if(values.Length == 2){
return (bool) values[0] && (bool) values[1];
}
return false;
}
//... omit ConvertBack NotImplementedException stuff
}
Here is the modifed XAML (to use MultiBinding instead):
<Label x:Name="firstLabel" Content="{Binding Path=Filename}">
<Label.IsEnabled>
<MultiBinding Converter="{StaticResource isEnabled}">
<Binding Path="IsValid"/>
<Binding Path="IfPrint"/>
</MultiBinding>
</Label.IsEnabled>
</Label>
Now one of IsValid and IfPrint changing will trigger the MultiBinding's Converter. Here you can also bind to IsChecked of the CheckBox directly instead of indirectly via IfPrint.
PS: Note Name used in XAML (as well as in codebehind) must not start with number.
Since the instance of MyFileInfo does not change while you check/uncheck the checkbox hence IsEnabledConverteris not getting called.
In order to Enable/Disable your 1stLabel depending on two properties, either use MultiValueConverter or use MultiDataTrigger by applying Style to your Label.
So I'm trying to build out a project that will allow a user to type some text into a textbox on the left side of the form and that will filter out the available items from my datasource list.
<Label Content="Enter item name below"></Label>
<TextBox Name="SearchTermTextBox" TabIndex="0" Text="" />
I was under the impression I could bind to the datasource the list then use a converter to filter out the items that were unlike the string.
<ListBox DataContext="{Binding Colors}">
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource FilterTextValueConverter}" ConverterParameter="{Binding ElementName=SearchTermTextBox, Path=Text}" />
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
//etc...
</ListBox.ItemTemplate>
</ListBox>
However, you can't bind to an elementname in the converterparameter unless you use something called a dependency property.
Edit: Seeing as I've created confusion with the code above, here's the converter I'm trying to bind:
public class FilterTextValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var trackedColors = value as List<Colors>;
if (trackedColors != null)
return (trackedColors).Where(item => item.ColorName.Contains(parameter.ToString())).ToList();
return null;
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Colors
{
public String ColorName;
public String Description;
}
What is wrong with my approach here? Clearly I'm angering the WPF gods since this is a fairly straightforward operation but I'm being denied it on principle. Any help would be appreciated.
Simple binding with converter will work here, no need for MultiBinding.
<ListBox ItemsSource="{Binding Path=Text, ElementName=SearchTermTextBox,
Converter="{StaticResource FilterTextValueConverter}">
......
</ListBox>
Assuming FilterTextValueConverter is implementing IValueConverter, you can access text from value passed to Convert method.
public class FilterTextValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
string text = value.ToString(); // TEXT for textBox can be accessed here.
return new List<string>(); // Return filtered list from here.
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
UPDATE
In case you want to pass multiple bindings to converter, use IMultiValueConverter because ConverterParameter is not Dependency property, hence cannot be bound.
XAML
<ListBox DataContext="{Binding Colors}">
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource FilterTextValueConverter}">
<Binding/>
<Binding ElementName="SearchTermTextBox" Path="Text"/>
</MultiBinding>
</ListBox.ItemsSource>
</ListBox>
Converter
public class FilterTextValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
var trackedColors = values[0] as List<Colors>;
if (trackedColors != null && !String.IsNullOrEmpty(values[1].ToString()))
return (trackedColors).Where(item =>
item.ColorName.Contains(values[1].ToString())).ToList();
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I continued looking into this issue well after the accepted answer was posted and working for me. What I discovered is that it's a fairly trivial task to wrap the control you're trying to get a new dependencyproperty out of to allow for proper binding.
I will not be accepting my own answer to this determined so much later, but this seems (in my amateur opinion) like a much more elegant solution than adding a converter despite being a bit more complex:
Note that this is for a new dependency on the caretindex property of a textbox, not for the original question on binding, but it just requires some smart renaming to get it working ;).
public class TextBoxDependencyWrapper : TextBox
{
public static readonly DependencyProperty CaretIndexProperty = DependencyProperty.Register(
"CaretIndex", typeof (int), typeof (TextBoxDependencyWrapper), new FrameworkPropertyMetadata(default(int), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CaretIndexChanged ));
protected override void OnKeyUp(KeyEventArgs e) //Event that changes the property we're trying to track
{
base.OnKeyUp(e);
CaretIndex = base.CaretIndex;
}
protected override void OnKeyDown(KeyEventArgs e) //Event that changes the property we're trying to track
{
base.OnKeyDown(e);
CaretIndex = base.CaretIndex;
}
public new int CaretIndex
{
get { return (int) GetValue(CaretIndexProperty); }
set { SetValue(CaretIndexProperty, value); }
}
}
Let's say I have a ribbon tab name A (name="_tabA") and B (name="_tabB").
How can I disable or hide tab A or B dynamically?
I use VS2010 with RibbonControlsLibrary.dll.
<ribbon:RibbonTab Visibility="{Binding ShowThisRibbonTab, Converter=...}">
Where ShowThisRibbonTab is a property of your ViewModel and the Converter is most likely a BooleanToVisibilityConverter.
Alternatively, if you're not doing MVVM, you can just give it a name and set the Visibility
Without MVVM
I could easily hide/show with _tabA.Visibility = Visibility.Collapsed or Visibility.Visible.
With MVVM
The .xaml.cs code
Make the class inherit also from INotifyProperty
Make a property to raise event when property is modified
Setup DataContext.
Make Converter code.
The main code is as follows
public partial class MainWindow : RibbonWindow, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public const string NamePropertyName = "VisibleA";
private bool _visibleA = true;
public bool VisibleA
{
get
{
return _visibleA;
}
set
{
_visibleA = value;
RaisePropertyChanged(NamePropertyName);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The converter code is as follows
[ValueConversion(typeof(bool), typeof(Visibility))]
internal class CheckVisibleA : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool? val = value as bool?;
string param = parameter as string;
if (value != null)
{
if (val == true)
{
return Visibility.Visible;
}
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The .xaml code
This XAML has two ribbon tabs: _ribboHome and _ribbonHelp. And the property of "VisibleA" controls the visibility. When I click the checkbox, the VisibleA property turns on/off, and the _ribbonHome is visbile/collapsed accordingly.
<ribbon:Ribbon DockPanel.Dock="Top" Title="teusje.wordpress.com" >
<ribbon:RibbonTab Header="Home" Name="_ribbonHome" Visibility="{Binding Path=VisibleA, Converter={StaticResource CheckVisibleA}, ConverterParameter=Show}">
<ribbon:RibbonGroup Name="Clipboard" Header="Clipboard">
<ribbon:RibbonButton Command="{StaticResource hwc}" CommandParameter="Hello, smcho" Label="Copy" LargeImageSource="Images/LargeIcon.png" />
</ribbon:RibbonGroup>
</ribbon:RibbonTab>
<ribbon:RibbonTab Header="Help">
<ribbon:RibbonGroup Name="_ribbonHelp" Header="Help this">
<ribbon:RibbonButton Command="{StaticResource hwc}" CommandParameter="Hello, smcho" Label="Copy Help" LargeImageSource="Images/LargeIcon.png"/>
<ribbon:RibbonCheckBox IsChecked="{Binding Path=VisibleA}"/>
</ribbon:RibbonGroup>
</ribbon:RibbonTab>