WPF trigger can't change a property in mvvm - wpf

I wanted to make it happened to change property in ViewModel using Trigger tag in xaml.
The code I made was like this.
<Grid x:Name="LogoGrid">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Height" Value="{Binding LogoHeight, Mode=TwoWay}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualHeight, Converter={StaticResource ImageHeight}}" Value="False">
<Setter Property="Height" Value="150"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Image Source="/Resources/Logo/Logo.png" Style="{StaticResource GameWindowLogoStyle}"/>
</Grid>
I bound data to LogoHeight property in ViewModel and I wanted to set this property to 150 when parents grid is big enough to show Logo Image.
It works fine in the UI. but it didn't change LogoHeight property.
It is the same things keep happening when I use animation with Storyboard tag.
Is it normal things that propery can't be changed by Trigger tag or animation in the Storyboard tag.
Please tell me the way if there is a way to change property in Trigger tag or Storyboard tag.
Thank you.

A Setter in a Style cannot set the property of a view model. It can only set a property of the element to which the Style is being applied, i.e. the Grid in this case.
You may be able to work around this by using a Storyboard that animates the source property:
Setting a ViewModel boolean property inside a storyboard

Related

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.

Adding tool tip functionality to a wpf control that does not descend from FrameworkElement

I'm working on a custom class that descends from DataGridColumn. The base class for DataGridColumn is DependencyObject. As such, it does not have a Tooltip property.
I want my custom class to have a Tooltip property. Actually, I want it to also have a ToolTipTemplate property that is a DataTemplate that can be used to generate the ToolTip. How do I go about adding this functionality to my class?
Tony
Its a common misconception that DataGridColumn being a dependency object, is part of the visual tree. It is not. So even if we create an inheritable dependency property (just like DataContext or FlowDirection which automatically propagates down the visual parent to its child elements), the new property of ToolTip wont descend down to individual cells, as those cells are not the children of the data grid column.
So now that we know this, the only way left is to add a binding in the CellStyle and bind to the self Column.ToolTip property. Just because you have decided to go with ToolTipTemplate, then you could add a ContentControl and then bind to its content template.
Something like this...
<tk:DataGrid x:Name="MyDataGrid" RowHeaderWidth="15"
ItemsSource="{StaticResource MyData}"
AutoGenerateColumns="False">
<tk:DataGrid.CellStyle>
<Style TargetType="{x:Type tk:DataGridCell}">
<Setter Property="ToolTip">
<Setter.Value>
<ContentControl
ContentTemplate="{Binding Column.ToolTipTemplate,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type tk:DataGridCell}}}"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger
Binding="{Binding Column.ToolTipTemplate,
RelativeSource={RelativeSource Self}}"
Value="{x:Null}">
<Setter Property="ToolTip" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</tk:DataGrid.CellStyle>
....
</tk:DataGrid>

How Biniding Mode(TwoWay) works?

I was trying out some dummy application just to test binding modes. So, just curious to know how did the binding modes work. I have this xaml code-
<Button x:Name="btn"
Height="20"
Width="200"
VerticalAlignment="Top">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled"
Value="{Binding CanEnable, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding TextChanged}" Value="true">
<Setter Property="IsEnabled"
Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Here by button IsEanbled is binded to my viemodel property "CanEanble" whose default value is false. Now in my trigger i was listening to "TextChanged" property and setting button IsEnabled to true. Button gets enabled as it should be but the property "CanEnable" did not set to true even the biding mode is set to TwoWay..
Why this is happening??
By setting the value in the trigger you basically remove the binding you previously set in the style setter. Take a closer look at the style. You will notice that you basically you set the property IsEnabled twice. First in the style setter, second in the trigger. It is logical that the second value overrides the previous value.
The desired effect can be achieved from code if you set the value of the dependency property using SetCurrentValue method:
SetCurrentValue(Button.IsEnabledProperty, true);
This way the bindings set on this property will not be removed and it will work as expected.

FrameworkElement`s DataContext Property does NOT inherit down the element tree

Hello WPF Pros at least I hope some of you read this!
DataContext is a property on FrameworkElement (base class for all WPF Controls) and is implemented as a DependencyProperty. That means all the descendant elements in the logical tree share the same DataContext.
So the ContentControl should do it with its descendant elements right?
I have a scenario where that is NOT the case and I would like to know WHAT is the cause of that misbehaviour ?!
That you understand a bit more about it please read this thread ( dont NOT want to copy everything here) where the trouble starts...:
WPF: Can not find the Trigger target 'cc'. The target must appear before any Setters, Triggers
and to say it in short words: My DataTemplates within the ContentControl do have a dead DataContext that means there is NOTHING to bind to it, what is actually not possible...
Every Element down the ContentControl has NOTHING set in the DataContext Property ???
DataContext is a property on
FrameworkElement (base class for all
WPF Controls) and is implemented as a
DependencyProperty. That means all the
descendant elements in the logical
tree share the same DataContext.
The fact that it's a dependency property doesn't imply inheritance... It's true for DataContext, but only because the dependency property has the FrameworkPropertyMetadataOptions.Inherits flag in its metadata.
So the ContentControl should do it
with its descendant elements right?
ContentControl is a bit special: the DataContext of its descendants (the visual tree built from the DataTemplate) is actually be the Content of the ContentControl. So if your ContentControl has no content, the DataContext inside it is null.
This worked for me:
<ContentControl ContentTemplate="{StaticResource NotesTemplate}"
Content="{Binding}"
DataContext="{Binding HeightField}"/>
Without the Content="{Binding}", the DataContext was NULL
The last answer (from VinceF) worked for me too.
I wanted to show a usercontrol depending on the value of a property in my viewmodel. So I made a ContentControl with some Style Triggers. Depending on the value of a bind property the trigger sets a specific ContentTemplate containing the specific usercontrol.
The usercontrol was shown right, but its DataContext was always null. So I had to set the Context of the ContentControl to: Content="{Binding}" After that, the UserControls worked fine and had the same DataContext as their parent.
So my XAML looks like that:
In the Resources part I defined two DataTemplates; each one for each UserControl I want to show.
<DataTemplate x:Key="ViewA">
<namespace:UserControlA/>
</DataTemplate>
<DataTemplate x:Key="ViewB">
<namespace:UserControlB/>
</DataTemplate>
The part where I show the UserControl depending on a property is the following:
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Property}" Value="0">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Property}" Value="1">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
after reading this question and previous answers, I prefer using ContentControl with data triggered Content like this:
Controls which will be set as Content of ContentControl:
<TextBox x:Key="ViewA">
...
</TextBox>
<ComboBox x:Key="ViewB">
...
</ComboBox>
ContentControl which switch own content by DataTrigger in ContentControl style:
<ContentControl>
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Property}" Value="0">
<Setter Property="Content" Value="{StaticResource ViewA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Property}" Value="1">
<Setter Property="Content" Value="{StaticResource ViewB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
I hope this helps to someone like previous answers to me.

Binding a Style DataTrigger to a custom ICommand's status property

I have a custom implementation of WPF's ICommand, that executes its bindings in a BackgroundWorker, so that the UI stays responsive.
I would like to display a custom effect while the bindings are being executed. For now, the effect is simplified to setting command source's IsEnabled property to False. Later, our designer will come up with some nice progress-style animation.
Here's an example of what I'm trying to do, that works (sort of):
<Button Width="80"
Command="{x:Static Member=local:AppCommands.Test}"
DataContext="{x:Static Member=local:AppCommands.Test}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsExecuting, Mode=OneWay}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
As evident, my ICommand contains an IsExecuting property, which is set to True while the BackgroundWorker does its thing with the bindings, consequently disabling the button in the UI.
Unfortunately, having to explicitly set the button's DataContext to command instance prevents me from referring to the original DataContext in cases like this one:
<Button Width="80"
Command="{x:Static Member=local:AppCommands.ParamTest}"
CommandParameter="{Binding Path=SelectedItem}"
DataContext="{x:Static Member=local:AppCommands.ParamTest}">
Here, the CommandParameter should bind to the window's DataContext (which is set to an instance of my view-model), but instead it binds to the command which knows nothing about SelectedItem.
There are probably more ways to solve this, and I'm interested to hear all of them. I myself have been thinking along the lines of using an attached property to replace the direct binding to command's IsExecute and with it the need for setting the DataContext, but I'm not yet sure how to do that.
Any advice is most welcome. Thank you!
For now, I've come up with this simple solution:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Command.IsExecuting, Mode=OneWay}" Value="True">
It does what I wanted so I'm happy. If anyone thinks there's a better way, I'm still interested, though.

Resources