How fire trigger when one or multiple property had changed? - wpf

I have many components in my XAML, I need fire Trigger when one or many components has your value changed.
I not want use one PropertyChangedTrigger for each components, I want use one Trigger for all components.
thanks.

Yes, use a MultiDataTrigger. Here's an example from the linked MSDN documentation about how you can use this on multiple properties:
<Window.Resources>
<c:Places x:Key="PlacesData"/>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=State}" Value="WA">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=Name}" Value="Portland" />
<Condition Binding="{Binding Path=State}" Value="OR" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Cyan" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type c:Place}">
<Canvas Width="160" Height="20">
<TextBlock FontSize="12"
Width="130" Canvas.Left="0" Text="{Binding Path=Name}"/>
<TextBlock FontSize="12" Width="30"
Canvas.Left="130" Text="{Binding Path=State}"/>
</Canvas>
</DataTemplate>
</Window.Resources>
<StackPanel>
<TextBlock FontSize="18" Margin="5" FontWeight="Bold"
HorizontalAlignment="Center">Data Trigger Sample</TextBlock>
<ListBox Width="180" HorizontalAlignment="Center" Background="Honeydew"
ItemsSource="{Binding Source={StaticResource PlacesData}}"/>
</StackPanel>
EDIT: Not the cleanest solution ever, but you could do something like this. Basically use the MultiDataTrigger to execute whenever any of the properties change. Then, you use a converter for a simple null check (or perhaps you could always return true in your case). That way, your value in the MultiDataTrigger is just True instead of a specific value.

Related

Handling different item templates that share the same content

I have two custom ItemTemplates for the ListBox, one for the regular items, and one for the selected item. An example of how would I handle this is:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContentTemplate" Value="{StaticResource Template1}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource Template2}" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
Template1 and Template2 are very similar:
<DataTemplate x:Key="Template1">
<SameContent />
<DifferentContent1 />
</DataTemplate>
<DataTemplate x:Key="Template2">
<SameContent />
<DifferentContent2 />
</DataTemplate>
So, is it a proper way to duplicate the code for the SameContent (which is like a bunch of TextBlocks, Panels, etc) in both templates, or it is a better approach to have only one template, but switch the DifferentContent based on the IsSelected property, or...?
if second approach, how would it be properly done?
Obviously duplicating the code is not a very good solution. A better approach is to define another DataTemplate as your common content and then use ContentPresenter to present it:
<Window.Resources>
<DataTemplate x:Key="CommonTemplate">
<TextBlock Text="{Binding CommonProperty1}" />
<TextBlock Text="{Binding CommonProperty2}" />
</DataTemplate>
<DataTemplate x:Key="Template1" >
<StackPanel>
<ContentPresenter ContentTemplate="{StaticResource CommonTemplate}"/>
<TextBlock Text="{Binding Template1Property1}"/>
<TextBlock Text="{Binding Template1Property2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Template2" >
<StackPanel>
<ContentPresenter ContentTemplate="{StaticResource CommonTemplate}"/>
<TextBlock Text="{Binding Template2Property1}"/>
<TextBlock Text="{Binding Template2Property2}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>

Conditional formatting for the Header content

I have the following template for some header:
<telerik:RadExpander.Header>
<StackPanel Orientation="Horizontal" DataContext="{Binding CurrentItem, ElementName=activityProductGrid}">
<TextBlock Text="{Binding Strings[Product], Source={StaticResource LanguageResources}, StringFormat='{}{0}: '}" />
<TextBlock Text="{Binding Product.Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Strings[GroupName], Source={StaticResource LanguageResources}, StringFormat=', {0}: '}" />
<TextBlock Text="{Binding GroupName}" />
<TextBlock Text="{Binding Strings[UnitPrice], Source={StaticResource LanguageResources}, StringFormat=', {0}: '}" />
<TextBlock Text="{Binding UnitPrice}" />
</StackPanel>
</telerik:RadExpander.Header>
How can I do conditional formmatting of the header, if for example Product == null, then Header should not display anything?
Edit: This one works.
<StackPanel Orientation="Horizontal" DataContext="{Binding CurrentItem, ElementName=activityProductGrid}">
<i:Interaction.Triggers>
<ie:DataTrigger Binding="{Binding}" Value="{x:Null}">
<ie:ChangePropertyAction TargetObject="{RelativeSource={RelativeSource AncestorType=StackPanel}}" PropertyName="Visibility">
<ie:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ie:ChangePropertyAction.Value>
</ie:ChangePropertyAction>
</ie:DataTrigger>
</i:Interaction.Triggers>
<-- from this point the came code as above -->
</StackPanel>
Specifically for null, you can specify binding parameters for a replacement value, or one to display in case of binding errors:
<TextBlock
Text="{Binding Product.Name, TargetNullValue=(empty), FallbackValue=(error)}"/>
(Taken from the WPF Binding Cheatsheet)
EDIT: I noticed that you didn't just want a specific binding to have a different value, but the whole control to not be displayed. For this, you can use Styles and Triggers, binging a DataTrigger to your Product property, and setting Visibility if it's null.
<telerik:RadExpander.Header>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding Product}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</telerik:RadExpander.Header>

How do I switch DataTemplate by RadioButton checked state changing using only xaml?

I have WPF window which contains Frame and two RadioButtons. I need to switch Frame's ContentTemplate by changing RadioButtons checked state. How do I implement that using only xaml?
There isn't much to go on since you didn't add any sample code.. Try something like this
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<DataTemplate.Resources>
<DataTemplate x:Key="dataTemplate1">
<TextBlock Text="Template 1"/>
</DataTemplate>
<DataTemplate x:Key="dataTemplate2">
<TextBlock Text="Template 2"/>
</DataTemplate>
</DataTemplate.Resources>
<StackPanel>
<Frame x:Name="frame1"
Height="100"
ContentTemplate="{StaticResource dataTemplate1}"/>
<RadioButton x:Name="template1RadioButton" IsChecked="True" Content="Template 1"/>
<RadioButton x:Name="template2RadioButton" Content="Template 2"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=template2RadioButton, Path=IsChecked}" Value="True">
<Setter TargetName="frame1" Property="ContentTemplate" Value="{StaticResource dataTemplate2}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
You can use the Triggers of Radio Button like
<RadioButton.Triggers>
<Trigger Property="RadioButton.IsChecked" Value="True">
//enter your xaml here..
</Trigger>
</RadioButton.Triggers>
You can see more information about Triggers on this blog Triggers

Bind rectangle fill to a value

<UserControl.Resources>
<DataTemplate x:Key="MyCustomTemplate">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=ID}"/>
<Rectangle Height="18" Width="20" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ListBox x:Name="userListBox" Margin="10"/>
</Grid>
Code behind:
userListbox.ItemsSource = myservice.getvalue();
Now how do I bind the rectangle color. The getValue return a list of Objects whose one member is integer and I have to use that value to decide the color of rectangle.
Say if object.item = 1 color = green
object.item=2 color = red
Use ValueConverter, info here:
http://blogs.msdn.com/b/bencon/archive/2006/05/10/594886.aspx
you can use data trigger to achieve that, example:
<DataTemplate x:Key="MyCustomTemplate">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=ID}"/>
<Rectangle x:Name="rect" Height="18" Width="20" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=object.item}" Value="1">
<Setter TargetName="rect" Property="Fill" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=object.item}" Value="2">
<Setter TargetName="rect" Property="Fill" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
and then bind the value for the templated control to selected item of the listbox
<Label ContentTemplate="{DynamicResource MyCustomTemplate}" Grid.Column="2" Content="{Binding ElementName=userListBox, Path=SelectedItem}"/>
or if the datatemplate is actually for the listbox, then you can do it this way:
<ListBox x:Name="userListBox" Margin="10" ItemTemplate="{DynamicResource MyCustomTemplate}" />

WPF Tooltip Binding

I am only two weeks into WPF so this is probably a trivial question. I have a collection "CellList" which has a few properties I would like to bind to a ToolTip so when I hover over a label information from the current instance of CellList is displayed. How do I do that? I understand simple binding and this maybe simple binding too but I can't wrap my head around it. Below is my XAML for the label. Could someone explain to me how I can accomplish this.
<HierarchicalDataTemplate>
<ListBox ItemsSource="{Binding CellList}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content=" " Height="20" Width="15" Background="{Binding Path=ExptNameBkg, Converter={StaticResource ExptNameToBrushConverter}}" BorderBrush="Black" BorderThickness="1" >
</Label>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</HierarchicalDataTemplate>
Thanks.
The tricky thing about ToolTips is that a ToolTip is an object you associate with a control, and not part of the control's visual tree. So you can't populate it the way you'd populate things in the visual tree, e.g.:
<TextBox.ToolTip>
<StackPanel>
...put bound controls here
</StackPanel>
</TextBox.ToolTip>
Instead, what you have to do is create a specific instance of a ToolTip, and assign it a style that sets its DataContext (very important; that's how you can bind to the properties of the data source of its "placement target," i.e. the control that's displaying the tooltip) and its Template. Then put the visual tree of the ToolTip, including bindings, into the template. Finally, reference the ToolTip in your control.
So, here's a TextBox whose Binding does validation:
<TextBox ToolTip="{StaticResource ErrorToolTip}">
<TextBox.Text>
<Binding Source="SourceProperty">
<Binding.ValidationRules>
<DataErrorValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
It uses this ToolTip:
<ToolTip x:Key="ErrorToolTip" Style="{StaticResource ErrorToolTipStyle}"/>
And the ToolTip uses this style, which gets its content from the ValidationError property of the TextBox's binding source:
<Style x:Key="ErrorToolTipStyle" TargetType="{x:Type ToolTip}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="DataContext" Value="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border
Name="Border"
BorderThickness="1"
BorderBrush="LightGray">
<StackPanel Orientation="Vertical">
<Label Background="Firebrick" Foreground="White" FontWeight="Bold" Margin="4">Validation error</Label>
<TextBlock Margin="10" Text="{Binding ValidationError}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" Value="true">
<Setter TargetName="Border" Property="CornerRadius" Value="4"/>
<Setter TargetName="Border" Property="SnapsToDevicePixels" Value="true"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I'm not certain of this, but I think that the only part of the above that actually has to be set in the style is the DataTrigger setting the DataContext; I think most everything else could just be explicitly set in the ToolTip's visual tree. But I'm probably not thinking of something important.
<Label Content={Binding Path=Id} ToolTip={Binding Path=Name}/>
just try this
Here's a kaxaml-ready example that includes a tooltip that is a little more elaborate than just text:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<XmlDataProvider x:Key="CharacterData">
<x:XData>
<Data xmlns="">
<Character First="Bart" Last="Simpson" Background="LightGreen" />
<Character First="Homer" Last="Simpson" Background="LightBlue" />
<Character First="Lisa" Last="Simpson" Background="Pink" />
<Character First="Maggie" Last="Simpson" Background="Yellow" />
<Character First="Marge" Last="Simpson" Background="PapayaWhip" />
</Data>
</x:XData>
</XmlDataProvider>
<ToolTip x:Key="ElaborateToolTip">
<Grid Margin="5">
<Rectangle RadiusX="6" RadiusY="6" Fill="{Binding XPath=#Background}" />
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="{Binding XPath=#First}" Margin="0,0,6,0" />
<TextBlock Text="{Binding XPath=#Last}" />
</StackPanel>
</Grid>
</ToolTip>
</Page.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource CharacterData}, XPath=Data/Character}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ToolTip" Value="{StaticResource ElaborateToolTip}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#First}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Page>

Resources