I try to get a Button to Enable if all textbox have a value but it doesn´t work and i can´t find out why.
NullToBoolConverter.cs
public class NullToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof(bool));
bool nullResult = false;
if (parameter != null)
nullResult = (bool)converter.ConvertFrom(parameter);
if (value == null)
return nullResult;
else
return !nullResult;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
XAML-Ressourcedict.
<ex:NullToBoolConverter x:Key="NullToBoolConverter"/>
<Style x:Key="okButtonStyle" TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Hostadress, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}" Value="True" />
<Condition Binding="{Binding UserBox, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}" Value="True" />
<Condition Binding="{Binding PasswordTextBox, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}" Value="True" />
<Condition Binding="{Binding PortBox, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
I changed your converter to this, as it just seemed to be a lot simpler...
public class NullToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return false;
string currentValue = value.ToString();
if (string.IsNullOrWhiteSpace(currentValue))
return false;
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
Then your XAML style should look like this...
<ex:NullToBoolConverter
x:Key="NullToBoolConverter" />
<Style
x:Key="okButtonStyle"
TargetType="{x:Type Button}">
<Setter
Property="IsEnabled"
Value="False" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding ElementName=Hostadress, Path=Text, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}"
Value="True" />
<Condition
Binding="{Binding ElementName=UserBox, Path=Text, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}"
Value="True" />
<Condition
Binding="{Binding ElementName=PasswordTextBox, Path=Text, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}"
Value="True" />
<Condition
Binding="{Binding ElementName=PortBox, Path=Text, Converter={StaticResource NullToBoolConverter}, UpdateSourceTrigger=PropertyChanged}"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter
Property="IsEnabled"
Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
Debugging worked and typing in all of the textboxes would enable the button.
Notice that I changed the Binding statements to use the ElementName and Path.
I simplified a little bit code:
[ValueConversion(typeof(object), typeof(bool))]
public class NullToBoolValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var result = value == null;
return parameter != null ? !result : result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
I tried your code and I your only problem is that you are only comparing the value to null and not to string.Empty. Try this
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof(bool));
bool nullResult = false;
if (parameter != null)
nullResult = (bool)converter.ConvertFrom(parameter);
if (value == null || value.ToString() == string.Empty)
return nullResult;
else
return !nullResult;
}
Related
I have a normal Checkbox, where I want to set the IsChecked property to a Binding resource.
The resource is a self written class myClass, which can be null or referenced (means not null).
The Checkbox should be NOT checked, if the assigned object myObject (out of myClass) is null
and checked, if it is not null.
What do I have to write in the IsChecked="..." property in my xaml file?
You can create a style with a DataTrigger that sets the IsChecked property.
<CheckBox>
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="IsChecked" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyObject}" Value="{x:Null}">
<Setter Property="IsChecked" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
An alternative is to create a reusable value converter.
public class NotNullToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Create an instance of the converter in any resource dictionary, e.g. application resources.
<local:NotNullToBooleanConverter x:Key="NotNullToBooleanConverter"/>
This converter can be used directly in the binding.
<CheckBox IsChecked="{Binding MyObject, Converter={StaticResource NotNullToBooleanConverter}}"/>
I have a datagrid with data trigger.
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="ContextMenu" Value="{StaticResource RowMenu}" />
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<DataTrigger x:Uid="DataTrigger_1" x:Name="RowMenuDataTrigger" Binding="{Binding IsModified, diag:PresentationTraceSources.TraceLevel=High}" Value="True">
<Setter x:Uid="Setter_1" Property="Opacity" Value="0.5" />
<Setter x:Uid="Setter_2" Property="ToolTip">
<Setter.Value>
<MultiBinding x:Uid="MultiBinding_1" Converter="{StaticResource ModifiedTypeMessage}">
<Binding x:Uid="Binding_1" Path="Class"></Binding>
<Binding x:Uid="Binding_2" Path="OriginalClassName" ></Binding>
</MultiBinding>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</control:DataGrid.RowStyle>
and IMultiValueConverter looks like:
public class EnTypeModifiedMessageConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2)
{
string current = values[0].ToString();
string original = values[1].ToString();
if (current != original)
{
return string.Format(LanguageHelper.LocalizedString("RawTypeModified"), original.ToUpper());
}
}
return string.Empty;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
when the data changes I get the control hitting this converter. but the UI still not updated. ie opacity and tooltip remains same.
I am trying to set visibility of my stackpanel to collapsed if value is null, but DataTriggerBehavior is not changing it's visibility value, If I change value to something else than it work's, below is the xaml for that:
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding Name}"
Value="{x:Null}">
<Core:ChangePropertyAction TargetObject="{Binding ElementName=spName}"
PropertyName="Visibility"
Value="Collapsed" />
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
One option is to use a simple converter which presumes Name is a String and returns an empty String if value is null else returns the value as a String
public class NullToEmptyStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return "";
else
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
And your xaml will reference NullToEmptyStringConverter NB local is the namespace where I've created the converter class it maybe different in your app
<Page.Resources>
<local:NullToEmptyStringConverter x:Key="NullToEmptyStringConverter"/>
</Page.Resources>
and your DataTrigger
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding Name, Converter={StaticResource NullToEmptyStringConverter}}"
Value="">
<Core:ChangePropertyAction TargetObject="{Binding ElementName=spName}"
PropertyName="Visibility"
Value="Collapsed" />
</Core:DataTriggerBehavior>
I have a viewmodel implementing the IDataErrorInfo interface for validation, for an error it produces a string which is a key in the resource dictionary for a localized string describing the error. However when trying to apply the following style and template to the textbox I get the red border but no tooltip, however removing my converter and using the default one gives me the tooltip but obviously not the localized string.
Can you see what I am doing wrong and/or if there is a better way of doing this?
class MessageCodeToMessageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
string messageCode = (string)value;
try
{
return (string)App.Current.Resources[messageCode];
}
catch (Exception)
{
return messageCode;
}
}
else
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "";
}
}
<local:MessageCodeToMessageConverter x:Key="Converter"></local:MessageCodeToMessageConverter>
<Style x:Key="TextBox" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent, Converter={StaticResource Converter}}"/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="ErrorTemplate">
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
In my opinion if your resource key is defined in the App.xaml then it should work. But your resource key is probably in resource in some user control. (string)App.Current.Resources[messageCode]; search only in the App.xaml resource.
Solution for you can be use multivalueconverter
class MessageCodeToMessageConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
FrameworkElement targetObject = values[0] as FrameworkElement;
if (targetObject == null)
{
return DependencyProperty.UnsetValue;
}
if (value != null)
{
string messageCode = (string)values[1];
try
{
return targetObject.FindResource(messageCode);
}
catch (Exception)
{
return messageCode;
}
}
else
{
return null;
}
}
public object ConvertBack(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "";
}
}
and
<Style x:Key="TextBox" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip">
<Setter.Value>
<MultiBinding Converter="{StaticResource Converter}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}" />
<Binding RelativeSource="{x:Static RelativeSource.Self}" Path="(Validation.Errors)[0].ErrorContent" />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Is there a way to check in DataTrigger if the object is of a particular class?
In fact I would like that DataTrigger answer this question in C#:
if(MyObject is MyClass)
I want it to look something like this in XAML:
<Grid>
<Grid.Triggers>
<DataTrigger Binding="{Binding MyObject}" Value="MyClass?">
<Setter..../>
</DataTrigger>
</Grid.Triggers>
</Grid>
You can use a converter for this:
<Grid>
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding MyString, Converter={StaticResource OConv}, ConverterParameter=System.String}" Value="True">
<Setter Property="Grid.Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
Use the ConverterParameter to state the type of the object you expect to receive...
Converter will return true if it matches or false otherwise...
Example of converter:
public clas s ObjectTypeToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType().ToString() == (string)parameter)
{
return true;
}
return false;
}