WPF Trigger not working, what am i doing wrong? - c#

I have the following code (only relevant snippets)
<ribbon:RibbonWindow .............>
<Grid>
<ribbon:Ribbon>
<ribbon:RibbonToggleButton
x:Name="Button2"
SmallImageSource="Images\SmallIcon.png"
Label="Properties">
<ribbon:RibbonToggleButton.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="SPanel1" Property="UIElement.IsVisible" Value="False"/>
<Setter TargetName="SPanel2" Property="UIElement.IsVisible" Value="True"/>
</Trigger>
</ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
</ribbon:Ribbon>
</Grid>
</ribbon:RibbonWindow>
And when i run the application, it hangs up and te debugger pops up. What am i doing wrong?
I have tried setting the trigger on the grid, wrapping everything in a control template and setting a trigger there, same problem!
Edit
I have tried creating a button, and only the tag of trigger causes the Unhandled Exception error.
<Button Content="Button" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="12,27,0,0" Name="TestButton" VerticalAlignment="Top" Width="75" >
<Button.Triggers>
<Trigger></Trigger>
</Button.Triggers>
</Button>
Edit 2
Using Data triggers makes the Unhandled Exception go away but the trigger is not responding:
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button2, Path=ToggleButton.IsChecked}" Value="True">
<Setter Property="UIElement.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
Edit 3
<ribbon:RibbonToggleButton
x:Name="Button2"
SmallImageSource="Images\SmallIcon.png"
Label="Properties">
</ribbon:RibbonToggleButton>
And The panel to be hidden
<StackPanel
Grid.Row="2"
Grid.Column="1"
x:Name="SPanel1"
Visibility="Visible">
<Label>*Deafult Grid*</Label>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button2, Path=ToggleButton.IsChecked}" Value="True">
<Setter Property="UIElement.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
And for second StackPanel that i want to show
<StackPanel
Grid.Row="2"
Grid.Column="1"
x:Name="SPanel2"
Visibility="Hidden">
<Label>*Panel 2 *</Label>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button2, Path=IsChecked}" Value="True">
<Setter Property="UIElement.Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>

You need to use DataTrigger, and set the style property of the two panels to the IsChecked propery of the checkbox, here an example
<CheckBox Name="check" Content="Prova" IsChecked="True"> </CheckBox>
<Canvas Name="SPanel1" Background="Blue" Width="100" Height="100">
<Canvas.Style>
<Style TargetType="Canvas">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=check, Path=IsChecked}" Value="True">
<Setter Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
</Canvas>

UIElement.IsVisible is not a mutable property. You'll have to set UIElement.Visibility, which is an enum.
The reason why you get an exception is because FrameworkElement.Triggers only accepts EventTrigger. This means that if you do UIElement.Triggers (Button.Triggers or ribbon:RibbonToggleButton.Triggers), you can only add EventTriggers under that. Style, DataTemplate and ControlTemplate accept all TriggerBase derived classes.
Edit
As Mackho pointed out, you cannot use TargetName under Style. You'd have to accomplish this using data binding.
Assuming SPanel1 is a StackPanel, here's what you can do:
Create a converter
public class BoolToVisibilityConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Somewhere in the root control's resources, add BoolToVisibilityConverter
Add x:Name to your toggle button; lets say you call it "MyToggleButton"
<StackPanel x:Name="SPanel1" ...
Visibility="{Binding IsChecked, ElementName=MyToggleButton, Converter={StaticResource BoolToVisibilityConverter}}" />
And do something very similar for SPanel2.

Related

Dynamic Combobox isselected

I have a combobox that has the period of the day as its items. I am trying to make the isSelected item based on the period on the day through the use of a converter but for some reason my code won't work.
Below is my xaml:
<Window.Resources>
<staticData:SelectedPeriodConverter x:Key="SelectedPeriodConverter"/>
</Window.Resources>
<ComboBox Grid.Column="4" Margin="0,7" Width="100" HorizontalAlignment="Right" Name="PeriodPicker" VerticalAlignment="Top" Height="25" SelectedItem="PM">
<ComboBoxItem>AM</ComboBoxItem>
<ComboBoxItem>PM</ComboBoxItem>
<ComboBox.ItemContainerStyle>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource Self}, Converter={StaticResource SelectedPeriodConverter}}" Value="True">
<Setter Property="ComboBoxItem.IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
and my c# code for the converter is as follows:
public class SelectedPeriodConverter : IValueConverter
{
public object Convert(object values, Type targetType, object parameter, CultureInfo culture)
{
string test = values.ToString();
if (test == DateTime.Now.ToString("tt"))
{
return true;
}
else
{
return false;
}
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
return value;
}
}
The weird thing is is if I change isSelected to IsEnable it will trigger but otherwise it won't.
Another way I tried was to have the styling in the Windows resources.
This worked if I targetted comboboxitem but would not work if I added an x:class and used ItemContainerStyle to isolate the trigger to one combobox as I did not want it working on all the comboboxes I had in my form.
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource Self}, Converter={StaticResource SelectedPeriodConverter}}" Value="True">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ComboBox Grid.Column="4" Margin="0,7" Width="100" HorizontalAlignment="Right" Name="PeriodPicker" VerticalAlignment="Top" Height="25" SelectedItem="PM">
<ComboBoxItem>AM</ComboBoxItem>
<ComboBoxItem>PM</ComboBoxItem>
</ComboBox>
Does anyone know how I can get this to work?
thanks Callum
The fix was quiet simple.
Inserting the second code inside ComboBox.Resources tags instead of Windows.Resources tags worked for me like so:
<ComboBox.Resources>
<staticData:SelectedPeriodConverter x:Key="SelectedPeriodConverter"/>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource Self}, Converter={StaticResource SelectedPeriodConverter}}" Value="True">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Resources>
thanks Callum

How to Change ContentControl `s Content on Toggle Button IsChecked Proprrty

Currently i am working on Changing the Content ( as image) of a container on toggle button IsChecked Proprty.
So i thought ContentControl would be a nice choice for a container. But i am not able to figure out how to achieve the result.
I created a resource of images in windows.resource
<Image Source="Resources/Desert.jpg" x:Key="image1"/>
<Image Source="Resources/Koala.jpg" x:Key="image2"/>
<Image Source="Resources/Lighthouse.jpg" x:Key="image3"/>
<Image Source="Resources/Chrysanthemum.jpg" x:Key="image4"/>
So i thought to Use the above resource to change the Content Property of a ContentControl by changing the ControlTemplate proprty by using Triggers where SourceName as (ToggleButton) and TargetName as (ContentControl) but its not working
SO how can I change the content of a ContentControl on toggleButton Ischeck property.
Edit
<ContentControl BorderBrush="Black" Name="cc">
<ControlTemplate>
<ControlTemplate.Triggers>
<Trigger SourceName="ToggleButton" Property="IsChecked" Value="True">
<Setter TargetName="cc" Property="Content" Value="{StaticResource image1}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl>
I just tried as i have no idea how to do it.....
Any help are welcomed
You seem to have over complicated the situation unnecessarily. You can just use an Image control rather than a ContentControl. To alternate between two Images, you can simply do this with a DataTrigger:
<Image Source="Resources/Desert.jpg" x:Key="image1"/>
<StackPanel>
<ToggleButton Name="Button" Content="Change Image" Margin="10" />
<Image Margin="10,0,10,10">
<Image.Style>
<Style>
<Setter Property="Image.Source"
Value="/YourAppName;component/Resources/Desert.jpg" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=Button}"
Value="True">
<Setter Property="Image.Source"
Value="/YourAppName;component/Images/Resources/Koala.jpg" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</StackPanel>

Custom ListBox in WPF

I am trying to create a custom ListBox control in WPF for a chat Messenger. I am using an ellipse to show the online/offline user. The ellipse is to be displayed on left and some text in center of the ListBoxItem.
I want to set the ellipse fill propert to red/green based on some variable.
This is what I have done :
<ListBox Name="myList" HorizontalAlignment="Left" Height="232" Margin="117,74,0,0" VerticalAlignment="Top" Width="207">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Ellipse Name="ellipse" Fill="Red" DockPanel.Dock="Left">
<Ellipse.Triggers>
<Trigger Property="{Binding Online}" Value="True">
<Setter TargetName="ellipse" Property="Ellipse.Fill" Value="Green"/>
</Trigger>
</Ellipse.Triggers>
</Ellipse>
<TextBlock Text="{Binding text}"></TextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in the code :
myList.Items.Add(new { text="Hello",Online="True" });
I am getting an error as
Cannot find the static member 'FillProperty' on the type 'ContentPresenter'.
What am I doing wrong here?
Obviously this is wrong: Property="{Binding Online}"
Also you should use a Style for triggers, no need to set TargetName, and you need to take precedence into consideration, and use a Setter for the default value.
you are actually misleading WPF with some of these concerns.
Binding A property on trigger will not work. you have to use DataTrigger insteed of Triggers.
Implementing Trigger on the Fly for any control. most of the times not work. So go with Styles.
While you are creating Ellipse in template make sure you have created enough size for it. So that can be visible to users.
try this.
<Window.Resources>
<Style x:Key="elstyle" TargetType="Ellipse">
<Setter Property="Height" Value="5"/>
<Setter Property="Width" Value="5"/>
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Online}" Value="true">
<Setter Property="Fill" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListBox x:Name="myList" HorizontalAlignment="Left" Height="232" Margin="117,74,0,0" VerticalAlignment="Top" Width="207">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Ellipse Name="ellipse" Margin="5" DockPanel.Dock="Left" Style="{DynamicResource elstyle}">
</Ellipse>
<TextBlock Text="{Binding Name}"></TextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
code behind .
public MainWindow()
{
Random r = new Random();
InitializeComponent();
for (int i = 0; i < 10; i++)
{
myList.Items.Add(new { Name = "Name" + i.ToString(), Online = Convert.ToBoolean(r.Next(-1, 1)) });
}
}
You need to set the initial colour via a Setter and not in XAML. For more information and see this question: Change the color of ellipse when mouse over
there are a few issues in your XAML
set the size of your Ellipse
you need to use Style instead of Ellipse.Triggers
set your Fill color in your Style if you wane be able to change it in XAML by some condition
here an working example for your problem
<DataTemplate>
<!--<DockPanel> juste because i like StackPanel-->
<StackPanel Orientation="Horizontal">
<!--<Ellipse Name="ellipse" Fill="Red" DockPanel.Dock="Left">-->
<Ellipse Name="ellipse" Width="15" Height="15">
<!--<Ellipse.Triggers>-->
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<!--<Trigger Property="{Binding Online}" Value="True">-->
<DataTrigger Binding="{Binding Online}" Value="True">
<Setter Property="Fill" Value="LimeGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock Text="{Binding text}"/>
</StackPanel>
</DataTemplate>

WPF DataTemplate Binding depending on the type of a property

I have a collection of objects bound to a hierarchical data template, each of my objects have a property on them (lets call it Property "A") that is of a certain type. This type varies among each of the objects.
If the data template contains an image and some text, what would be the best way to change the image that is displayed in the template based on the type of property "A".
I know I could just stick this into a converter and do the binding translation manually in code, but with all the binding facilities available in WPF, I think theres probably a better way.
It's pretty simple to do this within your data template, if you create local data templates and use a ContentPresenter. This template presents objects of type MyObject, displaying an image whose source is determined by the type of the A property next to a TextBlock that displays the content of the Text property:
<DataTemplate DataType="{x:Type MyObject}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type Thing1}">
<Image Source="thing1.png"/>
</DataTemplate>
<DataTemplate DataType="{x:Type Thing2}">
<Image Source="thing2.png"/>
</DataTemplate>
</StackPanel.Resources>
<ContentPresenter Content="{Binding A}"/>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
If you want to use styles to do this instead, you're going to run into a problem, because data triggers want to look at property values, and the type of the A property is not, itself, exposed as a property.
Unless, of course, you implement one:
public Type AType { get { return A.GetType(); } }
(You'll also need to raise PropertyChanged for AType when the value of A changes.) Once you've done this, you should be able to implement a data trigger in a style, e.g.:
<Style TargetType="Image">
<Setter Property="Source" Value="default.png"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AType}" Value="{x:Type Thing1}">
<Setter Property="Source" Value="thing1.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding AType}" Value="{x:Type Thing2}">
<Setter Property="Source" Value="thing2.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
I think You can do that with triggers.
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding TheProperty}" Value="TheValue">
<Setter Property="Source" Value="NewPath"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
DataTemplateSelector doesn't seem to be a good choice here since you have the same template for all values of A.
Use DataTriggers:
<DataTemplate>
<StackPanel>
<Image x:Name="image" />
<TextBlock>Your text</TextBlock>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=A}" Value="ValueToCheck1">
<DataTrigger.Setters>
<Setter Property="Source" Value="Image1.png" TargetName="image" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding Path=A}" Value="ValueToCheck2">
<DataTrigger.Setters>
<Setter Property="Source" Value="Image2.png" TargetName="image" />
</DataTrigger.Setters>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Haven't tested it, but the idea is like that.

Setting ItemTemplate based on CheckBox value

I have a DataTemplate which contains a CheckBox and ListBox. When the CheckBox is checked, I want to change the ItemTemplate property on the ListBox to change the appearance of each item.
Right now, it looks like this:
<DataTemplate DataType={x:Type MyViewModel}>
<DockPanel>
<CheckBox DockPanel.Dock="Bottom"
Content="Show Details"
HorizontalAlignment="Right"
IsChecked="{Binding ShowDetails}"
Margin="0 5 10 5" />
<ListBox ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource SimpleItemTemplate}"
Margin="10 0 10 5">
<ListBox.Triggers>
<DataTrigger Binding="{Binding ShowDetails}" Value="True">
<Setter Property="ItemTemplate"
Value="{StaticResource DetailedItemTemplate}" />
</DataTrigger>
</ListBox.Triggers>
</ListBox>
</DockPanel>
</DataTemplate>
However, when I try to compile, I get the following error messages:
Value 'ItemTemplate' cannot be assigned to property 'Property'. Invalid PropertyDescriptor value.
and
Cannot find the static member 'ItemTemplateProperty' on the type 'ContentPresenter'.
I'm still fairly new to WPF, so perhaps there is something I'm not quite understanding?
You need to do this through the ListBox Style rather than directly through its Triggers collection. A FrameworkElement's Triggers collection can only contain EventTriggers (so I'm surprised your sample got as far as complaining about the properties!). Here's what you need to do:
<ListBox ItemsSource="{Binding Items}">
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="ItemTemplate" Value="{StaticResource SimpleItemTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ShowDetails}" Value="True">
<Setter Property="ItemTemplate"
Value="{StaticResource DetailedItemTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>

Categories

Resources