Wpf DataTrigger not working - c#

I have a button where is enabled property should happen twice. In the viewmodel if the IsEnabled property is set to false the button should be disabled which works fine. On the other hand when the Validate button in the UI is disabled, this button should also be disabled that is not working which indicates that the Data Trigger is not working. Please help.
<Button x:Name="BtnValidate" Content="Validate" Height="24" VerticalAlignment="Top" Grid.Column="2" Width="83" Command="{Binding ValidateCommand}" IsEnabled="{Binding IsValidateEnabled}" HorizontalAlignment="Left" Margin="8,28,0,0" />
<Button Name="BtnReload" IsEnabled="{Binding IsEnabled}" HorizontalAlignment="Left" Width="123" Grid.Row="0" Grid.Column="5" Content="Reload" Command="{Binding DateCommand}" Margin="8,24,0,32">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=BtnValidate,Path=IsEnabled}" Value="False">
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Properites set in the <Tag> of the object take precendence over any property set in a <Style>, so in your case the IsEnabled="{Binding IsEnabled}" is always going to be used. See MSDN's Dependency Property Precedence List for more information.
The solution would be to move the IsEnabled property out of the tag definition and into the style, since properties set in a Trigger take precedence over properties set in the <Style>
<Button Name="BtnReload" Content="Reload" Command="{Binding DateCommand}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=BtnValidate,Path=IsEnabled}" Value="False">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Related

WPF Radio Button Group How to enable a TextBox when any item gets selected?

I have no issues getting this functionality to work with textboxes and comboboxes by simply binding the IsEnabled Property of the control I want to stay hidden until something is selected to the .Text Property, like so:
<Label Content="Please Select the status for this client:" Margin="0,0,65,0" IsEnabled="{Binding CBCustomer.Text}" />
So basically this item will not show until someone selects something in the ComboBox.
How do I get the same functionality to work with a group of radio buttons? Basically I have a stackpanel that I want the IsEnabled property(shown as XXXXXX) to be bound to a Boolean value of whether any button was selected.
<StackPanel Orientation="Horizontal" Canvas.Left="54" Canvas.Top="40" Margin="0,0,0,50">
<Label Content="Please Select the RAG Status for this client:" Margin="0,0,65,0" IsEnabled="{Binding CBCustomer.Text}" />
<RadioButton GroupName="RAGGroup" Margin="0,8,0,0" Background="Red" Foreground="Red" IsEnabled="{Binding CBCustomer.Text}">Red</RadioButton>
<RadioButton GroupName="RAGGroup" Margin="10,8,0,0" Background="DarkGoldenrod" Foreground="DarkGoldenrod" IsEnabled="{Binding CBCustomer.Text}">Amber</RadioButton>
<RadioButton GroupName="RAGGroup" Margin="10,8,0,0" Background="Green" Foreground="Green" IsEnabled="{Binding CBCustomer.Text}">Green</RadioButton>
</StackPanel>
<StackPanel Margin="55,80,0,0" IsEnabled="{Binding XXXXXXX}">
Use a Multi-Binding? I see examples that show how to get the value back from the button that was selected but I don't care about that, I simply need to see whether the value was selected or not.
If you want to bind to other elements, you can use DataTriggers:
<RadioButton x:Name="RB1"/>
<RadioButton x:Name="RB2"/>
<RadioButton x:Name="RB3"/>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=RB1, Path=IsChecked}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=RB2, Path=IsChecked}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=RB3, Path=IsChecked}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
But I almost always bind to the data layer, like:
<RadioButton IsChecked="{Binding RAG, Converter=...}"/>
<RadioButton IsChecked="{Binding RAG, Converter=...}"/>
<RadioButton IsChecked="{Binding RAG, Converter=...}"/>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RAG}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>

How to use WPF Validation rules on a Listbox to enable a 'OK' button

I'm new with WPF and I have a problem. I want to enable a button only if en element of a Listbox is selected, otherwise it has to be disabled. I've tried with simple Validation Rule but it doesn't worked. Can anyone give me a hint? Ty
You don't use a ValidationRule to enable a Button but you could use a Button style with a trigger that binds to the SelectedItem property of the ListBox and sets the IsEnabled property of the Button to false if the SelectedItem property of the ListBox returns a null reference, e.g.:
<ListBox x:Name="lb">
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
<ListBoxItem>3</ListBoxItem>
</ListBox>
<Button Content="Button">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=lb}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Ok, this works well ty. But I've to enable the button when 2 conditions are satisfied (a textbox not empty and an item of the listbox selected). How can i do this?
You could add another trigger:
<ListBox x:Name="lb">
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
<ListBoxItem>3</ListBoxItem>
</ListBox>
<TextBox x:Name="txt" />
<Button Content="Button">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=lb}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding Text.Length, ElementName=txt}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Hover over a button and highlight multiple controls when condition is met (MVVM)

I have two buttons with commands bound to them.
If the user hovers over a button1 it should highlight (border color changes)
textbox1 and combobox1 only when the button1 is in disabled state.
If the user hovers over a button2 it should highlight (border color changes)
textbox2, textbo3 and combobox1 only when the button2 is in disabled state.
And finally unhighlight the controls on mouseleave.
Is this possible with pure XAML, because the style should be applied to other controls, not to the button itself who triggers the event and only when conditions are met?
And how could this be done?
I found many examples on SO, but they do not apply to this specific case.
I started to do this programmatically:
<Button Name="btnGenerateHash"
IsEnabled="{Binding VM.IsGenerateButtonEnabled}"
Command="{Binding GenerateCommand}"
Content="{Binding VM.GenerateHashButtonLabel}" Width="160"
Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Margin="10" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding HighlightFieldsCommand}" CommandParameter="{Binding Source='generate,enter'}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding HighlightFieldsCommand}" CommandParameter="{Binding Source='generate,leave'}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Modified version:
<StackPanel Orientation="Vertical">
<CheckBox
x:Name="EnableButton1CheckBox"
Content="Enable Button1"
Margin="4"
/>
<Grid Margin="4">
<Button Click="button1_Click"
Content="Button1"
x:Name="button1"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}">
</Button>
<Border
x:Name="Button1MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->
</Grid>
<Grid Margin="4">
<Button
Content="Button2"
x:Name="button2"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}">
</Button>
<Border
x:Name="Button2MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->
</Grid>
<TextBox
Text="tb1"
x:Name="tb1"
Margin="4"
>
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button1MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button1}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button2MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button2}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Green" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
This is partial, but illustrates how to
Expose mouseover state of a possibly disabled control to XAML style triggers on other controls, and
How those other XAML triggers can do stuff only when two different conditions are both true.
XAML:
<StackPanel Orientation="Vertical">
<CheckBox
x:Name="EnableButton1CheckBox"
Content="Enable Button1"
Margin="4"
/>
<Grid
Margin="4"
>
<Button
Content="Button1"
x:Name="button1"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}"
>
</Button>
<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->
<Border
x:Name="Button1MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
<Button
Content="Button2"
x:Name="button2"
Margin="4"
>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button1MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button1}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>

WPF Styling Not Working as Expected

I have the following style applied to a Button (enabled by default):
<Style x:Key="DisableFoxProCancelButton"
TargetType="{x:Type Button}"
BasedOn="{StaticResource ButtonStyle}">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=FoxProTableIsDirty}" Value="false">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
This works to disable the button when FoxProTableIsDirty is False.
I originally had this wired up as follows (disabled by default):
<Style x:Key="DisableFoxProCancelButton"
TargetType="{x:Type Button}"
BasedOn="{StaticResource ButtonStyle}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=FoxProTableIsDirty}" Value="true">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
But this second way did not work to disable the button when FoxProTableIsDirty is False.
The second style seemed to make more sense when I was writing the style as by default the button should be disabled until a change is made to the associated table and the changes need to be saved.
Can someone explain why the second one would not work as expected?
EDIT
Here is the code for the button using this style:
<Button Name="CancelFoxproChanges"
Content="Cancel"
Style="{StaticResource DisableFoxProCancelButton}"
DockPanel.Dock="Right"
HorizontalAlignment="Right">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction TargetObject="{Binding}"
MethodName="FoxProCancelButtonClicked" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
check if there is IsEnabled set in the Button element.
remove if it is set as triggers can not override local or inline values.
<Button Style="{StaticResource DisableFoxProCancelButton}" IsEnabled="False" />
in other way you can directly bind FoxProTableIsDirty to Button's IsEnabled, eg
<Button IsEnabled="{Binding FoxProTableIsDirty}" />
or via style setters
<Setter Property="IsEnabled" Value="{Binding FoxProTableIsDirty}" />
so as long as FoxProTableIsDirty is being resolved and notifying changes, this will enable/disable the button as desired

WPF Styling child control based on parent command enabled

I just want to make the Label inside my Button template look disabled. Scoured the Internet and this seems like a simple task, what I'm doing wrong?
<Button Command="{Binding CommandProcess}">
<StackPanel Orientation="Horizontal">
<Image Source="Images\Cloud-Upload.png"/>
<Label Content="Upload and Process" Foreground="White" VerticalAlignment="Center" FontWeight="Bold" FontSize="18.667" Margin="5,0,0,0">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="False">
<Setter Property="Foreground" Value="Gray"></Setter>
<Setter Property="ToolTip" Value="Please select a record type for each file selected for processing"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackPanel>
</Button>
EDIT: (full code after using the answer by RV1987)
<Button Command="{Binding CommandProcess}" x:Name="ProcessButton">
<StackPanel Orientation="Horizontal">
<Image Source="Images\Cloud-Upload.png"/>
<Label Content="Upload and Process" VerticalAlignment="Center" FontWeight="Bold" FontSize="18.667" Margin="5,0,0,0">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, ElementName=ProcessButton}" Value="True">
<Setter Property="Foreground" Value="White"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsEnabled, ElementName=ProcessButton}" Value="False">
<Setter Property="Foreground" Value="Gray"></Setter>
<Setter Property="ToolTip" Value="Please select a record type for each file selected for processing"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackPanel>
</Button>
Use ElementName instead. RelativeSource = FindAncestor won't work here since button does not lies in Visual Tree but instead its the sibling of StackPanel. Give name to button and use it in your binding using ElementName -
<Button Command="{Binding CommandProcess}" x:Name="MyButton">
and in DataTrigger -
<DataTrigger Binding="{Binding Path=IsEnabled, ElementName=MyButton}"
Value="False">
.....
</DataTrigger>
Visual Tree structure for Label and Button is like this -
Label <--- StackPanel <--- StackPanel's Parent
Button <--- StackPanel's Parent
As it can be seen Button is the sibling of StackPanel and it lies nowehere in the Visual tree of Label that's why FindAncestor won't get you to Button.

Categories

Resources