WPF: Bind to element in DataTemplate in ResourceDictionary - wpf

I'd like to bind to a element's property (a ListBox's SelectedItems.Count in my specific case) that's dynamically inserted into my window from a DataTemplate located in a ResourceDictionary. I'd like to enable/disable a button when the count reaches a certain number of ListBoxItems are selected. I thought this would work:
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource myResourceKey}, Path=myListBox.SelectedItems.Count}" Value="25">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
But I'm getting the following error:
System.Windows.Data Error: 40 : BindingExpression path error: 'myListBox' property not found on 'object' ''DataTemplate' (HashCode=50217655)'. BindingExpression:Path=aoiListBox.SelectedItems.Count; DataItem='DataTemplate' (HashCode=50217655); target element is 'Button' (Name='myBtn'); target property is 'NoTarget' (type 'Object')
How can I achieve this binding? Thanks in advance.

Well you can write a workaround, but I strongly recomment not to implement it that way. Consider, that a style in a ResourceDictionary is an empty resource, which should be decoupled from any specific instance (in your case myListBox) in your application. Problem is, that you cannot use this malformed style on another Button. So you don't need to, better you shouldn't, declare it as resource.
I definitely recomment to declare this Style directly in the Button. E.g.
<ListBox x:Name="myListBox" />
<Button>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListBox,
Path=SelectedItems.Count}" Value="25">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Additionally, I would use a Binding via the ElementName property.

Related

Binding to attached property of property in style

Before I begin, my end goal here is to have a default ToolTip style for all controls which has a normal border that goes red when there's a validation error on the control for which the ToolTip is being shown.
So ToolTip has a PlacementTarget property which is the control over which the ToolTip is being shown. There is a Validation.HasError attached property that gets set on that PlacementTarget control when it has a databinding error. For instance I've got a TextBox, and in the TextBox style I can do this:
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Content="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.(Validation.Errors)[0].ErrorContent}"/>
Now, I can just make a style for the ToolTip with a red border and set it in that trigger above, but I'd rather not have to do this in every control's style triggers, so I want my ToolTip style to handle this functionality itself if possible.
I've tried various things, most recently this (the converter basically returns red if there's an error, black if not):
<Style TargetType="{x:Type ToolTip}">
<Setter Property="BorderBrush" Value="{Binding Path=PlacementTarget.Validation.HasError, RelativeSource={RelativeSource Self}, Converter={StaticResource ToolTipBorderConverter}}"/>
</Style>
But from the error that gives it looks like that's trying to get an actual property called Validation on the TextBox, as opposed to the attached property:
System.Windows.Data Error: 40 : BindingExpression path error: 'Validation' property not found on 'object' ''TextBox' (Name='')'. BindingExpression:Path=PlacementTarget.Validation.HasError; DataItem='ToolTip' (Name=''); target element is 'ToolTip' (Name=''); target property is 'BorderBrush' (type 'Brush')
I also tried something like:
<Style TargetType="{x:Type ToolTip}">
<Style.Triggers>
<Trigger Property="PlacementTarget.Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
But I get an error saying I can't nest the Trigger's property like that.
Is there a way to accomplish what I'm trying to do here?
Searched for an answer for this for hours, and right after posting I saw a related article that got me the answer: you have to wrap Validation.HasError in parentheses for it to recognize it as an attached property.
<Style TargetType="{x:Type ToolTip}">
<Setter Property="BorderBrush" Value="{Binding Path=PlacementTarget.(Validation.HasError), RelativeSource={RelativeSource Self}, Converter={StaticResource ToolTipBorderConverter}}"/>

DataTrigger Binding to a ToggleButton within another namespace

I try to access a ToggleButton that is within a separate UserControl to Trigger a DockPanel.Style DataTrigger.
Here is how I made it work when both, the ToggleButton and the DockPanel, are in the same namespace:
<ToggleButton x:Name="OneToggleButton"
Content="Click me..." />
<DockPanel>
<DockPanel.Style>
<Style>
<Setter Property="UIElement.Visibility"
Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,
ElementName=DetailsBookToggleButton}"
Value="False">
<Setter Property="UIElement.Visibility"
Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<TextBlock DockPanel.Dock="Top" Text="..." />
</DockPanel>
But now when I move the ToggleButton into an other file (other namespace) it doesn't work anymore. ElementName (as I understand it) only works for elements within the same file.
So how can I manage a Binding to the IsChecked of my ToggleButton in another file?
Anybody have a suggestion? Would be great :)
FYI, the term you are looking for is "name scope", and there is no way to reference an element defined in another name scope. Arguably, you should not be allowed to do this.
Rather than binding one UI element to another, consider binding them both to a common property, either in your view model, on some common ancestor element, or via dependency property inheritance.

wpf datatrigger + value binding

I have a custom component that should change your alignment as a property of the window changes and getting the value of another property of itself. The property of the window is changing correctly, however, the alignment of the component is not changing. The converter that returns the alignment is also working correctly (it returns Left, Right or Center, depending on the other property of the component). So where is the error? Here is the code of the DataTrigger:
<myComponent.Resources>
<lib:HorizontalAlignmentConverter x:Key="HorizontalAlignmentConverter"/>
<Style TargetType="{x:Type myComponent}" x:Key="HorizontalAligner">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MainWindow1, Path=myWindowResizedProperty}" Value="True">
<Setter Property="HorizontalAlignment" Value="{Binding Path=myOtherProperty, RelativeSource={RelativeSource Self}, Converter={StaticResource HorizontalAlignmentConverter}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</myComponent.Resources>
Thanks in advance!
Another question: Is there any way I can reference, in the expression of DataTrigger Binding, the Window object, without using his name? This way would be more generic.
Thanks again!

Change Dependency Property value on Textbox ValidationRule

I have implemented Custom Control inheriting from ContentControl,which has dependency property called "CanNavigate"(bool).
In a Window.xaml,I have a text box with some ValidationRule checking for Textbox emptiness.I want to set "CanNavigate" to true/false based on TextBox.Validation.HasError as shown below code:
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="{Binding CanNavigate}" Value="false"></Setter>
</Trigger>
</Style.Triggers>
</Style>
Having this code,gives an error "'Property' property cannot use markup extensions or property element syntax on Setter. Only the Value property can use markup extensions or property element syntax. Error at object 'System.Windows.Setter' in markup file"
Is there a way where I can set CanNavigate property based on TextBox.validationError.
Regards,
Patil
Concerning your first setter I think you should replace this setter with a Binding on the ToolTip property and use a ValueConverter when neccessary, triggers can not.
It's better to do it like this (note I haven't tested this piece of code I'm just trying to point to this way):
<TextBox ... ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
Concerning the second setter: In a setter when defining the Property you don't need to specify a Binding you just provide the property name just like this:
<Setter Property="CanNavigate" Value="False" />

WPF ComboBox - showing something different when no items are bound

I have a ComboBox, and i want to change its look when the ItemsSource property is null. When it is in that state, i want to show a TextPanel with the text "Retrieving data" in it, and give it a look kind of similar to the watermarked textbox.
I figure to do this i need a ControlTemplate, and a trigger. I have the ControlTemplate here:
<ControlTemplate x:Key="LoadingComboTemplate" TargetType="{x:Type ComboBox}">
<Grid>
<TextBlock x:Name="textBlock" Opacity="0.345" Text="Retrieving data..." Visibility="Hidden" />
</Grid>
<!--
<ControlTemplate.Triggers>
<Trigger Property="ComboBox.ItemsSource" Value="0">
<Setter Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
-->
</ControlTemplate>
but my issue is how do i set up the trigger to show this when the ItemsSource property is null? I have tried a couple of different ways, and each way has given me the error message "Value 'ItemsSource' cannot be assigned to property 'Property'. Invalid PropertyDescriptor value.". My ComboBox xaml is this (including the attempted trigger):
<ComboBox Margin="112,35,80,0"
Name="MyComboBox"
Height="22.723"
VerticalAlignment="Top"
DisplayMemberPath="FriendlyName"
SelectedValuePath="Path"
TabIndex="160"
>
<Trigger>
<Condition Property="ItemsSource" Value="0" />
<Setter Property="Template" Value="{StaticResource LoadingComboTemplate}" />
</Trigger>
</ComboBox>
now should the trigger go on the ComboBox, or on the ControlTemplate? How do i access the ComboBox's ItemsSource property? Should i even be using a trigger?
Thanks!
Try putting {x:Null} for the value of the condition instead of 0.
Also I got it working by moving the Trigger to a style and modifing it slightly, see below.
<Style TargetType="ComboBox" x:Key="LoadingComboStyle">
<Style.Triggers>
<Trigger Property="ItemsSource" Value="{x:Null}">
<Setter Property="Template" Value="{StaticResource LoadingComboTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
<ComboBox Style="{StaticResource LoadingComboStyle}" .... >
The reason it only works in a style, is that only EventTriggers are allowed in the triggers collection directly on the Framework Element. For property triggers (like above) you need to use a style (I learn something every day).
See FrameworkElement.Triggers
Note that the collection of triggers established on an element only supports EventTrigger, not property triggers (Trigger). If you require property triggers, you must place these within a style or template and then assign that style or template to the element either directly through the Style property, or indirectly through an implicit style reference.

Resources