I have a test WPF Window with the below XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wpfApplication2="clr-namespace:WpfApplication2"
xmlns:test="clr-namespace:WpfApplication2.Properties"
DataContext="{Binding Path=TestClass}"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<wpfApplication2:TestTypeConverter x:Key="TestConverter"/>
</Window.Resources>
<Grid>
<Grid Visibility="{Binding TestProperty, Converter={StaticResource TestConverter}, ConverterParameter='nottest'}">
<Label Content="Test Label"></Label>
</Grid>
</Grid>
</Window>
I have a test type-converter class which is below:
using System;
using System.Globalization;
using System.Windows.Data;
namespace WpfApplication2
{
class TestTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var valueToTest = (string) value;
var parameterToCheck = (string) parameter;
return valueToTest == parameterToCheck ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
}
TestClass is below:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using WpfApplication2.Annotations;
namespace WpfApplication2
{
public class TestClass : INotifyPropertyChanged
{
public TestClass()
{
TestProperty = "test";
}
private string _testProperty;
public string TestProperty
{
get { return _testProperty; }
set
{
if (_testProperty == value)
{
return;
}
_testProperty = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The visibility property isn't being affected by the type-converter, and the Convert method itself isn't even being called (I have placed a breakpoint which is not being hit).
What am I doing wrong?
Thanks
Your binding seems wrong:
Visibility="{Binding 'test', Converter={StaticResource TestConverter}, ConverterParameter='nottest'}"
change it to:
Visibility="{Binding test, Converter={StaticResource TestConverter}, ConverterParameter='nottest'}"
If the property test does not exist, no converter will be called
Create a resource for the converter parameter (as shown in the snippet below) and use the resource as converter parameter instead of the literal
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication2="clr-namespace:WpfApplication2"
xmlns:test="clr-namespace:WpfApplication2.Properties"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
DataContext="{Binding Path=TestClass}"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<wpfApplication2:TestTypeConverter x:Key="TestConverter"/>
<sys:String x:Key="converterParam">nottest</sys:String>
</Window.Resources>
<Grid>
<Grid Visibility="{Binding TestProperty, Converter={StaticResource TestConverter}, ConverterParameter={StaticResource converterParam}">
<Label Content="Test Label"></Label>
</Grid>
</Grid>
</Window>
Just omit the name of property altogether if you do not have a datacontext and it will work, The following wroked for me.
Visibility="{Binding Converter={StaticResource myTestConverter}}"
// see how Converter keyword is followed by Binding keyword and I did not specify property name after Binding keyword.
Related
I have the following code:
XAML code:
<Window x:Class="combobinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:combobinding"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<local:EnumConverter x:Key="isEnabledConverter" />
</Window.Resources>
<Grid>
<TextBox Text="Hello" IsEnabled="{Binding SectionTitle, Converter={StaticResource isEnabledConverter}}" />
</Grid>
</Window>
C# Code
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty SectionTitleProperty =
DependencyProperty.Register(nameof(SectionTitle),
typeof(SectionTitle),
typeof(MainWindow));
public SectionTitle SectionTitle
{
get { return (SectionTitle)GetValue(SectionTitleProperty); }
set { SetValue(SectionTitleProperty, value); }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
SectionTitle = SectionTitle.TitleBlock;
}
}
public enum SectionTitle
{
Normal,
TitleBlock
}
public class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var sectionType = (SectionTitle)value;
if (sectionType == SectionTitle.Normal)
return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I would expect that the EnumConverter would be called as I am setting the DependencyProperty SectionTitle and any breakpoint inside the method will be hit.
However this doesn't seem to be the case; and the IsEnabled property is not being binded to SectionTitle as I wish.
What's wrong with this code?
The problem is the DataContext. The binding does not find its target.
You can set the context in the declaration of the window. Add this to the Window tag in your XAML:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Define Name property on your Window with Name="MyWindow", then use it in your binding like this:
<TextBox Text="Hello" IsEnabled="{Binding ElementName=MyWindow, Path=SectionTitle, Converter={StaticResource isEnabledConverter}}" />
You need to set the DataContext of your MainWindow. You can esaily do this inside the constructor:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
...
I have the following code in my test application.
XAML file:
<Window x:Class="TestWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfApplication"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<CheckBox Grid.Row="0" IsChecked="{Binding IsItemEnabled}" Margin="11,30,-10,129">IsItemEnabled</CheckBox>
<ComboBox Grid.Row="0" Margin="15,54,352,225" ItemsSource="{Binding Source={StaticResource dataFromEnum}}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled">
<Setter.Value>
<MultiBinding Converter="{StaticResource EnabledConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Content" />
<Binding RelativeSource="{RelativeSource PreviousData}" Path="IsItemEnabled" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
</Window>
ViewModel
using System.ComponentModel;
using System.Runtime.CompilerServices;
using TestWpfApplication.Annotations;
namespace TestWpfApplication
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private bool _isItemEnabled;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsItemEnabled
{
get { return _isItemEnabled; }
set
{
_isItemEnabled = value;
OnPropertyChanged(nameof(IsItemEnabled));
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Converter
public class EnabledConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].ToString() != "Time" || (bool)parameter != false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
How to do the same functionality when the combo box will bind with enum like this one? It`s possible at all?
public enum OpTypes
{
Time,
Cost,
Fuel
}
In the my converter i'm getting {DependencyProperty.UnsetValue} instead true or false and in the second parameter value i'm getting the values of enum. Another issue is that converter fires only first time when i open the combobox.
Or how to do it correct? Any suggestions please. Thanks in advance.
Now it works) Thanks for all
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using TestWpfApplication.Properties;
namespace TestWpfApplication
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private bool _isItemEnabled;
private IList<OptType> _optimizationTypes;
private OptimizeTypes _selectedIndex;
public event PropertyChangedEventHandler PropertyChanged;
public OptimizeTypes SelectedIndex
{
get { return _selectedIndex;}
set
{
_selectedIndex = value;
OnPropertyChanged(nameof(SelectedIndex));
}
}
public IList<OptType> OptimizationTypes
{
get { return _optimizationTypes; }
set
{
_optimizationTypes = value;
OnPropertyChanged(nameof(OptimizationTypes));
}
}
public bool IsItemEnabled
{
get { return _isItemEnabled; }
set
{
_isItemEnabled = value;
InitOptimizationTypes(value);
OnPropertyChanged(nameof(IsItemEnabled));
}
}
public MainWindowViewModel()
{
InitOptimizationTypes(false);
}
private void InitOptimizationTypes(bool isEnabled)
{
OptimizationTypes = new List<OptType>
{
new OptType
{
TypeName = "Time",
IsItemEnabled = isEnabled,
},
new OptType
{
TypeName = "Cost",
IsItemEnabled = true,
},
new OptType
{
TypeName = "Fuel",
IsItemEnabled = true,
}
};
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
using System.Collections;
namespace TestWpfApplication
{
public class OptType
{
public string TypeName { get; set; }
public bool IsItemEnabled { get; set; }
}
}
<Window x:Class="TestWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfApplication"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<CheckBox Grid.Row="0" IsChecked="{Binding IsItemEnabled}" Margin="11,30,15,129">IsItemEnabled</CheckBox>
<ComboBox Grid.Row="0" Margin="15,54,352,231" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding OptimizationTypes}" DisplayMemberPath="TypeName"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding IsItemEnabled}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
</Window
>
In my mvvm application I have a listview.
I like to show/hide some columns of the
listview, depending on the state of the checkbox "Show all columns" (Here: Col1 should be showed/hidden).
That's my very simplified code. What is wrong?!
Obviously this does't work!
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="185">
<Window.Resources>
<local:ConverterHideListViewColumn x:Key="ConverterHideListViewColumn" />
</Window.Resources>
<Grid>
<ListView Height="100" Width="100">
<ListView.View>
<GridView>
<GridViewColumn Header="Col0" Width="40"/>
<GridViewColumn Header="Col1" Width="{Binding ShowAllColumns, Converter={StaticResource ConverterHideListViewColumn}}"/>
</GridView>
</ListView.View>
</ListView>
<CheckBox
Content="Show all columns"
IsChecked="{Binding ShowAllColumns, Mode=TwoWay}"
Margin="40,140,0,0">
</CheckBox>
</Grid>
</Window>
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
VM _vm;
public MainWindow ()
{
InitializeComponent ();
_vm = new VM ();
this.DataContext = _vm;
}
}
/// <summary>
/// Dummy Viewmodel
/// </summary>
public class VM : INotifyPropertyChanged
{
private bool _bShowAllColumns;
public event PropertyChangedEventHandler PropertyChanged;
public VM ()
{
ShowAllColumns = true;
}
public bool ShowAllColumns
{
get { return _bShowAllColumns; }
set { _bShowAllColumns = value; }
}
private void OnPropertyChanged (string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler (this, new PropertyChangedEventArgs (propertyName));
}
}
/// <summary>
/// Converter for setting the ListView-Column width, depending on value VM.ShowAllColumns
/// </summary>
class ConverterHideListViewColumn : IValueConverter
{
public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool bShowAllColumns = false;
double dWidth = 0;
if (value is bool)
{
bShowAllColumns = (bool) value;
dWidth = bShowAllColumns? 40 : 0;
}
return dWidth;
}
public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException ();
}
}
}
A GridViewColumn is not added to the visual tree and doesn't inherit any DataContext so you cannot bind its Width property to a source property of a view model without using a BindingProxy class as suggested here: https://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
This is all you need...
public bool ShowAllColumns
{
get { return _bShowAllColumns; }
set
{
if (_bShowAllColumns != value)
{
_bShowAllColumns = value;
OnPropertyChanged("ShowAllColumns");
}
}
}
The documentation for {X:bind} mentions that casting in a property path is possible (the mention {x:Bind obj.(TextBox.Text)}) as example. I can't see how this should work in the following simple example. I tried various combinations of the type name in parens, but no success.
A common use case would be a two-way binding for a combobox, here I would cast the SelectedValue property from the generic object type to a more specifc type.
Example xaml page MainPage.xaml:
<Page
x:Class="XBindTest4.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XBindTest4"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" >
<StackPanel>
<ComboBox ItemsSource="{x:Bind SampleList, Mode=OneWay}"
SelectedValue="{x:Bind SampleData, Mode=TwoWay}"/>
</StackPanel>
</Page>
Code behind MainPage.xaml.cs:
using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;
namespace XBindTest4 {
public class SampleClass {
}
public sealed partial class MainPage : Page {
public ObservableCollection<SampleClass> SampleList = new ObservableCollection<SampleClass>(new[] {
new SampleClass(),
new SampleClass()
});
public SampleClass SampleData { get; set; }
public MainPage() {
this.InitializeComponent();
}
}
}
Currently, this is not possible with the existing syntax possiblities. That's bad because it adds unnecessary and repeating code to the binding expression: to use {x:Bind} in such a scenario, a converter has to be specified, even if it does nothing.
...
SelectedValue="{x:Bind SampleData, Mode=TwoWay, Converter={StaticResource NoOpConverter}}
...
If a converter is used, the apropriate cast is inserted into the generated file MainPage.g.cs:
this.dataRoot.SampleData = (global::XBindTest4.SampleClass)this.LookupConverter("NoOpConverter").ConvertBack((this.obj2).SelectedValue, typeof(global::XBindTest4.SampleClass), null, null);
Additional sources:
NoOpConverter.cs:
using System;
using Windows.UI.Xaml.Data;
namespace XBindTest4 {
public class NoOpConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, string language)
=> value;
public object ConvertBack(object value, Type targetType, object parameter, string language)
=> value;
}
}
Register the converter in App.xaml:
<Application
x:Class="XBindTest4.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XBindTest4"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<local:NoOpConverter x:Key="NoOpConverter"/>
</ResourceDictionary>
</Application.Resources>
</Application>
MainPage.xaml.cs:
<Page
x:Class="XBindTest4.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XBindTest4"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" >
<StackPanel>
<ComboBox ItemsSource="{x:Bind SampleList, Mode=OneWay}"
SelectedValue="{x:Bind SampleData, Mode=TwoWay, Converter={StaticResource NoOpConverter}}"/>
</StackPanel>
</Page>
I have searched several tutorials and tried each option, but I cannot get my radio buttons to bind. When I try to compile the following code I get the error
The resource "nullableBooleanConverter" could not be resolved
Here is what I currently have in XAML:
<RadioButton GroupName="grp_Option_1" Content="Yes" IsChecked="{Binding Path=OpstionSelected, Mode=TwoWay, Converter={StaticResource nullableBooleanConverter}, ConverterParameter=true}" />
<RadioButton GroupName="grp_Option_2" Content="No" IsChecked="{Binding Path=OptionSelected, Mode=TwoWay, Converter={StaticResource nullableBooleanConverter}, ConverterParameter=false}" />
My CS has
public bool OptionSelected
{
get { return optionSelected; }
set
{
optionSelected = value;
this.OnPropertyChanged("OptionSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
Here is my converter:
[ValueConversion(typeof(bool?), typeof(bool))]
public class SuccessConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool param = bool.Parse(parameter.ToString());
if (value == null)
{
return false;
}
else
{
return !((bool)value ^ param);
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool param = bool.Parse(parameter.ToString());
return !((bool)value ^ param);
}
}
Any help is greatly appreciated!
Try to add the following lines in your <Window.Resources>
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:Converters="clr-namespace: here.yournamespace.converts">
<Window.Resources>
<Converters:SuccessConverter x:Key="nullableBooleanConverter" />
</Window.Resources>
<Grid>
</Grid>
</Window>