Simple WPF IValueConverter and DataTrigger not working together - wpf

I've been having trouble using a value converter with a data trigger. In some of my code it seems like the DataTrigger's Path is being applied to the root element, rather than the element which the style applies to.
I created a simple test case, and I don't understand its behavior. I'm expecting the Button to turn red or blue depending on which value is being fed to the DataTrigger's converter, but the Button isn't being affected at all!
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SimpleWpfApplication"
x:Class="SimpleWpfApplication.SimpleUserControl"
ToolTip="UserControl ToolTip">
<UserControl.Resources>
<local:SimpleConverter x:Key="SimpleConverter" />
</UserControl.Resources>
<Button ToolTip="Button ToolTip">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=ToolTip, Converter={StaticResource SimpleConverter}}"
Value="Button ToolTip">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger
Binding="{Binding Path=ToolTip, Converter={StaticResource SimpleConverter}}"
Value="UserControl ToolTip">
<Setter Property="Background" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</UserControl>
And a simple converter:
class SimpleConverter : 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)
{
throw new InvalidOperationException("SimpleConverter is a OneWay converter.");
}
}
Why isn't Convert being called? Why doesn't the Button turn red or blue?

Found the answer in another StackOverflow question: What’s wrong with my datatrigger binding?
The answer is to add RelativeSource={RelativeSource Self} to the binding:
<DataTrigger Binding="{Binding Path=ToolTip,
RelativeSource={RelativeSource Self},
Converter={StaticResource SimpleConverter}}" />

Related

how to disable a button when a textbox becomes readonly?

I have a button and text box inside a stackpanel, when text box gets readonly state then the button should disable, i want to do this in XAML only is there any way to do this?
The binding is easy to define but you need a converter to invert the boolean value. Since you want to enable (IsEnabled = true) the Button when the TextBox is not readonly (IsReadOnly = false).
XAML
<StackPanel>
<StackPanel.Resources>
<local:InvertBooleanConverter x:Name="invertBoolConverter"/>
</StackPanel.Resources>
<TextBox x:Name=textBox />
<Button IsEnabled={Binding IsReadOnly, ElementName=textBox, Converter={StaticResource invertBoolConverter}}/>
</StackPanel>
local is a namespace you define in your UserControl that points to the namespace of the InvertBooleanConverter
xmlns:local="clr-namespace:NamespaceOfTheInvertBooleanConverter"
Converter
public class InvertBooleanConverter: IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
bool currentValue = System.Convert.ToBoolean(value);
return !currentValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
bool currentValue = System.Convert.ToBoolean(value);
return !currentValue;
}
}
<StackPanel>
<TextBox x:Name="targetTB">Hello</TextBox>
<Button Content="Test Button">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=targetTB, Path=IsReadOnly}" Value="True">
<Setter Property="Button.IsEnabled" Value="False"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=targetTB, Path=IsReadOnly}" Value="False">
<Setter Property="Button.IsEnabled" Value="True"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
try this, it can work as you need.

stackpanel visibility based on label content not working

I have a stack panel that I want to make visible based on a label's content. Just not sure why it isnt working for me. What's highlighted in bold is what I want to hide. Any suggestion?
<StackPanel Orientation="Horizontal">
<Label Nane="lblCarrier" Content="{Binding Path=Carrier}" />
**<StackPanel Orientation="Horizontal">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding Content, ElementName=lblCarrier}" Value="">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label x:Name="lblCarrierGrade" Content="Grade Carrier:" />
<TextBox x:Name="txtCarrierGrade1" />
<TextBox x:Name="txtCarrierGrade2" />
</StackPanel>**
It could be that the Content is null rather than String.Empty.
You could try using TargetNullValue
<DataTrigger Binding="{Binding Content, ElementName=lblCarrier,TargetNullValue=''}" Value="">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
Why not using a converter? Add a class file to you project like this:
class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return string.IsNullOrEmpty(value as string) ? Visibility.Hidden : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your Window definition add this:
xmlns:myNamespace="clr-namespace:[YourProjectName]"
Then somewhere in the resources add this
<myNamespace:VisibilityConverter x:Key="myConverter"/>
Now you can use it:
<Style TargetType="StackPanel">
<Setter Property="Visibility"
Value="{Binding Content, ElementName=lblCarrier,
Converter = {StaticResources myConverter}}"/>

WPF Progress bar

WPF: I have a Problem in Progressbar i want it to show when the operation is not finished and when my operation is finished it will hide. Please show me understandable example so i can apply it to my work. Thanks in advance!
you can do that in different scenarios.
using triggers, (I'd prefer that)
<ProgressBar Maximum="100" Margin="10,107,232,168" Value="0" Name="progr">
<ProgressBar.Resources>
<Style TargetType="{x:Type ProgressBar}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}" Value="100">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Resources>
</ProgressBar>
Using converters
<Grid>
<Grid.Resources>
<delWpf:VisibilityConverter x:Key="conv"/>
</Grid.Resources>
<ProgressBar Name="prog2" Minimum="0" Maximum="100"
Value="{Binding CurrentIndex, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=Value, Mode=OneWay, Converter={StaticResource conv}}" />
</Grid>
and converter
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Math.Abs((double)value - 100) < 0.001 ? Visibility.Hidden : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You might use Extended WPF Toolkit which has a BusyIndicator control,
http://wpftoolkit.codeplex.com/wikipage?title=BusyIndicator
Samples are included in the download.
For your information, Microsoft introduces BusyIndicator in Silverlight first (but fails to ship one for WPF) as a replacement of progress bar.

WPF Data Binding and Formatting

I have a bool property in my ViewModel called IsConnected and I would like to bind it to a TextBlock in my MainWindow. Rather than have the textblock read true or false I need it to say Connected or Disconnected instead. Forgive me because I'm new to WPF. If someone could give me a head start I can take it from there but I'm not sure how to figure out what I need.
Easiest way is probably to create a custom converter which converts your bool value to a string. Search anywhere for IValueConverter and/or WPF.
public class BoolToConnectedConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if((bool)value)
return "Connected";
else
return "Disconnected";
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
add xmlns:
xmlns:converter="clr-namespace:MyProjectNameSpace"
add resource to XAML (change to whatever element needed)
<Window.Resources>
<converter:BoolToConnectedConverter x:Key="connectedConverter" />
</Window.Resources>
in XAML:
<TextBlock Text={Binding IsConnected, Converter={StaticResource connectedConverter}" />
I'd generally prefer to just add a property to the view model (I really dislike value converters), but here's a simple way to accomplish what you're trying to do using a style:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="Connected"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="False">
<Setter Property="Text" Value="Disconnected"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Edit
Note that once you get used to using data triggers, you can make all kinds of modifications to your view without touching your view model. For instance:
<StackPanel>
<Image Source="images\connected.png">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Image Source="images\disconnected.png">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</StackPanel>
Using ViewModel, you write two property wrap, and notify changes in the real property.
So that whenever the value is changed, you the string representation will update and bind to controls, while you can still use the bool property in the code.
public string IsConnectedStr{
get{
return IsConnected?"Connected":"Disconnected";
}
}
public bool IsConnected{
get{
return _isConnected;
}
set{
_isConnected=value;
PropertyChanged("IsConnected");
PropertyChanged("IsConnectedStr");
}
}
You could do this in two ways
1) Write a converter
2) Change the function in the ViewModel so that it returns the desired string instead of a bool
The easiest way is #2, but if you really need the bool value somewhere else in your code you go with #1 (google converter and wpf)
Take a look at value converters.
http://www.wpftutorial.net/ValueConverters.html
public class BoolToConnectedConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var isConnected = (bool)value;
return isConnected ? "Connected" : "Disconnected";
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException("Not required for read-only values");
}
}
In your XAML:
<Window.Resources>
<l:BoolToConnectedConverter x:Key="boolToConnectedConverter" />
</Window.Resources>
<Grid>
<Label Content="{Binding IsConnected, Converter={StaticResource boolToConnectedConverter}}" />
</Grid>

WPF Datatrigger not firing when expected

I have the following XAML:
<TextBlock Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Margin="0,0,5,0"/>
<TextBlock Text="items selected">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Value="1">
<Setter Property="TextBlock.Text" Value="item selected"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
The first text block happily changes with SelectedItems.Count, showing 0,1,2, etc. The datatrigger on the second block never seems to fire to change the text.
Any thoughts?
Alternatively, you could replace your XAML with this:
<TextBlock Margin="0,0,5,0" Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count}"/>
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="items selected"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Value="1">
<Setter Property="Text" Value="item selected"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Converters can solve a lot of binding problems but having a lot of specialized converters gets very messy.
The DataTrigger is firing but the Text field for your second TextBlock is hard-coded as "items selected" so it won't be able to change. To see it firing, you can remove Text="items selected".
Your problem is a good candidate for using a ValueConverter instead of DataTrigger. Here's how to create and use the ValueConverter to get it to set the Text to what you want.
Create this ValueConverter:
public class CountToSelectedTextConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((int)value == 1)
return "item selected";
else
return "items selected";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Add the namespace reference to your the assembly the converter is located:
xmlns:local="clr-namespace:ValueConverterExample"
Add the converter to your resources:
<Window.Resources>
<local:CountToSelectedTextConverter x:Key="CountToSelectedTextConverter"/>
</Window.Resources>
Change your second textblock to:
<TextBlock Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count, Converter={StaticResource CountToSelectedTextConverter}}"/>

Resources