I'm trying to bind the visibility of a label to the typed length of a PasswordBox control. I'm binding the Visibility property of the label to the output of a converter targeted at the SecurePassword.Length property of the passwordbox.
The binding works fine, but only once, when the app first starts. It doesn't stay in sync. If I seed the password with a long enough value (as in my example), the message is displayed, but it doesn't update as I type or delete text. Clearly, I'm missing something.
I used this question as a template for my implementation.
My Xaml:
<PasswordBox Name="PwdPassword" HorizontalAlignment="Left" MinWidth="120"
Password="abcdefghijklmnopqrstuvwxyz1234567890"></PasswordBox>
<Label Name="LblPasswordMsg" Content="Message" FontWeight="Bold">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Visibility" Value="{Binding UpdateSourceTrigger=PropertyChanged, ElementName=PwdPassword, Mode=OneWay,
Path=SecurePassword.Length, Converter={StaticResource IntLengthVisibilityConverter}}" />
</Style>
</Label.Style>
</Label>
My Converter:
[ValueConversion(typeof(int), typeof(Visibility))]
public class IntLengthVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Do the conversion from int to visibility
int length = (int)value;
Visibility visible = length >= 25 ? Visibility.Visible : Visibility.Hidden;
return visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Do the conversion from visibility to int
return null;
}
}
I think the problem is SecurePassword.Length is not a dependency property, so no automatic notification occurs when its value changes.
Related
Is there a way to bind underline?? I'm trying to achieve the following:
I have VievModel with a bool property:
public bool HomeButtonUnderline { get; set; } = false;
I would then like to control this property in the following function:
public void Home() {
//CurrentPage = ApplicationPage.Home;
//HomeButtonForeground = new SolidColorBrush(Colors.White);
HomeButtonUnderline = true;
SettingsButtonUnderline = false;
}
I could then utilize this control in XAML:
<Button>
<TextBlock Command="{Binding HomeNavCommand}" Underline="{Binding HomeButtonUnderline}"/>
</Button>
The problem is that there isn't an 'Underline' property, instead it is handled by 'TextDecorations':
<Button>
<TextBlock TextDecorations="Underline">
</Button>
So is there a way to control underline using MVVM or even without it??
You may use a DataTrigger in a Style for the TextBlock:
<Button Command="{Binding HomeNavCommand}">
<TextBlock Text="Home">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding HomeButtonUnderline}" Value="True">
<Setter Property="TextDecorations" Value="Underline"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Button>
Use a Converter to convert your model types (in this case, a bool) to the UI types (in this case, a TextDecoration).
public class UnderlineConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return null;
return System.Convert.ToBoolean(value) ? TextDecorations.Underline : null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
And then use it in your binding (after creating a resource in your Window, UserControl App.xaml or whereever you feel is the right place; for this sample I'm just putting it in the button resources)
<Button Command="{Binding HomeNavCommand}">
<Button.Resources>
<local:UnderlineConverter x:Key="UnderlineConverter" />
</Button.Resources>
<TextBlock TextDecorations="{Binding HomeButtonUnderline, Converter={StaticResource UnderlineConverter}"/>
</Button>
Of course, another way to handle this is for your view model to be the right type for the view:
public TextDecorationCollection HomeButtonUnderline { get; set; }
HomeButtonUnderline is already GUI-orientated, so there's probably some code in your viewmodel that assigns a value from the model, so it might as well also convert from bool to TextDecorations.Underline.
This method keeps all the logic in the same file, unlike a converter, although a converter is more reusable. It also has the advantage of being easily unit-testable (unlike a data trigger).
I have an ObservableCollection<MyEntity> and MyEntity has a IsChecked property with a PropertyChanged event.
I have a Button and I would like to change IsEnabled property to true when at least one of MyEntity of the MyObservableCollection is checked.
I created a converter which takes the ObservableCollection and return true when a MyEntity is checked at least.
But the return "null" is returned.
What is wrong ? Thank you for your help.
XAML
<Window.Resources>
<CollectionViewSource x:Key="MyObservableCollection"/>
<src:MyConverter x:Key="MyConverter"/>
</Window.Resources>
<Button IsEnabled="{Binding Converter={StaticResource MyConverter}, Source={StaticResource MyObservableCollection}}"/>
C# Converter
class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (null == value)
return "null";
ReadOnlyObservableCollection<object> items = (ReadOnlyObservableCollection<object>)value;
List<MyEntity> myEntities = (from i in items select (MyEntity)i).ToList();
foreach (MyEntity entity in myEntities)
{
if (entity.IsChecked)
{
return true;
}
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
I think your Binding is wrong. The Converter want's the underlying collection not the CollectionView. And set the CollectionViewSource.Source after InitializeComponent(), the Binding will be refreshed.
<Button IsEnabled="{Binding Path=SourceCollection,
Converter={StaticResource MyConverter},
Source={StaticResource MyObservableCollection}}" />
Since StaticResources are resolved at the time of intializing itself i.e. at the time of InitializeComponent() but till that time your collection is yet not intialized that's why null value is passed to the converter.
So, better choice would be to move that property in your code behind and bind to that property since binding will be resloved after InitializeComponent(). Create property in your code-behind-
public CollectionViewSource MyObservableCollection { get; set; }
and bind to your button -
<Button IsEnabled="{Binding MyObservableCollection, RelativeSource=
{RelativeSource AncestorType=Window}, Converter={StaticResource MyConverter}}"/>
I have a WPF Solution with 2 projects, the first one (ResourcesLibrary) contains common styles and common resources. The other is the WPF application.
I created a style for DataGridRows in the ResourcesLibrary generic.xaml file:
<Style x:Key="DGRowStyle" TargetType="{x:Type DataGridRow}">
<Setter Property="ValidationErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Image Source="/Resources/stop.png"
ToolTip = "{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}},
Path=(Validation.Errors),
Converter={StaticResource errorConverter]}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
I added the .cs converter files to the ResourcesLibrary project:
namespace ResourceLibrary
{
public class ErrorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var errors = value as ReadOnlyObservableCollection<ValidationError>;
if (errors == null)
return "";
return errors.Count > 0 ? errors[0].ErrorContent : "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And added the reference and the static resource:
xmlns:my="clr-namespace:ResourceLibrary"
<!-- CONVERTERS -->
<my:ErrorConverter x:Key="errorConverter" />
But at runtime when, in the main project, I use the DataGridRow style defined in the ResourcesLibrary I get this error:
{"Cannot find resource named 'errorConverter]'. Resource names are case sensitive."}
Do I need to have another project inside my solution for the converters I will use?
Thanks to #Clemens I removed the extra char ']' in the expression "={StaticResource errorConverter]}" and it all works fine.
I have 4 buttons in grid which datacontext is set to an object which has property that indicates what button should be enabled (it's enumerable).
Currenty I have done this in code-behind so that when that specific property changes, it disables all but one depending on the value. It works, but I really don't like to put stuff like this to code-behind. There must be a way to do this in xaml?
I could make own style for all four buttons and then do this with data triggers, but I would prefer more generic approach: use same style for all buttons that somehow applies differently depending on, for example, a button name and value of the property.
Thanks in advance.
You could use a MultiBinding to bind the IsEnabled property to a combination of the control's name and the property from your DataContext, and create a Style to apply it to all buttons in the Grid:
<Grid.Resources>
<local:EqualsConverter x:Key="EqualsConverter"/>
<Style TargetType="Button">
<Setter Property="IsEnabled">
<Setter.Value>
<MultiBinding Converter="{StaticResource EqualsConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Name"/>
<Binding Path="EnabledButtonName"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
And in code:
public class EqualsConverter
: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values.Length == 2 && object.Equals(values[0], values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
You can create an association between the enum you've created and the buttons by having integer references to your enum values and giving those enum values as ConverterParameters for corresponding buttons.
For Eg:
The enum:
public enum myOptions
{
value1 = 1,
value2 = 2,
value3 = 3,
value4 = 4
}
The Binding:
<Button IsEnabled = {Binding Path=myProperty,
Converter = {StaticResource EnumToBoolConverter},
ConverterParameter = 1} />
<Button IsEnabled = {Binding Path=myProperty,
Converter = {StaticResource EnumToBoolConverter},
ConverterParameter = 2} />
And the Converter:
public class EnumToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value == (int)parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
I'm trying to bind element's Height value to Checkbox.IsChecked property. Why that's not working?
<Window.Resources>
<local:BoolToHeightConverter x:Key="BoolToHeightConverter"/>
</Window.Resources>
<Button Name="JustBtn" Content="Hello World"/>
<CheckBox IsChecked="{Binding ElementName=JustButton, Path=Height, Converter=BoolToHeightConverter}" />
[ValueConversion(typeof(Nullable<bool>), typeof(double))]
public class BoolToHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return double.NaN;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
It doesn't even initalize the window. Says:
'IValueConverter' type does not have a public TypeConverter class
There are a couple of problems. First, it looks like you are trying to modify the Height property when the CheckBox is checked. If this is the case, you should implement your logic in the ConvertBack method of the converter, and specify a Mode on the Binding. Secondly, your Binding should use a StaticResource to reference your converter:
<CheckBox IsChecked="{Binding ElementName=JustButton, Path=Height, Converter={StaticResource BoolToHeightConverter}, Mode=OneWayToSource}" />
I'm sorry - my bad: I forgot to attach converter through StaticResource.
Sorry guys...