I'm struggeling with binding a datatrigger to a textblock property. I have added a working Datatrigger example in the TextBox.
The issue is the TextBlock DataTrigger I can not get working. At startup IsNameActive has value 'false' and BackGround is PaleVioletRed, but it does not change when IsNameActive changes.
public bool IsNameActive
{
get => !string.IsNullOrEmpty(FirstName);
}
public string FirstName
{
get => _firstName;
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged(FirstName);
OnPropertyChanged(IsNameActive);
}
}
}
<TextBox
Grid.Row="0"
Grid.Column="1"
Margin="15"
Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=FirstName, ValidatesOnDataErrors=true, NotifyOnValidationError=true}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="RosyBrown" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="aa">
<Setter Property="Background" Value="DarkOliveGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="">
<Setter Property="Background" Value="CornflowerBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock
Grid.Row="2"
Grid.Column="1"
Margin="15"
Text="TextBlock with binding">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.IsNameActive, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="Background" Value="CornflowerBlue" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=DataContext.IsNameActive, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}" Value="false">
<Setter Property="Background" Value="PaleVioletRed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
As #Ash pointed out in the comment section, you need to wrap nameof around IsNameActive when you call OnPropertyChanged:
OnPropertyChanged(nameof(IsNameActive));
Related
I have a DataGrid which itemsSource is an ObservableCollection<MyType>. This type has this properties:
long ID;
long IDCategory;
long? IDState01;
long? IDEstate02;
long? IDEste03;
I have 3 categories, if it is category 1, IDState01 is not null and the others states are null. If category is 2, the IDState02 is not null and the others are null and son on.
I have a state column in my DataGrid, which value depends on the category. So I would like to binding to the correct property depending on the category, so if the category is 1, it would bind property state01, if the category is 3 it would bind property state02 and so on.
I think that my DataGrid would be something like that:
<DataGrid HorizontalAlignment="Stretch" Margin="5,5,5,5" VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Header="State">
<!--Something here, perhaps a datatrigger.-->
</DateGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Thanks.
EDIT: I would like to do it in XAML if it is possible, instead of using a converter.
introduce a property which will get/set the correct state based on category:
public long? State
{
get
{
if (IDCategory == 1) return IDState01;
if (IDCategory == 2) return IDState02;
return null;
}
set
{
if (IDCategory == 1) IDState01 = value;
else if (IDCategory == 2) IDState02 = value;
}
}
in DataGird bind column to a new property:
<DataGridTextColumn Header="State" Binding="{Binding State}"/>
I would like to do it in XAML if it is possible ...
You could use a DataGridTemplateColumn:
<DataGridTemplateColumn Header="State">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IDCategory}" Value="1">
<Setter Property="Text" Value="{Binding IDState01}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="2">
<Setter Property="Text" Value="{Binding IDEstate02}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="3">
<Setter Property="Text" Value="{Binding IDEste03}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IDCategory}" Value="1">
<Setter Property="Text" Value="{Binding IDState01}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="2">
<Setter Property="Text" Value="{Binding IDEstate02}" />
</DataTrigger>
<DataTrigger Binding="{Binding IDCategory}" Value="3">
<Setter Property="Text" Value="{Binding IDEste03}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
I think you will need own DataTemplate for each category and DataTemplateSelector to assign DataTemplates based on category type.
This is my binding data:
List<MyData> files;
public clas MyData
{
public string Name { get; set; } // Column
public bool IsOK { get; set; } // Not a Column
public string Format { get; set; } // Column
}
My ListView:
<ListView
Grid.Row="0"
Name="lvFiles"
ItemsSource="{Binding wiresharkFiles}">
<ListView.ItemContainerStyle>
<DataTrigger Binding="{Binding IsOK}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="Background" Value="Green"/>
</MultiTrigger>
</ListView.ItemContainerStyle>
And i have several ListViewColumns because i want to change the color of only the Name column i added this to my other column (Format column) GridViewColumn:
<GridViewColumn Width="115" Header="Format">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock
x:Name="textBlock"
Text="{Binding FileFormat}"
Margin="0,0,0,0"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsOK}" Value="True">
<Setter TargetName="textBlock" Property="Foreground" Value="Silver"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
This not mark this Column (format column) in red but in case IsMouseIsOver is true this column is still in Silver and not become White like define in my trigger.
So i changed my GridViewColumn to this:
<GridViewColumn Width="115" Header="Format">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock
x:Name="textBlock"
Text="{Binding FileFormat}"
Margin="0,0,0,0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Gray"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding BadCheckSumExist}" Value="True">
<Setter TargetName="textBlock" Property="Foreground" Value="Silver"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
But this still not works as expected.
Keep your triggers in ItemContainerStyle. Just add Foreground binding to TextBlock that will take foreground brush from ListViewItem's Foreground.
<TextBlock x:Name="textBlock"
Text="{Binding FileFormat}"
Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}}"
Margin="0,0,0,0"/>
I want to use a NumercUpDown value designed inside a ControlTemplate in another NumericUpDown DatatTrigger, to set up its maximum value based on a condition.
Code -1
<ControlTemplate x:Key="OrderInfo" TargetType="ContentControl">
<TextBlock Grid.Row="0" Grid.Column="0" Style="{StaticResource TextBlockStyle}">Limit Price:</TextBlock>
<i:NumericUpDown Grid.Row="0" Grid.Column="1" x:Name="Price" i:Skin.IsPrice="True" RoundingDecimalPlaces="{Binding Source={StaticResource PriceFormat}, Path=MaxDecimalPlaces}" DisplayFormat="{Binding Source={StaticResource PriceFormat}, Path=StringFormat}" Minimum="0" Increment="{Binding Path=PriceIncrement.Value, TargetNullValue=1}" IncrementCount="{Binding Path=PriceIncrementCount.Value}">
<i:NumericUpDown.Style>
<Style TargetType="i:NumericUpDown" BasedOn="{StaticResource BasicStyle}">
<Setter Property="Value" Value="{Binding Path=Price.Value, ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Price.IsAvailable}" Value="False">
<Setter Property="Value" Value="{Binding Path=Price.EstimatedPrice, Mode=OneWay}" />
</DataTrigger>
</Style.Triggers>
</Style>
</i:NumericUpDown.Style>
</i:NumericUpDown>
</Grid>
</ControlTemplate>
Want to use the above Price element in the below Data Trigger
<i:NumericUpDown Grid.Row="0" Grid.Column="3" x:Name="CompletionPrice"
Value="{Binding Path=ExternalAlgoProperties[(i:Description)CompletionPrice].Value,
ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" IsEnabled="True"
RoundingDecimalPlaces="0" Increment="1" Minimum="0">
<i:NumericUpDown.Style>
<Style TargetType="i:NumericUpDown" BasedOn="{StaticResource BasicStyle}">
<Setter Property="Maximum" Value="0"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Side.Code,ConverterParameter={x:Static i:SideCodes.Sell}, Converter={StaticResource EqualsConverter},UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="Maximum" Value="{Binding ElementName=Price,Path=Text,UpdateSourceTrigger=PropertyChanged}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</i:NumericUpDown.Style>
</i:NumericUpDown>
USe Ancestor Binding to access
<DataTrigger
Binding="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type i:NumericUpDown }},
Path=value}"
Value="True">
//Set property
</DataTrigger>
I'm new to WPF and I trying to create xaml logic to show / hide a control based on the value of the AllowMiscTitle on the ViewModel. The xaml consist of two fields a combobox of the standard tiles ("Mr", "Mrs", ..., "Other") when "Other" is selected I want the textbox to display.
I've created the follow xaml:
<DockPanel Validation.Error="Validation_Error" HorizontalAlignment="Stretch">
<ComboBox ItemsSource="{Binding Path=Titles, Mode=OneTime}"
Text="{Binding Path=Title}"/>
<TextBox x:Name="TxtBxTitle" Margin="5,5" Visibility="Visible">
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AllowMiscTitle}" Value="false">
<Setter Property="TextBox.Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DockPanel>
That Trigger won't work because you have set Visibility property explicitly in TextBox
Do it like this:
<TextBox x:Name="TxtBxTitle" Margin="5,5">
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AllowMiscTitle}" Value="false">
<Setter Property="TextBox.Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
The reason for this is Dependency property value precedence.
There is a
<BooleanToVisibilityConverter x:Key="BoolToVis"></BooleanToVisibilityConverter>
You can use it as following
<TextBox Visibility="{Binding YourPropertyName, Converter={StaticResource BoolToVis}}"></TextBox>
If I got your question right:-
If your selected value is binded to some property in the ViewModel like:-
private string _GenderType;
public string GenderType
{
get
{
return _GenderType;
}
set
{
_GenderType= value;
RaisePropertyChanged("GenderType");
In xaml:-
<TextBox.Style>
<Style>
<Setter Property="TextBox.Visibility" value="Hidden"/>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=GenderType,ElementName=Combo1}" Value="Other">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
I have a datatemplate containing an image that I want to be hidden if the the value of a property in a ViewModel is true. Can anyone tell me why the the xaml below does not work?
<Image x:Name="img" Source="..\Images\List_16.png" Margin="0,0,5,0">
<Image.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentListHasPendingChanges}" Value="True">
<Setter Property="Image.Visibility" Value="Hidden" />
</DataTrigger>
<DataTrigger Binding="{Binding CurrentListHasPendingChanges}" Value="False">
<Setter Property="Image.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Try removing "Image" part from Property="Image.Visibility" so you'll have:
<Setter Property="Visibility" Value="Hidden"/>
and add TargetType to your Style:
<Style TargetType="{x:Type Image}">
I just did something similar using a ContentControl.
<ContentControl Content="{Binding CurrentListHasPendingChanges}">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image x:Name="img" Source="..\Images\List_16.png" Margin="0,0,5,0" Visibility="Hidden" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="False">
<Setter Property="Image.Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
From http://karlhulme.wordpress.com/2007/03/06/using-a-contentcontrol-and-datatemplate-to-indicate-new-andor-modified-data/
isn't that
<Setter Property="Visibility" Value="Hidden" />
?
I assume you use INotifyProptyChanged.
EDIT I did some Googling and I think you need to use some sort of template in order to make the trigger work.
eg.: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/ae2dbfb7-5dd6-4352-bfa1-53634289329d
http://www.thejoyofcode.com/Help_Why_cant_I_use_DataTriggers_with_controls_in_WPF.aspx
In my opinion we not need to use Triggers, with only the Binding it works well.
To make binding to a property model, you can use BooleanToVisibilityConverter
Is declared as follows:
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</UserControl.Resources>
And how to use it is simple, just point to the key stated above:
<Image HorizontalAlignment="Left" Height="16" VerticalAlignment="Center" Width="16"
Visibility="{Binding HasError, Converter={StaticResource BooleanToVisibilityConverter}}"
Source="/myPath;component/Resources/Images/image1.png"/>
The property in the ViewModel:
private bool _hasError = false;
public bool HasError
{
get { return !string.IsNullOrEmpty(_messageError); }
set
{
_hasError = value;
this.NotifyOfPropertyChange(() => this.HasError);
}
}