DataTrigger Overflow exception - wpf

How can this be an overflow exception...?
<DataTemplate x:Key="ElementTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource TypeConv}}" Value="{x:Type models:GroupModel}">
<Setter Property="Margin" Value="5 0 0 0"></Setter>
<Setter Property="DataContext" Value="{Binding Model}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="{Binding Name}"></TextBlock>
...
<StackPanel/>
<DataTemplate/>
For the reason: this is a template (with at least 25 UI contros) that normally needs Model A as datatype. the DataTemplate is a ListvVew ItemTemplate. But the datatype can be of type Model B. Model B has a property called 'Model', which is of type Model A.
So instead of copy pasting the whole block template and use style triggers or DataTemplate selectors, I just want to change the DataContext (from "{Binding}" to "{Binding Model}")
anyone has some suggestions, a solution?
Thx!
EDIT: the Converter returns the type of the incoming value (the data object itself). that way i can know when Model B is using the template and so to change the DataContext.

A work-around I would suggest to avoid possible recursion between setting the data context and triggering the DataTrigger:
Have both ModelA and ModelB implement a common interface called IListViewModel for example with a single property getter:
public interface IListViewModel
{
ModelA Model {get;}
}
Then, ModelA's implementation will return this, while ModelB's implementation returns this.ModelA The DataTemplate simply binds to the .Modelof whichever view model it's given.

Related

How to apply convention-over-configuration with Caliburn.Micro instead of these DataTriggers?

I have a View/ViewModel pair properly working with Caliburn.Micro.
Inside the view, there is a ContentControl whose content is bound to the same viewmodel, and depending on the value of a given enumerated property in the viewmodel, I want a different template for the ContentControl:
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate x:Key="TurnedOffView">
<local:TurnedOffView/>
</DataTemplate>
<DataTemplate x:Key="DeviceReadyView">
<local:DeviceReadyView/>
</DataTemplate>
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource TurnedOffView}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="{x:Static local:DeviceStates.Ready}">
<Setter Property="ContentTemplate" Value="{StaticResource DeviceReadyView}"/>
</DataTrigger>
<!-- More DataTriggers here, one for each state -->
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
I know Caliburn.Micro can use cal:View.Model and cal:View.Context so that multiple views can be bound to each ViewModel, but I can't figure out how to use this to avoid all this verbosity.
So the question is:
How should I use View.Model and View.Context (and rename my views) in this scenario to take advantage of Caliburn.Micro convention-over-configuration approach?
<ContentControl cm:View.Model="{Binding}" cm:View.Context="{Binding ContextProp, Mode=TwoWay}" /> might be something to try... Then your context would be based off code in your viewmodel not having to structure datatemplates with view-first code. ContextProp would be basically a sub view of your view in question (based on namespace Project.Views.MainView.Main.TurnedOff) ContextProp = "TurnedOff";
yea sorry.. Its how I see the folder structure (namespaces)... So instead of TurnedOffView it could be TurnedOff.xaml under a folder named Main. This was assuming your primary view is MainView.xaml and your primary Viewmodel is MainViewModel.cs. SOrry for the confusion.
View.Model can be setup to do multiple viewmodels, at that rate you might want to think about Conductor in the framework. But I don't think it would be warranted since you are basically doing view switching in the presented case above.

Xaml - send viewmodel property to method in another viewmodel

I have a datagrid bound to a collection of ViewModels that have a property called Distance. Is there a way in xaml to send that Distance property to a method in the ViewModel that the datagrid itemsource is on?
For example: (GetDistanceInKM would be on the same VM as the collection of reports)
<DataGrid ItemsSource="{Binding ReportViewModels}">
<DataGrid.Columns>
<DataGridTextColum Binding="{Binding Distance}" Header="Distance" EditingElementStyle="{StaticResource DistanceStyle}"/>
</DataGrid.Columns>
</DataGrid>
<Style x:Key="DistanceStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding={Binding GetDistanceInKM[Distance], Converter={StaticResource IsDistanceGreaterThanTen}} Value="True">
<Setter Property="BorderBrush" Value={StaticResource HighlightBorderBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
You cannot bind to methods, only to properties. If you want to call a method when a property changes, do it in the setter of that property.
If i understood correctly you have two options:
When Distance is set, call GetDistanceInKM and modify a new property DistanceInKM. Then bind your DataTrigger to DistanceInKM using the converter as is now.
Bind your DataTrigger directly to Distance property and make the conversion to kms in a IsDistanceGreaterThanTenKMs Converter.

Can't change content-template dynamically

I have a ContentControl that I need to set it's ContentTemplate dynamically.
so I decided to write 2 DataTemplates, and then style my ContentControl such that a trigger fires and set the proper template (dt1/dt2) when a Boolean dependency property in my view-model changes (true/false).
But the problem is if the Boolean property is primarily set to true, the data template will always be dt1 and changing the property to false wont change the template to dt2.
since the data triggers are bound to the Boolean dependency property, shouldn't changing the property result in firing the triggers?
notes:
There is a button in MyView which changes BooleanDependencyProp on it's
click event.
MyViewModel inherits from an interface that
implements INotifyPropertyChanged.
Xaml:
<UserControl x:Class="Views.MyView">
...
<StackPanel>
<ContentControl Content="{Binding RelativeSource={RelativeSource AncestorType=MyView}, Path=MyViewModel}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding BooleanDependencyProp}" Value="true">
<Setter Property="ContentTemplate">
<Setter.Value>
<dt1 ... />
</Setter.Value>
</Setter>
<DataTrigger Binding="{Binding BooleanDependencyProp}" Value="false">
<Setter Property="ContentTemplate">
<Setter.Value>
<dt2 ... />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</StackPanel>
This is a known shortcoming of the WPF framework, if you want to apply different data templates, consider either using the visual state manager to change the presentation, or swap out the content data template for a user control that changes based on the triggers instead, you'll get more mileage.
There's a lot more I could say, but it would involve knowing your scenario and the differences in these DataTemplates, why you are disambiguating, etc. Also, MVVM all around? or straight ahead Code+Markup style with a few view models?

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.

WPF binding based on comparison

There is a relatively simple thing I'm trying to achieve but I'm unsure how to do it. Basically, I have a CLR class as follows:
class SomeClass
{
public SomeEnum Status;
}
public enum SomeEnum { One, Two, Three };
I've got a DataGrid that I'm binding an ObservableCollection<SomeClass> programmatically through the code-behind. In this DataGrid I have a DataGridTemplateColumn containing two buttons, as follows:
<toolkit:DataGridTemplateColumn Header="Actions">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="ActionOne" />
<Button Content="ActionTwo" />
</StackPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
What I want to do is bind the IsEnabled property of these buttons to a comparison based on the value of {Binding Path=Status}. For example, in pseudocode:
ActionOne.IsEnabled = BoundValue.Status != SomeEnum.Two
ActionTwo.IsEnabled = BoundValue.Status == SomeEnum.One || BoundValue.Status == SomeEnum.Two
Is there anyway to do this in XAML? The alternative would be just to write a value converter for each button, but since the content and other details of the button may vary, too, I don't want to end up writing like 6 value converters.
Cheers!
Why not expose additional Properties in SomeClass that performs the comparison logic?
ex:
public bool ActionOneEnabled
{
get { return Status != SomeEnum.Two; }
}
Then you can easily bind the Button's IsEnabled to the appropriate Property.
Don't forget to include an OnPropertyChanged("ActionOneEnabled") in your setter for Status - so that when your Status changes your Properties based on Status are re-evaluated.
You could do this using a DataTrigger in conjunction with a Converter like below. However, Bryan's solution has the benefit of not using multiple converters and it looks like that was one of your concerns so his answer might be better for your scenario.
<Button>
....
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Status, Converter={StaticResource yourConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
Another option would be to not use the DataTrigger and add the binding directly in the IsEnabled property:
<Button
IsEnabled="{Binding Path=Status, Converter={StaticResource yourConverter}}"
...
/>

Resources