I need to flip between two views dynamically based on a boolean flag in my ViewModel.
I thought it would be as simple as:
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{StaticResource View1}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsShowingView2}">
<Setter Property="Content" Value="{StaticResource View2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
However, View2 never gets displayed, even if IsShowingView2 is always true.
Any ideas anyone? All the examples I can find seem to be altering the ContentTemplate instead, but I have no need to do that. I just want different content.
You're not actually setting a value for the DataTrigger
<DataTrigger Binding="{Binding IsShowingView2}" Value="True">
<Setter Property="Content" Value="{StaticResource View2}" />
</DataTrigger>
Also check for binding errors in the output window.
Related
This behavior seems incredibly odd to me, and I assume I am doing something wrong to get it. I have a ContentControl that uses a DataTemplete to render an TabControl. I want an image to display when there are no tabs open, and hide when there are. But here is the problem:
<Image Name="image1" Stretch="Uniform" Visibility="Hidden" Source="/Affinity;component/Images/affinity_logo.png">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, ElementName=tabcontrolworkspaces}"
Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
This doesn't work... sort of.
I have tested this on Visiblity and Margin (just to be sure). This trigger will alter the property, unless that property is defined in the Image tags. If it is, the trigger will not update that property. So, if I don't define a visibility for the image, and the trigger hides it, it works. The problem is, the default is Visible and the trigger needs to show it when value=0 and hide it otherwise.
Why won't the trigger override properties that are explicitly defined? Isn't that its purpose?
This is the normal Dependency Property Value Precedence. Setting it on Image is at #3, while in the Style trigger is at a lower precedence of #6.
You can do this instead:
<Image Name="image1" Stretch="Uniform" Source="/Affinity;component/Images/affinity_logo.png">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, ElementName=tabcontrolworkspaces}"
Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Set your Visibility in the Style in addition to in the Trigger
I've encountered this weird behavior with DataTriggers many times, where sometimes DataTrigger Setters won't take effect unless the Setter is also defined in the Style.
Won't work
<Image Visibility="Collapsed">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding Something}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Image.Style>
</Image>
Will work
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Something}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Image.Style>
</Image>
Edit: See the accepted answer for an explanation on why this doesn't work. It has to do with the order in which dependency properties are determined, where properties defined in the <Tag> always take precedence over triggered values.
When a trigger is true it changes the value to the desired value. When it is no longer true it returns the value to the previous value. It won't change it to a value it wans't.
This means that if the original value is visible, and you change it to visible, when the trigger is no longer active, the value will revert back to visible.
I am working on a c# WPF project which I am using a style trigger to style the background colour of the each row based on one of the cell values.
Below is my style trigger.
<Window.Resources>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Highest Alarm Level}" Value="Critical">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Highest Alarm Level}" Value="Medium">
<Setter Property="Background" Value="Orange" />
</DataTrigger>
<DataTrigger Binding="{Binding Highest Alarm Level}" Value="Warning">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
<DataTrigger Binding="{Binding Highest Alarm Level}" Value="Info">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
What I need to be able to do, is under certain circumstances I don't want this style to be used, so what I am after, is if in the C# code a certain condition becomes to true, I want to disable all of the styling done above to not be used, i.e. all of the styling is turned off so the background colour of the rows are not set.
Add a Key to your custom DataGridRow Style
<Style TargetType="DataGridRow" x:Key="MyRowStyle">
<!-- Define Triggers -->
</Style>
Then you just need to switch between default and custom styles.
If you set Style with null, that means the default Style will be applied
dataGrid.RowStyle = _boolCondition ? this.FindResource("MyRowStyle") as Style : null;
I had make an Button Style as DataTemplate in a ResourceDictionary. Here a small part:
<Style TargetType="{x:Type Button}">
<Setter Property="Focusable" Value="False"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border">
...
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" TargetName="border" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In this Template a have a binding to a Propertie IsSelected. This Propertie is in one situation there and in a other not.
Is it possible to Check in Xaml if the binding path exist, then use it in other case forget it?
Now i had BindingExpression in the Debug output and i want to eliminate this.
The more pertinent question is: why do you have a DataTrigger in your ControlTemplate? This creates a dependency between the control and its data context, which is why you're experiencing this issue when your data context does not match the control template's expectations.
Are you certain you cannot use a more suitable mechanism? For example, could you instead use a style for those buttons where IsSelected should affect the Background?
<Style x:Key="SpecialButtonStyle" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<Button DataContext="relevant data context" Style="{StaticResource SpecialButtonStyle}"/>
Or, even better, could you define a data template for the specific data class you have that has the IsSelected property? This data template could automatically use the correct Button style.
after having found much help on stackoverflow, I am now struggling with a problem I could not find an answer for yet. My goal is the following:
I have a datagrid in a wpf/c# application filled with figures. Dependending on whether the figure is positive or negative, I want to change the foreground font to green or red. The itemsource for the datagrid is a list of my own class which includes the following elements:
string description
a list of 12 doubles called totalMoney
I have manually configured the columns for the datagrid so that the first shows the description, then the value of totalMoney[0], then totalMoney[1] and so on. After some searching, I found a way to change the foreground colour of the individual cells based on the values here on stackoverflow via a datatrigger method plus IValueConverter with the following code:
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding totalMoney[2], Converter={StaticResource money}}" Value="1">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding totalMoney[2], Converter={StaticResource money}}" Value="0">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding totalMoney[2], Converter={StaticResource money}}" Value="-1">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
However, as you see in the code, in WPF I have to reference the Binding to an individual item in my List (totalMoney[X]). I therefore have to manually set the style for each Column in my datagrid. As I want to use the style in several datagrids, this seems to me as highly unpractical.
My question is therefore - is it possible to define the style so that it changes depending on the value of a cell in general? Or is there maybe a completely different way to achieve my goal that I have overlooked?
Thank you for your help.
At first glance, you could use the following style for all totalMoney[] columns ...
<Style TargetType="{x:Type TextBlock}" x:Key="MoneyIndicatorStyle">
<Style.Triggers>
<DataTrigger
Binding="{Binding Text, RelativeSource={RelativeSource Self},
Converter={StaticResource money}}"
Value="1">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
<DataTrigger
Binding="{Binding Text, RelativeSource={RelativeSource Self},
Converter={StaticResource money}}"
Value="0">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger
Binding="{Binding Text, RelativeSource={RelativeSource Self},
Converter={StaticResource money}}"
Value="-1">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
But here is a potential catch... if tommorrow you decide that the text of these textblocks is going to be a formatted currency like this... $(1,00) i.e. negative 100 dollars in accounting world, then the converter could have to cast this formatted string into a locale-neutral number i.e -100.00 and yield 0, 1 or -1 based on the current logic in converter.
Right now your converter is at ease because it receives a straightforward numeric value (totlaMoney[n]) from your model but using the above style it will rely on the Text displayed by the Textblock!
So the decision is yours.
I'm using the WPF DataGrid and have it set to place a red border around any row that the user modifies. It works well, however when the border appears all the cells inside that row get pushed over a pixel or two. In other words a cell's left and right border no longer lines up with the one above or below it, so it looks weird.
Here's the code I'm using to get the red border to appear OnEdit (Note: IsDirty is a property on my bound object that's set when a value gets modified):
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="true">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
What can I add so that I can modify the red border margins so that it won't interfere with the cells' borders?
Thanks
One way could be to apply border to each row rather than the dirty one but set the border to transparent and set the border to red for the row which is actually dirty
Something like
<Style TargetType="DataGridRow">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
The Visual Tree looks something like this
DataGridRow
Border
DataGridCellsPresenter
When you change the BorderThickness of the Border you could compensate this by adding negative Margin.Left to the DataGridCellsPresenter at the same time. Not the prettiest solution but it works
<DataGrid ...>
<DataGrid.Resources>
<Style TargetType="DataGridCellsPresenter">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="true">
<Setter Property="Margin" Value="-1,0,0,0" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="true">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<!--...-->
</DataGrid>