WPF Trigger, is it like an if/else? - wpf

I've been reading into Triggers lately and am trying to incorporate it into my application but I keep getting the thought that the triggers act like if statements, if this property has a value of this, do this to the control.
What I'm wondering is if we can change a different controls' properties in another trigger? I'm looking to either remove an extra row that is created dynamically or increase the width of a button by a columnspan of 3 only if a button is hidden or specific label text is on the window.
I'm trying to figure out how to do this, but anything I'm trying is requiring me to only change the property of the control that is within the trigger, it won't allow me to change a property of a control outside of the one in the trigger.
Here's what I'm wanting to do:
<Style x:Key="Triggers" TargetType="{x:Type Label}">
<Style.Triggers>
<Trigger Property="Label.Content" Value="Test Label1: ">
<Setter TargetName="Button1" Property="Grid.ColumnSpan" Value="3" /> 'Error 13 Cannot find the Trigger target 'Button1'. (The target must appear before any Setters, Triggers, or Conditions that use it.)
</Trigger>
</Style.Triggers>
</Style>
What my mind is coming up with is the trigger is the IF, the setter is the code within the IF if the logic matches(property is true) - so If the Label contains the text 'Test Label1: " then increase button width by 3 columns. Am I thinking triggers have the functionality they don't and is this possible to do within xaml?

A Trigger is somewhat like an if condition in XAML. However, when using a Trigger to change a property dependant on another control, try to remember this rule of thumb:
Add the Trigger to the control that must do something in reaction to a change in another control
In your case, you want the Button to span some columns when the Label.Content = Test Label1:, so you'd need to apply the Trigger to the Button instead. Furthermore, to read the value of the Label.Content from the Button, you'll need to use a DataTrigger. Try this:
<Button Content="Something">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Content, ElementName=LabelName}"
Value="Test Label1: ">
<Setter Property="Grid.ColumnSpan" Value="3" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Related

Set property after command

I have a StatusBar below the screen in SL4 (using PRISM), just a very simple Telerik RadDockPanel.
I also have a menu (Telerik RibbonView with RadRibbonGroup and RadRibbonToggleButton). When the toggle button is pressed, I want to set the text to 'ON' and 'OFF', and I want to hide the status bar, but... only in XAML (not using code behind).
I believe this is a common SL/WPF coding practice... but how ?
Have to use EventTrigger (check example bellow on page by link that I provided) and ObjectAnimationUsingKeyFrames to change properties that aren't animated (Text, Visibility so on).
Check good example in other answer on so.
You can specify a DataTrigger in your window like this -
<StatusBar.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=MyRadRibbonToggleButton, Path=IsChecked}"
Value="True">
<Setter Property="Grid.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBar.Style>
In case you can't use ElementName binding then you can use a property in your ViewModel(corresponding to RadRibbonToggleButton state). Similar Trigger can be created for a TextBlock/Label to show On/Off text.
This is how I implement this kind of functionality in WPF/MVVM applications;
You may have to apply some hack to make this work with telerik controls.

How to use StyleSelector without losing the global style set?

lets say that I have a global style set for ItemsContainer control, and this style is applied well during runtime. Now I have a requirement to paint Row background for each row, based on some condition. For this I decided to use RowStyleSelector.
<RadTreeListView RowStyleSelector="{StaticResource ActivityRowStyleSelector}" />
<Style x:Key="xxxTreeListViewStyle" TargetType="telerik:TreeListViewRow">
<Setter Property="Background" Value="#FFFFE1C4" />
</Style>
<local:xxxRowStyleSelector x:Key="xxxRowStyleSelector"
FailureStyle="{StaticResource xxxTreeListViewStyle}"
.../>
But this overrides the global style that I set for row, and I just want to change Background property to existing style.
And another question is: how would I apply this change to a particular cell, not row?
You can use the Style.BasedOn property to "inherit" one style from another. Use it like this (where RowGlobalStyle is the name of the global style you've created previously):
<Style x:Key="xxxTreeListViewStyle" TargetType="telerik:TreeListViewRow"
BasedOn="{StaticResource RowGlobalStyle}">
<Setter Property="Background" Value="#FFFFE1C4" />
</Style>

How to disable button when its Button.CommandProperty is null

Short explanation
Button.CommandProperty is bound to SomeObject.SomeCommand property of the ViewModel. When SomeCommand property of SomeObject is set to null or entire SomeObject property is set to null, this button remains enabled. How can the button be disabled under this circumstances?
Detailed explanation
I am creating application using MVVM, which behaves like a browser:
Main view_model (which corresponds to main window as view) has a list of Workspace view_models. Each Workspace view_model corresponds to TabPage in windows's TabControl.
Main view_model has CurrentWorkspace property which corresponds currently active TabPage.
In main window where is a toolbar with buttons, which utilizes commands provided by the CurrentWorkspace. For example, access to reloading workspace data is realized as:
<Button Name="btReload" Content="Reload"
Command="{Binding Path=CurrentWorkspace.ReloadCommand, UpdateSourceTrigger=PropertyChanged}"/>
I tried to accomplish the task of button disabling by creating DataTriggers, but it seems that triggers works only first time and no more:
<Button Name="btReload" Content="Reload"
Command="{Binding Path=CurrentWorkspace.ReloadCommand, UpdateSourceTrigger=PropertyChanged}">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentWorkspace, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="{x:Null}">
<Setter Property="dxb:BarButtonItem.IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentPage.CurrentWorkspace, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="{x:Null}">
<Setter Property="dxb:BarButtonItem.IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
it looks really stupid: like MS Word with documentless client area and at the same time with a lot of ready-for-click buttons in toolbar (with formatting and other document-specific features). Please, help me, :)
P.S. When adding to toolbar a button with DataContext binded to CurrentWorkspace, then its DataContextChanged event fires properly when activating or adding or removing workspace tab in the window. So, the problem somewhere in DataTrigger (or in View as generally), not in it's ViewModel(s).
UPDATE
I uploaded sample project on VS2010, link for the archive: http://www.filefactory.com/file/b43455e/n/WhatIfCommandIsNull.rar
bellow its description.
TextBox is bound to the ViewModel.Data property
Assigning or removing ViewModel into/from Window.DataContext can be done by clicking two buttons - btAssignViewModel and btRemoveViewModel respectively
ViewModel exposes two commands, one of which sets ViewModel.Data to string value, other - sets it to the NULL
These commands are bound to the buttons btSetData & btResetData via their Button.Command properties
As you can see, when Window.DataContext is set to the ViewModel instance, both the commands working properly, & ResetDataCommand.CanExecute is working too (when ViewModel.Data is NULL, ResetDataCommand.CanExecute returns false & button btResetData is disabled). Once the Window.DataContext is set to null, last two buttons enables (for the first two ones trere are no commands are bound).
The problem is to realize declaratively (via triggers) next four rules:
If btAssignViewModel.DataContext is not null then btAssignViewModel.IsEnabled = false, else true.
If btRemoveViewModel.DataContext is null then btRemoveViewModel.IsEnabled = false, else true.
If ViewModel.Data is null then btSetData.IsEnabled = true, else false.
If ViewModel.Data is null then btResetData.IsEnabled = false, else true.
I think that first two rules can be realized using Triggers, second two - using DataTriggers. But they aren't working, so I erased them from the project.
This might work (pending your circumstances)
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="Command" Value="{x:Null}">
<Setter Property="IsEnabled" Value="false"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>

How to use MultiDataTrigger to check a single condition to be true in Style.Triggers in WPF?

I have three grids in my UserControl of which one control is shown at time. In the last column I need to use a Style where I need to check the data and apply a ForeGround color. I can write style at each of the control in 3 grids using DataTriggers. But I want a concrete style in Resource which can be used anywhere. I tried MultiDataTrigger but it doesn't serve my purpose as it checks 2 or more Condintions to be true in MultiDataTrigger.Conditions whereas i need to check data in a single control. Are there any alternate solution to achieve this?
If you're using some kind of a grid, you're probably using CellTemplate or some other property like that to accomplish your task. I think you do need to use different styles in different columns.
But if those styles are the same except for the triggers, then you can make one style with everything that's common to both of them, and then create another style based on the first one. It's a bit similar to inheritance in OOP.
This is how it may look like:
<Style x:Key="BaseStyle" TargetType=".....">
<!-- Common setters and triggers -->
<Setter ... />
<Setter ... />
<Setter ... />
</Style>
<Style x:Key="InheritedStyle" BasedOn="{StaticResource BaseStyle}" TargetType=".....">
<!-- This style's specific setters and triggers -->
<Setter ... />
<Style.Triggers>
...
<Style.Triggers>
</Style>

Giving a WPF ItemsControl a different look based off whether ItemsSource holds a single value or multiple values?

Are there any slick ways to style/template a WPF ItemsControl differently based off whether ItemsSource holds a single value or multiple values?
What I've done so far is to create a custom ItemsControl class which among other things displays the list of bound items as a horizontally oriented comma separated list. So far I'm pretty happy with the results however I want to show a more brief view of the bound data in cases where multiple values are bound and if only a single value is bound then I want to show a more extended view of the bound data with a longer string description. I figure this is probably best solved by dynamically choosing the template either based off a trigger or possibly by using a template selector but it's not yet clear to me how this would be done.
You could use a DataTrigger in your style to replace the template:
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Items.Count}" Value="1">
<Setter Property="Template">
<Setter.Value>
<!-- Insert Template here -->
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
You could also add one for where the Value is 0 if you wanted to display a "no records" template.
You should use a StyleSelector.
Here is a sample.

Resources