WPF problems with trigger syntax - wpf

I can't get the following to work. The goal is to change the ZIndex of the usercontrol
when the mouse is over its content.
Using a simple property like "Background" instead of ZIndex does not work either. The compiler complains about "Value 'Grid.IsMouseOver' cannot be assigned to property 'Property'. Object reference not set to an instance of an object." (After compiling and starting the project).
Can someone please provide a working example of a trigger which changes some properties of a different control?
<UserControl x:Class="ImageToolWPF.Controls.sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Triggers>
<Trigger SourceName="viewPort" Property="Grid.IsMouseOver" Value="True">
<Setter TargetName="me" Property="UserControl.Panel.ZIndex" Value="2" />
</Trigger>
</UserControl.Triggers>
<Border Name="border" CornerRadius="3,3,3,3" BorderThickness="3" BorderBrush="Green">
<Grid Name="viewPort">
<Label Name="labelTop" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="16" Background="#a0ffffff" Padding="4"/>
</Grid>
</Border>
</UserControl>

There are a number of issues here:
FrameworkElement.Triggers can contain only EventTriggers, not general triggers. (See the Remarks in the MSDN docs.) You're going to need to move your trigger into a Style instead.
In your Setter, you've specified a TargetName of "me", but there doesn't appear to be any element with that name. I think you mean for the setter to affect the UserControl itself. In that case, if you move the Trigger into a Style on the UserControl, you can just omit the TargetName altogether: setters in a style automatically affect the styled element.
In your Setter, you've specified a Property of UserControl.Panel. That means you are expecting the target (the thing called "me") to have a property called UserControl, and that to have a property called Panel. I think what you are looking for is "(Panel.ZIndex)" - note the brackets showing that this is an attached property name rather than a multipart path, and that there is no UserControl prefix.

Related

Clarification about WPF Styles

In the below code, why might the first example fail to set the background to Blue, but, the second example work as one might expect they both would – that is, is sets the background to Blue? Interestingly, when the style is applied in the second example, even though the BorderThickness is not specified in the Style, the property value of "3" also gets picked up, presumably because the new Style does not set it at all.
Code 1:
<GroupBox Margin="4,12,4,4"
Grid.ColumnSpan="4"
Grid.Column="0"
Grid.Row="3"
Header="{x:Static res:UIResources.DepreciationText}"
BorderBrush="{DynamicResource MainControlBorderBrush}"
BorderThickness="3"
Background="Blue" />
Code 2:
<GroupBox Margin="4,12,4,4"
Grid.ColumnSpan="4"
Grid.Column="0"
Grid.Row="3"
Header="{x:Static res:UIResources.DepreciationText}"
BorderBrush="{DynamicResource MainControlBorderBrush}"
BorderThickness="3">
<GroupBox.Style>
<Style TargetType="GroupBox">
<Setter Property="Background"
Value="Blue" />
</Style>
</GroupBox.Style>
</GroupBox>
You could reason from the above observation that the following are true:
1) Some Style is getting applied to the GroupBox further up the tree – perhaps even to some base class of GroupBox, such as Control, since a search for a Style targeting GroupBox was not found.
2) A property set on a control instance will not override the same property set in a Style targeting the control.
3) There is not a way to augment an inherited Style, other than using the BasedOn property. Using the BaseOn property implies you must know the Key of the Style you would like to base it on, unless, if you want to use BasedOn with a Style applied to a Type, you could somehow specify that – perhaps using the Type name in BasedOn?
Can anyone confirm or correct the above assertions, and whether they correctly explain the observed result?
Are you using any sort of theme pack for your application that restyles controls? My guess would be that you have a style somewhere overriding the control template in such a way that the Background property is completely ignored. If the control template doesn't contain a {TemplateBinding Background}, the Background property does nothing.
You can definitely override properties by setting them explicitly, even if they are also set in a style.
By setting the style yourself without using a BasedOn, it implicitly uses the default control template for the GroupBox, rather than resolving to a style imported with your resources. If you wanted to use the imported resource style, you could do this:
<Style TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="Background" Value="Blue" />
</Style>
I suspect this would give you the same result as in the first case, as now you would be inheriting the offending control template that ignores your Background value.

The attachable property 'IsEnabled' was not found in type 'TextBox'

From my understanding of attached properties, I believe that I can set a property value that will apply to all of the children of a container that match the type. For instance, if I have a number of TextBoxes in a StackPanel, then I can disable them all by setting the TextBox.IsEnabled property to false in the StackPanel's declaration:
<StackPanel TextBox.IsEnabled="False" Orientation="Horizontal">
...
</StackPanel>
I tried this in Visual Studio, and the Xaml designer greyed-out the TextBoxes in the StackPanel exactly as expected, but when I tried to compile, I ran into the error:
The attachable property 'IsEnabled' was not found in type 'TextBox'
Have I misunderstood attached properties? Do they only go from the ancestor to the child? If so, is there a way to do what I am trying, ie, to set all child TextBoxes IsEnabled property to false?
Thanks for any pointers
Yes, attached properties allow to set a property value on the parent and the children inherit that value. On the other hand TextBox.IsEnabled is not an attached property and so you cannot do what you want.
Perhaps it is possible to get what you want with some custom panels and/or custom attached properties programming.
However you could also get the same result using a Style where you can also bind the IsEnabled property to your custom logic if you need.
<StackPanel Width="200" Height="50" >
<StackPanel.Resources>
<Style TargetType="TextBox">
<Setter Property="IsEnabled" Value="False" />
</Style>
</StackPanel.Resources>
<TextBox Text="one" />
<TextBox Text="two" />
</StackPanel>

WPF ComboBox: How to you utilise a generic ItemContainerStyle with binding

I want to utilise a generic style for my ComboBoxItem content and have the text content bound to different properties on my underlying class. So this is the best I can come up with but the bindings are hard coded. So for every class bound to a combobox using this ItemContainerStyle I'd have to implement a "MainText" and "SubText" property.
Question is, is there a way to have the binding soft coded so where the style referenced from a combobox I can specify which string properties of the underlying class are used.
<Style TargetType="{x:Type ComboBoxItem}" x:Key="ComboBoxItemStyleA1">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="BB" Padding="8,3,8,3" Background="DarkGreen">
<StackPanel Margin="0">
<TextBlock Foreground="White" FontSize="16" Text="{Binding MainText}"/>
<TextBlock Foreground="White" FontSize="8" Text="{Binding SubText}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="BB" Value="#FF256294"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And to use the style...
<ComboBox ItemsSource="{Binding Items}"
ItemContainerStyle="{StaticResource ComboBoxItemStyleA1}" />
Further to dowhilefor's answer (many many thanks - WPF is great but sure is a voyage of discovery)
I used a data template to define the cell look originally - and then wanted to use a comboboxitem based style with a control template defined where I could specify the onmouseover triggers. i.e. these were to change the background color etc.
Butj
a) I couldn't remove the Border section of the template above - the triggers are tied to it by targettype="BB". so I kind of wanted to get the trigger bound to the container such that the datatemplate would pick up the background from the template binding but not sure how to get this plumbed in.
b) I realised that even if I comment out the BB specific bindings on the triggers just to get it to run - the combobox doesn't find and use the DataTemplate I defined. Seems that defining the controltemplate in my comboboxitemstyle stops it picking up the datatemplate.
I hope I make sense here - bottom line is I just want a style that I can apply with triggers in that set the background color of my cobobox item. It should not know what the data is - i.e. be able to plug in a datatemplate that will (template ?) bind to this background color.
Many thanks for the very fast response.
btw I'm using ItemContainerStyle in conjuction with ItemTemplate so I can have a different representation in the dropdown to what appears in the combobox list
First of all don't use the ItemContainerStyle for that. To be more precise never have any Bindings to the datacontext inside an ItemContainerStyle, at least try not. Why? The Style is used for defining the appearance of a combobox item disregarding the content. If you want to define how the content should look like, you use a DataTemplate for that. There are multiple ways to tell the combobox where he can find a proper DataTemplate for the Data you supply. Checkout the property ItemTemplate, ItemTemplateSelector and search for implicit styles, to find out more about them.
So to your problem, create one ItemContainerStyle for you combobox (if you really have to anymore) which doesn't care about the object that will be put into. Now you still need to provide multiple DataTemplates each and everyone with the knowledge of the data object that you want to be templated. There is no way around it, there is no soft databinding. Just try to keep your templates small and simple. If for some reason you need the exact same template, but your properties are just named differently, why not use a wrapper item for the DataContext with the properties Caption, Description and you can decide in code how these properties are filled with your real data wrapped into this object.

Items in ObservableCollection not updating the view

I'm stumped here. I have an observable collection that holds business objects. I have it bound to ItemsSource of a ListBox. I am updating the X and Y of my object and it is being displayed correctly in the UI during runtime as it is bound the the Item top and Left. But, here is where the problem is. I have also bound some data to be displayed in textblock text property and the data only displays the initial value. It never updates the textblock Text no matter how many times I change it.
Here is the XAML. If you see a problem with the XAML please let me know. Like I said, the X/Y - Top/Left binding works just fine and updates when changed, the TextBlock that is bound to DisplayData does not.
Also, my business object in my collection does Implement INotifyPropertyChanged.
I will try to make a small demo to replicate this if an answer can not be given just by looking at the XAML.
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="614" Width="674">
<ListBox Name="PlottingBox" Background="White">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayData}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Canvas IsItemsHost="True" />
</Border>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding Path=PlotX}" />
<Setter Property="Canvas.Top" Value="{Binding Path=PlotY}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I think the problem is in the code behind. Your XAML is absolutely legal and looks good. But before you post the source code make sure the following conditions are true:
Your business object implements INotifyPropertyChanged interface and you raise PropertyChanged event every time DisplayData value is changed.
There are no typos. Neither in DipslayData property definition nor in the PropertyChangedEventArgs, where you pass "DispayData" property name.
DataContext of a ListBoxItem is of your business object's type. Check it with Snoop.
There are no binding errors in runtime. Run your application in debug and check your Output window. You can also check this with Snoop.
Hope after completing this check list you'll have the answer.
Cheers :)
Your business objects need to implement the INotifyPropertyChanged interface, so that the UI is notified of the change and can update to reflect the new value
Uhm, the DataContext inside the ListBox is an item of the ItemsSource list.
for example, if you ListBox is binded to a ObservableCollection, the DataContext inside the ListBox will be a Person object and not the parent's datacontext.
If you have a TextBlock with a binding, the binding will point to a Person Object, in other words, personInstance.DisplayData and not parentDC.DisplayData.
I don't know the behavior without a ItemsSource.
Maybe you know this, but maybe this helps you.

How do I update a label that is in a ControlTemplate of a Toolbar in WPF?

I have a ControlTemplate that is made up of a ToolBarTray and a ToolBar. In my ToolBar, I have several buttons and then a label. I want to be able to update the label in my toolbar with something like "1 of 10"
My first thought is to programatically find the label and set it, but I'm reading that this should be done with Triggers. I am having a hard time understanding how to accomplish this. Any ideas?
<Style x:Key="DocViewerToolBarStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<ToolBarTray... />
<ToolBar.../>
<Button../>
<Button..>
<Label x:Name="myStatusLabel" .. />
The purpose of a ControlTemplate is to define the look of a control. For your problem, I'm not sure if a control template is the right solution.
As Bryan also points out, you should bind the Content property of the Label to a property that is already present in your control. This should be done via TemplateBinding.
<Label x:Name="myStatusLabel" Content={TemplateBinding MyStatusLabelProperty} ../>
The property MyStatusLabelProperty then has to exist at your control class.
Usually, you would create your own UserControl that has a dependency property of the correct type (either object or string) that is named MyStatusLabelProperty.
I would set the label to the "Content" attribute of your control e.g.
<Label x:Name="myStatusLabel" Content="{TemplateBinding Content}"/>
Then you can set your label's text with your top level object's Content attribute.
I would create a view model which implements INotifyPropertyChanged interface and use DataTemplate to display it using something like this:
<DataTemplate DataType={x:Type viewmodel:MyToolBarViewModel}>
<Label Content={Binding CurrentPage} />
<Label Content={Binding TotalPages} ContentStringFormat="{}of {0}" />
</DataTemplate>
<ToolBar>
<ContentPresenter Content={Binding <PathtoViewModel>} />
</ToolBar>
With using bindings you don't have to explicitly update label's content. All you have to do is set property's value in view model and raise proper PropertyChanged event which causes the label to update its content.

Resources