I want to set the Source of an Image depending of the value of a Boolean.
Here is the code I have :
<Image DockPanel.Dock="Left">
<Image.Source>
[...]
</Image.Source>
</Image>
And in [...] I can access to a Boolean (Path="Item2" - I got a Tuple) and I want to set the value of my Source depending of the value of the Boolean.
I Absolutely got no idea how to do it...
I googled it and found some tips about Setters but I didnt managed to get it work
Any help will be very appreciated !
You could use a Style and DataTriggers:
<Image>
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding ThatBool}" Value="true">
<Setter Property="Source" Value="Path to image"/>
</DataTrigger>
<DataTrigger Binding="{Binding ThatBool}" Value="false">
<Setter Property="Source" Value="Path to another image"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
(You should be familiar with data binding)
You need a DataTrigger... which by the way requires a Style. Check this link.
Related
I have to accomplish the following task and don't know how to do it at all.
I am using a WizardControl (XCeed Wpf Toolkit) and want to skip one page, depending on a certain state in the ViewModel (let's say a bool-variable).
To achieve this I have to bind a different Control (a WizardPage) to the previous WizardPage's NextPage DependencyProperty according to this bool-variable. I think this can be done somehow with DataTrigger, but I am not so experienced in this topic. Can someone please help me?
The minimal code sample ist:
<xctk:Wizard>
<xctkWizardPage x:Name="Page1" NextPage="Page2"/>
<xctkWizardPage x:Name="Page2"/>
<xctkWizardPage x:Name="Page3"/>
</xctkWizardPage>
</xctk:Wizard>
public bool Property { get; set; }
What I want to do is make the "NextPage" of "Page1" dependant on the "Property", e.g. have "Page2" as NextPage if Property == true, otherwise go to "Page3".
Thank you very much for your help!
Jan
Maybe something like:
<Style TargetType="xctWizardPage">
<Style.Triggers>
<DataTrigger Property="YourProperty" Value="True">
<Setter Property="NextPage" Value="Page2" />
</DataTrigger>
<DataTrigger Property="YourProperty" Value="False">
<Setter Property="NextPage" Value="Page3" />
</DataTrigger>
</Style.Triggers>
</Style>
Try something like this.
This solution worked for me:
<xctk:Wizard>
<xctk:WizardPage x:Name="Page1">
<Style TargetType="xctk:WizardPage">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Property}" Value="True">
<Setter Property="NextPage" Value="{Binding ElementName=Page2}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Property}" Value="False">
<Setter Property="NextPage" Value="{Binding ElementName=Page3}" />
</DataTrigger>
</Style.Triggers>
</Style>
</xctk:WizardPage>
<xctk:WizardPage x:Name="Page2"/>
<xctk:WizardPage x:Name="Page3"/>
</xctk:Wizard>
Thanks for all replies.
I am a newbie in WPF, I have always done validation for various UI controls using custom ValidationRule classes, however, when using DataGrid for the first time and binding it with a simple DataTable, I found that the DataGrid has a pretty good default validation that detects the type of DataTable columns and gives a visual error if a cell value is not of the same expected type. This is pretty enough for me that I thought no need to create custom validation rules as the default one is fitting my purpose. However, I have a Submit button that I need to disable if this DataGrid has any errors, so I thought that this would be easy utilizing the Validation.HasError property using the following code:
<Button x:Name="btnSubmit" Content="Submit">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError),ElementName=dataGrid}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
But unfortunately, it seems that Validation.HasError is always False whatever the value I enter in the datagrid cells in Runtime. The default visual validation is working properly, the cell gets a red border when an incorrect value is entered, however, no notification is sent that there is an error coming from the dataGrid.
Is there any way to detect within XAML that the default visual validation of the dataGrid is producing an error? or do I have to use a custom validation rule for this purpose?
You can create a global validation on your Ap.xaml file. So Your control will have a red asterisk and the error message as Tooltip . Any control of your project can use the same validation.
In App.xaml file:
<Style x:Key="AsteriskTemplate" TargetType="Control">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right"
Foreground="Red"
FontSize="14pt"
Margin="-15,0,0,0" FontWeight="Bold">*
</TextBlock>
<Border>
<AdornedElementPlaceholder Name="myControl"/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGrid" BasedOn="{StaticResource AsteriskTemplate}" />
I've got a button with a fixed background image and would like to show a small overlay image on top of it. Which overlay image to chose depends on a dependency property (LapCounterPingStatus) of the according viewmodel.
This is what I got so far:
<Button>
<Grid>
<Image Stretch="None"> <!-- Background Image -->
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="/Images/Pingn.png"/>
</Style>
</Image.Style>
</Image>
<Image Stretch="None" Panel.ZIndex="1"> <!-- Small Overlay Image -->
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="PingStatus.PING_UNKNOWN">
<Setter Property="Source" Value="/Images/RefreshOverlayn.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="PingStatus.PING_FAILURE">
<Setter Property="Source" Value="/Images/ErrorOverlayn.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="PingStatus.PING_SUCCESS">
<Setter Property="Source" Value="/Images/CheckmarkOverlayn.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Grid>
</Button>
Relevant parts of my viewmodel
public class ConfigurationViewModel
{
public enum PingStatus { PING_UNKNOWN, PING_SUCCESS, PING_FAILURE };
public PingStatus LapCounterPingStatus
{
get { return _lapCounterPingStatus; }
set
{
_lapCounterPingStatus = value;
RaisePropertyChanged(LapCounterPingStatusPropertyName);
}
}
}
Right now, no overlay image at all is displayed. What could be wrong?
UPDATE
Trace window of my IDE is showing System.ArgumentException and System.FormatException.
Could the problem source be a unknown type of enumeration PingStatus im the XAML?
You need 2 things to get this working:
1 - Add an xmlns reference in the root element of your XAML file, to the namespace where your Enum is defined:
<UserControl ...
xmlns:my="clr-namespace:YourEnumNamespace;assembly=YourAssembly">
2 - in the Value property of the DataTrigger, use the {x:Static} form:
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="{x:Static my:PingStatus.PING_UNKNOWN}">
Notice that the Enum type must be prefixed with the xmlns prefix you defined above.
Edit:
If your Enum is declared inside a class you need to use the syntax:
{x:Static namespace:ClassName+EnumName.EnumValue}
for example:
{x:Static my:ConfigurationViewModel+PingStatus.PING_UNKNOWN}
Complete worked example for WPF + MVVM.
Tested on MSVC 2017.
In the view:
<TextBlock Text="Some text to be colored by an enum">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusIcon}" Value="{x:Static my:StatusIcon.Warning}">
<Setter Property="Foreground" Value="Yellow"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusIcon}" Value="{x:Static my:StatusIcon.Error}">
<Setter Property="Foreground" Value="Red}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
If using ReSharper, and if the DataContext is set up properly, there will be intellisense when you hit the . after StatusIcon, i.e. it will show the properties of the enum which are Debug, Info, Warning or Error.
If using ReSharper, it will suggest the following update to the namespace in the header for the XAML file(its
good like that):
xmlns:my="clr-namespace:Class.Path.MyViewModel;assembly=MyAssembly"
And the VieModel:
public enum StatusIcon
{
Debug,
Info,
Warning,
Error
}
public class MyViewModel
{
public StatusIcon StatusIcon { get; }
}
We also use Fody for automated binding.
You can simply set enum value as DataTrigger Value... Tested on MSVC 2017.
<TextBlock Text="Some text to be colored by an enum">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusIcon}" Value="Warning">
<Setter Property="Foreground" Value="Yellow"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusIcon}" Value="Error">
<Setter Property="Foreground" Value="Red}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I would like to implement a "NullText" behavior for a TextBlock that is bound to a property in a ViewModel. When that property in the ViewModel is null or empty, I would like to display gray italic text something like "No Data". I'd like this to follow MVVM pattern but I am lost...
Update
So after playing around with the solution James Webster suggested, I got it to work like this...
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<c:NullOrEmptyValueConverter x:Key="NullOrEmptyValueConverter" Text="(No Data)"/>
</UserControl.Resources>
...
<TextBlock Name="SerialNumberTextBlock" Text="{Binding Path=SerialNumber, Converter={StaticResource NullOrEmptyValueConverter}}">
<TextBlock.Resources>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SerialNumberTextBlock, Path=Text}" Value="(No Data)">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Resources>
</TextBlock>
I'd recommend implementing an IValueConverter; if the source value is not null or empty, then pass it through to the TextBlock. If the source value is null or empty, then render your chosen text.
public class NullValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = (string)value;
if (str.IsNullOrWhitespace())
{
return "No Data";
}
return str;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
... //An empty implementation I expect...
}
}
However I have just realised that you want to set the style as well... hmmm, probably a DataTrigger that sets the style if the value is 'No Data' required I expect;
<TextBlock Text="{Binding Path=SomeProperty, Converter={StaticResource keyToNullValueConverter}">
<TextBlock.Triggers>
<DataTrigger Binding="{Binding Path=Text}" Value="No Data">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
</TextBlock.Triggers>
</TextBlock>
Something along those lines might work.
I think you don't need to create Converter Class, you can simply write your style code like this.
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SerialNumberTextBlock, Path=Text}" Value="{x:Null}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=SerialNumberTextBlock, Path=Text}" Value="{x:Static System:String.Empty}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
</Style.Triggers>
</Style>
Note :- You need to include the system namespace as
xmlns:System="clr-namespace:System;assembly=mscorlib"
You could try to bind to a property that looks thus
private string _textBlockText;
public string TextBlockText
{
get
{
if(string.IsNullOrEmpty(_textBlockText))
{
return "No Data";
}
return _textBlockText;
}
set
{
_textBlockText = value;
}
}
and then use the XAML that James has mentioned for styling. Saves the need for a converter.
Very late to the party, but here is my answer.
<TextBox >
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Text" Value="{Binding MyText, TargetNullValue=No data}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyText}" Value="{x:Null}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
<DataTrigger Binding="{Binding MyText}" Value="{x:Static System:String.Empty}">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Text" Value="{Binding MyText}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
My answer assumes that you want the default text to disappear on focus, which is more in line how inputs with default text usually behave. Also this works only for null values. If you need a check for string empty or additional logic, you could instead use a converter like in other provided answers. The key idea is, that you remove null value text or converter in the binding when control gains focus and thus only display the default text when control has no focus. This also prevents the default value flowing back to your view model.
DataTriggers part for font style was kindly borrowed from pchajer's answer.
EDIT: So, it turns out that it was a problem with the code in the VM (embarrassingly enough checking on a property that always returned true [after a refactoring session] ) - I'd kind of assumed that I'd buggered up the databinding as that's the usual suspect (for me at least)
Thank you for all the help, and apologies for wasting your time.
Hi, I'm trying to get this to simply change text colour to either Red or Green depending on a boolean Dependency Property in the viewmodel. The triggers are where the problem is... I think?
<TextBlock>
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsNegativeChange}" Value="true">
<Setter Property="TextBlock.Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding IsNegativeChange}" Value="false">
<Setter Property="TextBlock.Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} ({1})">
<Binding Path="ReturnedData.Change" />
<Binding Path="ReturnedData.ChangePercentage" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
The IsNegativeChange is a member of the ViewModel object itself and so it doesn't need the 'ReturnedData' qualification.
As it stands, the text always appears as green. The ViewModel is correctly returning true/false depending on input.. Help! Is there something stupid I'm missing?
[edited for formatting]
Edit, in the debug window it says:
BindingExpression:Path=IsNegativeChange; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'NoTarget' (type 'Object')
Isn't the target set by the ??
The triggers look fine to me, does the output window in Visual Studio show any binding errors?
If not maybe this is a case where the value of the trigger is overwritten, see this article about dependency property value precedence for more information. If you set the value explicitly to green somewhere the trigger will not do anything.
You can probably do away with the second trigger like so:
<Style>
<Setter Property="TextBlock.Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=DataContext.IsNegativeChange}" Value="false">
<Setter Property="TextBlock.Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
That doesn't explain why one works and the other doesn't though.
I think your problem might be having the Style inline with the element. The Binding error message in your Console indicates that the binding target is being obscured inside your Style. However, you mention that adding another Label element with a binding shows the correct value.
I would also consider converting to a known default in your style, instead of two opposing triggers.
Try defining the style outside the TextBlock --
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Setter Property="TextBlock.Foreground" Value="Green" />
<DataTrigger Binding="{Binding IsNegativeChange}" Value="True">
<Setter Property="TextBlock.Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} ({1})">
<Binding Path="ReturnedData.Change" />
<Binding Path="ReturnedData.ChangePercentage" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
Hope that helps!