hide Canvas depending on child's content - wpf

this is my latest try to make the canvas Invisible whenever the label.Content is an empty String. Any help/advice appreciated, thanks.
<Canvas Visibility="Visible">
<Label Content="" Name="holamouse" />
<Canvas.Resources>
<Style TargetType="{x:Type Canvas}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, ElementName=holamouse, UpdateSourceTrigger=PropertyChanged}" Value="{x:Static sys:String.Empty}">
<Setter Property="Canvas.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Resources>
</Canvas>

The problem here is that a local property value always has higher precedence than a value set by a Style Setter. See Dependency Property Value Precedence.
When you set Visibility="Visible" on the Canvas, any Style Setter for that property is silently ignored. You could move the property assignment to the Style, although Visible is the default value anyway:
<Canvas>
<Label Content="" Name="holamouse" />
<Canvas.Resources>
<Style TargetType="{x:Type Canvas}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Content, ElementName=holamouse}"
Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Content, ElementName=holamouse}"
Value="{x:Null}">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Resources>
</Canvas>
Please note also that there is a second trigger for Value="{x:Null}" now.

You need to move the default Visibility property out of the <Canvas> tag and into the <Style>
This is because properties defined in the <Tag> take precedence over any property setters, including triggered property setters. See MSDN's Dependency Property Precedence List if you want more details.
<Canvas>
<Label Content="" Name="holamouse" />
<Canvas.Resources>
<Style TargetType="{x:Type Canvas}">
<Setter Property="Canvas.Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, ElementName=holamouse, UpdateSourceTrigger=PropertyChanged}" Value="{x:Static sys:String.Empty}">
<Setter Property="Canvas.Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Resources>
</Canvas>

Related

Default Content for Empty Datagrid or Listbox in WPF

Does anyone know how to add default content to an empty datagrid or listbox? Such as "No Results Returned" or something along those lines.
You can do something like this, where the ListBox is Hidden and an associated error Grid is displayed instead. The benefit of this approach is that it is a bit more flexible, as you have an entire Grid to use instead of a VisualBrush.
<Grid>
<ListBox x:Name="MyListBox">
<ListBox.Style>
<Setter Property="Visibility" Value="Visible" />
<Style TargetType="ListBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyListBox, Path=Items.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Grid.Style>
<TextBlock Text="No Results Returned" />
</Grid>
<Grid>
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="CanUserSortColumns" Value="False" />
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="We did't find any matching records for your group..." FontSize="14" FontWeight="SemiBold" Foreground="LightCoral"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
This is what I have found and was able to to test it. Thanks to anyone who tried to help.

Set to visible when item in a combobox is selected

As the title says, I have a hidden border with some controls inside, and I would like to show it when a particular item in a combobox is selected.
I tried the following
<ComboBox Name="cmbRequiredRule" SelectedValuePath="Content"
SelectedValue="{Binding Path=ClientValidation.NarrativeRequiredRule}">
<ComboBoxItem>All</ComboBoxItem>
<ComboBoxItem>Matching</ComboBoxItem>
</ComboBox>
<Border Visibility="Collapsed">
<Border.Resources>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ClientValidation.NarrativeRequiredRule}" Value="Matching">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Resources>
....
</Border>
and this property in the view model:
public string NarrativeRequiredRule
{
get...
set...
}
but the trigger doesn't seem to be working
Try setting Visibility=Collapsed in your Style Setters, not as part of the Border Tag. I've had issues in the past where a DataTrigger would not apply when the value was specified as part of the Tag.
<Border>
<Border.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Test}" Value="Matching">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Resources>
...
</Border>
Locally assigned value takes precedence over styles. Hence you need to have
<Setter Property="Visibility" Value="Collapsed" />
in Style as #Rachel has pointed out.
Also I tried debugging the binding using a dummy converter and found that the value turned out to be System.Windows.Controls.ComboBoxItem: Matching instead of Matching.
Hence your final style is:
<Style TargetType="{x:Type Border}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ClientValidation.NarrativeRequiredRule}" Value="System.Windows.Controls.ComboBoxItem: Matching">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
could be binding issue. In your example below:
<DataTrigger Binding="{Binding Path=ClientValidation.NarrativeRequiredRule}" Value="Matching">
where is the ClientValidation located ? because if the whole View's DataContext is bound to VM, you will need to include these hierarchies. Check your Output log, it should throw some errors if binding failes

Why can't I use a DataTrigger to set TextBox.IsEnabled = True?

In my application, I have a TextBox that I want to enable/disable based on an enum in my datacontext. The enum has three values (Anyone, Me, Someone) and I want to enable the Textbox when the value "Someone" is set. I am able to hack a solution by setting the value in reverse (see below). However, can someone please explain why the first solution didn't work?
This does not work...
<TextBox Text="{Binding ModifiedUser, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="False">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ModifiedBy}"
Value="Someone">
<Setter Property="IsEnabled"
Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Strangely, this code does work.
<TextBox Text="{Binding ModifiedUser, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ModifiedBy}"
Value="Anyone">
<Setter Property="IsEnabled"
Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding ModifiedBy}"
Value="Me">
<Setter Property="IsEnabled"
Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
you have to set the initial isEnabled in your style too. otherwise your "local" IsEnabled=false will always win!
change your style and it will work.
<TextBox Text="{Binding ModifiedUser, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding ModifiedBy}"
Value="Someone">
<Setter Property="IsEnabled"
Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>

Xamly determine if a ListBox.Items.Count > 0

Is there a way in XAML to determine if the ListBox has data?
I wanna set its IsVisibile property to false if no data.
The ListBox contains a HasItems property you can bind to. So you can just do this:
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
...
<ListBox
Visibility="{Binding HasItems,
RelativeSource={RelativeSource Self},
Converter=BooleanToVisibility}" />
Or as a Trigger so you don't need the converter:
<ListBox>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger
Binding="{Binding HasItems, RelativeSource={RelativeSource Self}}"
Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
I haven't tested the bindings so there might be some typos but you should get the idea.
Do it in a trigger and you won't need a ValueConverter:
<ListBox>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<DataTrigger
Binding="Items.Count, {Binding RelativeSource={RelativeSource Self}}"
Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
So that shows the ListBox by default, but if Items.Count is ever 0, the ListBox is hidden.
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
You can probably make this work using a ValueConverter and normal binding.
Set Visibility to be:
Visibility = "{Binding myListbox.Items.Count, Converter={StaticResource VisibilityConverter}}"
Then set up your converter to return Visibility.Collapsed etc based on the value of the count.

WPF: How do I set the Foreground property of a TextBlock using DataTrigger

This is my XAML:
<TextBlock Name="SeverityText"
Grid.Column="1"
Grid.Row="0"
Foreground="Red">
<TextBlock.Triggers>
<DataTrigger Binding="{Binding Path=Severity}">
<DataTrigger.Value>
<sm:Severity>Warning</sm:Severity>
</DataTrigger.Value>
<Setter TargetName="SeverityText"
Property="Foreground"
Value="Yellow" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Severity}">
<DataTrigger.Value>
<sm:Severity>Information</sm:Severity>
</DataTrigger.Value>
<Setter TargetName="SeverityText"
Property="Foreground"
Value="White" />
</DataTrigger>
</TextBlock.Triggers>
<TextBlock>Severity:</TextBlock>
<TextBlock Text="{Binding Path=Severity}" />
</TextBlock>
This is my error message:
Cannot find the static member 'ForegroundProperty' on the type 'ContentPresenter'.
sm:Severity is an enumeration I imported.
Your triggers and setters need to be defined in a style, rather than on the TextBlock directly:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Severity}">
<DataTrigger.Value>
<sm:Severity>Warning</sm:Severity>
</DataTrigger.Value>
<Setter TargetName="SeverityText"
Property="Foreground"
Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Writing the full path of the property also works:
So
Property="Foreground" -> Property="TextBlock.Foreground"
However as suggested in the previous answer, you get:
System.InvalidOperationException: Triggers collection members must be of type EventTrigger.
...if you don't put it in a style.

Resources