wpf: override label content when currently bound item is null - wpf

I want to set a Label (or TextBox) default string as long as the binding value is null. This works fine for any other property than Content, for example:
<Label Content="{Binding Source={StaticResource pumpCurvesViewSource}, Path=/Label}">
<Label.ContentStringFormat>Details for pump curve: {0}</Label.ContentStringFormat>
<Label.Style>
<Style TargetType="Label" BasedOn="{StaticResource header}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource pumpCurvesViewSource}, Path=/}" Value="{x:Null}">
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
The above will change the background of the label to red as long as there is no CurrentItem in the pumpCurvesViewSource, but what I really want to do is change the background trigger to this:
<Setter Property="Content" Value="No pump curve selected"></Setter>
But this does not work (I'm guessing it is because the Content is already bound and my setting would override the binding).
Does anyone know how to accomplish this?

have you tried TargetNullValue?
<Label Content="{Binding Source={StaticResource pumpCurvesViewSource}, Path=/Label, TargetNullValue='No pump curve selected'}">

If found a way of accomplishing the same thing, but with a bit more code than I liked:
<Label Content="{Binding Source={StaticResource pumpCurvesViewSource}, Path=/Label}">
<Label.ContentStringFormat>Details for pump curve: {0}</Label.ContentStringFormat>
<Label.Style>
<Style TargetType="Label" BasedOn="{StaticResource header}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource pumpCurvesViewSource}, Path=/, Mode=OneWay, Converter={StaticResource isNullConverter}}" Value="True">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Label Content="No pump curve selected">
<Label.Style>
<Style TargetType="Label" BasedOn="{StaticResource header}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource pumpCurvesViewSource}, Path=/, Mode=OneWay, Converter={StaticResource isNullConverter}}" Value="False">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>

the shortest answer would be:
<Label Content="{Binding Source={StaticResource pumpCurvesViewSource, FallbackValue=Your Default String}, Path=/Label}">

Related

WPF deselect ComboBox with CheckBox - ComboBox with reset item

I am using DataTrigger to deselect ComboBox with CheckBox (Unchecked == deselected).
What i Need is:
when i select some item from ComboBox then CheckBox need to become
Checked.
When i uncheck CheckBox then ComboBox need to deselect.
CheckBox cannot be checked if ComboBox is deselected (or if ComboBox is deselected after CheckBox is Checked ComboBox should be selected with first item)
Here is XAML (this is working version and need to be changed). Solution must be pure XAML (no C# code)!
<CheckBox HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center" Margin="90,519,13,0" VerticalAlignment="Top" Height="21" Width="Auto">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
<Style.Resources>
<Style TargetType="Path">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
</Style.Resources>
<Setter Property="FlowDirection" Value="RightToLeft" />
<!--<Setter Property="IsChecked" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=comboBoxEventDevice, UpdateSourceTrigger=PropertyChanged}" Value="{x:Null}">
<Setter Property="IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>-->
</Style>
</CheckBox.Style>
<ComboBox x:Name="comboBoxEventDevice" FlowDirection="LeftToRight" SelectionChanged="comboBoxEventDevice_SelectionChanged" Width="Auto">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="SelectedIndex" Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource AncestorType={x:Type CheckBox}}}" Value="True">
<Setter Property="SelectedIndex" Value="0" />
</DataTrigger>
<!--<DataTrigger Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource AncestorType={x:Type CheckBox}}}" Value="False">
<Setter Property="SelectedIndex" Value="1" />
</DataTrigger>-->
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</CheckBox>
UPDATE:
I try to not use nested controls (combobox inside checkbox) so i change XAML bu everything is same as before:
<CheckBox x:Name="checkBoxEventDevice" Margin="70,600,13,0" VerticalAlignment="Top" Height="21"/>
<ComboBox x:Name="comboBoxEventDevice" Margin="90,519,10,0" VerticalAlignment="Top" SelectionChanged="comboBoxEventDevice_SelectionChanged">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="SelectedIndex" Value="1" />
<Style.Triggers>
<!--<DataTrigger Binding="{Binding Path=IsChecked, ElementName=checkBoxEventDevice}" Value="True">
<Setter Property="SelectedIndex" Value="0" />
</DataTrigger>-->
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=checkBoxEventDevice}" Value="false">
<Setter Property="SelectedIndex" Value="-1" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
Problem is in <Setter Property="SelectedIndex" Value="1" /> line. If i remove it then ComboBox is deselected when datatrigger is off but if i keep it then when datatrigger is off combobox is set to second item. So i need some line where i can tell: do nothing, do not deselect combobox, keep current state.
I found solution and these features are satisfied :
When i select some item from ComboBox then CheckBox need to become Checked.
When i uncheck CheckBox then ComboBox need to deselect.
If ComboBox is in deselected state, and when CheckBox is Checked ComboBox should be selected with first item (if items exist elsewhere CheckBox stays checked and ComboBox deselected).
Here is XAML:
<CheckBox HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center" Margin="90,519,13,0" VerticalAlignment="Top" Height="21">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
<Style.Resources>
<Style TargetType="Path">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
</Style.Resources>
<Setter Property="FlowDirection" Value="RightToLeft" />
<Setter Property="IsChecked" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=comboBoxEventDevice}" Value="-1">
<Setter Property="IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
<ComboBox x:Name="comboBoxEventDevice" FlowDirection="LeftToRight" SelectionChanged="comboBoxEventDevice_SelectionChanged">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="SelectedIndex" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource AncestorType={x:Type CheckBox}}}" Value="False">
<Setter Property="SelectedIndex" Value="-1" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</CheckBox>
Now if someone need WPF nullable ComboBox all what need to do is to simple replace existing Combobox tag in its XAML code with XAML above and change x:Name and ElementName of ComboBox to original ComboBox in these lines:
<DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=comboBoxEventDevice}" Value="-1">
<ComboBox x:Name="comboBoxEventDevice" FlowDirection="LeftToRight" SelectionChanged="comboBoxEventDevice_SelectionChanged">
Also callback for SelectionChanged="comboBoxEventDevice_SelectionChanged" should be changed or removed depending do you need event handling or not.
Margins of CheckBox need to be changed to your margins (and maybe you will need to add Width property but that depends on your project) in line:
<CheckBox HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center" Margin="90,519,13,0" VerticalAlignment="Top" Height="21">

How to bind a DataTrigger to a non-bound attribute?

This is not in a template, it is a <Label> in the body of my XAML document. Notice that the Content of the label is set to 'PENDING'. This is monitoring a server connection and at various times the code-behind might change the value of the content to CONNECTED or ERROR. When that happens, I would like for the color of the text to change. I thought this would do it, but it doesn't... all I get is black text.
<Label x:Name="lbl_Connected" Content="PENDING" FontWeight="Bold" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="CONNECTED">
<Setter Property="Label.Foreground" Value="Green"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="PENDING">
<Setter Property="Label.Foreground" Value="Yellow"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content.Value}" Value="ERROR">
<Setter Property="Label.Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
Can someone tell me how I should be doing this?
Just remove the ".Value" part from the Binding's Path, i.e.:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=Content}" Value="CONNECTED">
<Setter Property="Label.Foreground" Value="Green"></Setter>
</DataTrigger>
Anyway if I were you, I would use a Binding in order to set the Label content and a converter to handle the Foreground color.

WPF Visibility of a UI element based on combo selection

Trying to show a label only when a certain item in a combo is selected. Code should pretty much explain it.
<ComboBox Name="comboMyCombo">
<ComboBoxItem>Don't show the label</ComboBoxItem>
<ComboBoxItem>Show the label</ComboBoxItem>
</ComboBox>
<Label Visibility="Collapsed">This is my label
<Label.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=comboMyCombo, Path=SelectedValue}" Value="Show the label">
<Setter Property="Label.Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
There are two issues here. First the default visibility should be specified in the style. But even with that it won't work because the binding on the trigger is comparing a SelectedValue, a ComboBoxItem object with a string object and that will never be equivalent. To keep the example simple, I've placed appropriate values in the ComboBoxItem's Tag properties. Although the actual implementation of the comparison will likely vary based on the specific needs of the app.
<ComboBox Name="comboMyCombo">
<ComboBoxItem Tag="Hide">Don't show the label</ComboBoxItem>
<ComboBoxItem Tag="Show">Show the label</ComboBoxItem>
</ComboBox>
<Label>This is my label
<Label.Style>
<Style>
<Setter Property="Label.Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=comboMyCombo, Path=SelectedItem.Tag}" Value="Show">
<Setter Property="Label.Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
A "cleaner" solution would be
<ComboBox>
<ComboBoxItem x:Name="iOne" Content="One"/>
<ComboBoxItem x:Name="iTwo" Content="Two"/>
<ComboBoxItem x:Name="iThree" Content="Three"/>
</ComboBox>
<Label Content="One is shown">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=iOne, Path=IsSelected}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>

WPF Listbox - Empty List Display Message

Can anyone suggest the best way to display a Textblock (with a text such as "List Empty") so that it's visibility is bound to the Items.Count.
I have tried the following code and can't get it to work, so think that I must be doing it wrong.
<ListBox x:Name="lstItems"
ItemsSource="{Binding ListItems}">
</ListBox>
<TextBlock Margin="4" FontStyle="Italic" FontSize="12" Text="List is empty" Visibility="Collapsed">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=lstItems, Path=Items.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
The problem in your code is that setting the value of Visibility in the text block itself has higher priority than setting it in the style. So, even when the trigger occurs, the setting inside the trigger has no effect. Change the XAML to:
<TextBlock Margin="4" FontStyle="Italic" FontSize="12" Text="List is empty" >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=lstItems, Path=Items.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Where the setting of Visibility is all in the style and it works (at least in my demo project).

How can I "Click Through" a control in WPF?

I have an order entry form that has a ListBox with a list of line items. I have my items template, and one of the values is a ComboBox in each of my Items.
Now, my form can also create Credit memo's in addition to purchase orders, but when I am creating a credit memo, I want to put the words "Credit Memo" over the list box, however, the TextBlock covers the ComboBox in two of my line items. I would like to pass my click event through the TextBlock to the ComboBoxes but I'm not sure how to do it.
This is what I have, ( Maybe I am coming at this totally wrong, I am kinda a noob with WPF )
<ListBox SelectionMode="Single" Grid.Row="2"
ItemsSource="{Binding Path=LineItems}" HorizontalContentAlignment="Stretch"
IsSynchronizedWithCurrentItem="True" Background="#66FFFFFF">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="WhiteSmoke"/>
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsPartBackOrder}" Value="True">
<Setter Property="Background" Value="Orange" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type Entities:SalesOrderLineItem}" >
<OrderEntry:SalesOrderLineItemCreate DataContext="{Binding}" DeleteSalesOrderLineItem="DeleteSalesOrderLineItem" Margin="0,3,3,0" >
<OrderEntry:SalesOrderLineItemCreate.Resources>
<Style TargetType="{x:Type OrderEntry:SalesOrderLineItemCreate}">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource=
{
RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}
},
Path=IsSelected
}" Value="True">
<Setter Property="Background" Value="LightBlue" />
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</OrderEntry:SalesOrderLineItemCreate.Resources>
</OrderEntry:SalesOrderLineItemCreate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="2"
Text="Credit Memo"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="48" Height="Auto"
FontStyle="Italic"
Foreground="Red"
Opacity=".25">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=OrderType}" Value="CR">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=OrderType}" Value="CU">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock IsHitTestVisible="False" .../>

Resources