here is my scenario: I have a control template for a custom control which is rather big. To encapsulate the code of one of the features of that custom control, I'd like to introduce a helper class (called Internals). This class has logic code and provides some properties, which should be used in Bindings within the ControlTemplate.
Therefore I need to create an instance of this class in XAML and bind the TemplatedParent to a dependency property of the Internals class. My problem now is a concrete binding to the object the ControlTemplate is applied on. I've created a little proof of concept:
MainWindow.xaml:
<Window x:Class="WpfProofOfConcept.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfProofOfConcept="clr-namespace:WpfProofOfConcept"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow"
Height="650"
Width="525">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.Resources>
<wpfProofOfConcept:Internals x:Key="InternalsKey"
Base="{Binding Path=., RelativeSource={RelativeSource TemplatedParent}}" />
</Grid.Resources>
<Grid.RenderTransform>
<RotateTransform Angle="20" />
</Grid.RenderTransform>
<!-- if uncommented, Binding to Base is working -->
<!--<wpfProofOfConcept:Internals Base="{Binding Path=., RelativeSource={RelativeSource TemplatedParent}}" />-->
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button>
<TextBlock>Some text</TextBlock>
</Button>
</Grid>
</Window>
Internals.cs:
public sealed class Internals : FrameworkElement
{
public static readonly DependencyProperty BaseProperty =
DependencyProperty.Register("Base", typeof(object), typeof(Internals), new PropertyMetadata((o, s) => { }));
public object Base
{
get { return (object)GetValue(BaseProperty); }
set { SetValue(BaseProperty, value); }
}
public Internals()
{
}
}
I need a reference to the concrete object the Template is applied on in my Internals object - thus the TemplatedParent binding. It does not work and I do not get any binding errors in the output.
The strange thing is that it works when the Internals object is created outside of the resources section (see commented lines in the XAML code). I do not
And one more thing thats keeps me confused: In Silverlight, binding to the TemplatedParent within a resource section works. This seems to be a WPF issue.
Do you have any ideas how to get this binding done? Or can you explain why TemplatedParent binding does not work in the resources section?
Thanks for all your hints!
It is possible to make Bindings from the Resources! But the object in resources has to be a freezable object, e.g. its class has to extend Freezable. For more information, have a look at this link from Dr. WPF http://drwpf.com/blog/2008/05/22/leveraging-freezables-to-provide-an-inheritance-context-for-bindings/:
In addition to the above scenario (where a freezable is set as a
dependency property value on a dependency object), it is this enhanced
notion of an inheritance tree and inheritance context that allows
bindings on brushes, animations, and other freezable objects to work
when those objects are placed within a resource dictionary.
You have to change the class implementation to:
public sealed class Internals : Freezable
Grüße,
Peter ;)
When you are creating Internals as Resource, it is not the part of the VisualTree and hence binding is not getting applied to it
Once your define it inside the Grid, it become the part of VisualTree.
Related
I have a binding error that makes no sense. It always works in Release Mode. It sometimes -- only sometimes -- fails to find the source via FindAncestor in Debug Mode
Its for Style for a WPF Path that I use only when that Path is inside a specific custom control named LayerView. Below is the style. Note the 3 bindings that look for the parent source object LayerView are the ones that sometimes fail
<Style x:Key="LayerViewGuidePathStyle" TargetType="{x:Type Path}">
<Setter Property="Data">
<Setter.Value>
<MultiBinding Converter="{StaticResource CvtGuideOption}">
<Binding Source="{svc:ViewSettings}, Path=GuideOption}" />
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:LayerView}}" Path="ScanWidth" Converter="{ctrl:LogValueConverter}"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:LayerView}}" Path="ScanHeight" Converter="{ctrl:LogValueConverter}"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:LayerView}}" Path="SceneTransform" Converter="{ctrl:LogValueConverter}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
When they fail in Debug mode, this is the warning
System.Windows.Data Warning: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='MyControls.LayerView', AncestorLevel='1''.
BindingExpression:Path=ScanHeight; DataItem=null; target element is
'Path' (Name='GuidePath'); target property is 'Data' (type 'Geometry')
Here is how I use this style inside my custom control LayerView
<ctrl:LayerView x:Name="MainLayerView" ItemsSource="{Binding Shapes}">
<ctrl:LayerView.Layers>
<Path x:Name="GuidePath" Style="{StaticResource LayerViewGuidePathStyle}" />
</ctrl:LayerView.Layers>
</ctrl:LayerView>
As you can probably tell, LayerView is an ItemsControl: Specifically a MultiSelector. My custom version merely adds non-dependency property named Layers which is a Collection<object>. The idea is that the user will set these statically in XAML, as I did above, and I will display them over the standard items of the control.
public class LayerView : MultiSelector
{
static LayerView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LayerView), new FrameworkPropertyMetadata(typeof(LayerView)));
}
private Collection<object>? _layers;
public Collection<object> Layers => _layers ??= new Collection<object>();
}
Finally, here is the ControlTemplate for LayerView. In addition to the required ItemsPresenter, I add an ItemsControl to show the contents of Layers as basically non-hit-detectable "overlays"
<ControlTemplate x:Key="LayerViewTemplate" TargetType="{x:Type gcl:LayerView}">
<Canvas x:Name="PART_MainCanvas" Background="Transparent">
<!-- We are an ItemsControl so present our items -->
<ItemsPresenter x:Name="PART_Items" />
<!-- Now present our custom layers. -->
<ItemsControl x:Name="PART_Layers"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Layers}"
IsHitTestVisible="False"/>
</Canvas>
</ControlTemplate
I have no moves here. I can usually fix bindings that fail, but I would expect consistent failure or success. Not a mix. And especially not intermittent failures It's almost as if it's a race condition.
Can anyone point me in the right direction as to why this would fail? Is my Layers property of the wrong type or is it missing some crucial attribute or something?
Without reproducing this error, of course, it is difficult to say exactly what the cause is. But I have some guesses.
The elements of the Layers collection are initialized BEFORE being added to the collection, but the binding is resolved after the element is loaded;
Layers is a regular CLR collection and information about loading ItemsControl (LayerView ) is not passed to it. Therefore, the elements of this collection, as I think, are initialized completely at once, including bindings. And this can happen before these items are included in the ItemsControl panel.
As I think, these errors also occur in Release, it's just that not all errors are caught in this mode. It is guaranteed that only errors that are critical for the operation of the application are caught. For example, exceptions.
At a minimum, you need to replace the collection with a FreezableCollection and implement a read-only DependecyProperty.
True, in this case, you can only include DependencyObject in it.
private static readonly DependencyPropertyKey LayersPropertyKey
= DependencyProperty.RegisterReadOnly(
nameof(Layers),
typeof(FreezableCollection<DependencyObject>),
typeof(LayerView),
new PropertyMetadata(null, (d, e) => ((LayerView)d).protectedLayers = (FreezableCollection<DependencyObject>)e.NewValue));
public static readonly DependencyProperty LayersProperty
= LayersPropertyKey.DependencyProperty;
public FreezableCollection<DependencyObject> Layers
{
get => (FreezableCollection<DependencyObject>)GetValue(LayersProperty);
}
// An optional auxiliary property for quickly obtaining a collection
// in the internal logic of the LayerView and its derivatives.
protected FreezableCollection<DependencyObject> protectedLayers {get; private set;}
// Initializing a Collection in a Constructor.
public LayerView() => SetValue(LayersPropertyKey, new FreezableCollection<DependencyObject>());
Supplement
Unfortunately, I could not immediately check your version - there was no "at hand" computer with the Studio.
Now I checked it.
I did not change the code of the LayerView element itself, I did not set the template in the style.
I got the Binding error only if the Element Template is not set.
<Window x:Class="Core2023.SO.Joe.BindingErrors.BindingErrorsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Core2023.SO.Joe.BindingErrors"
mc:Ignorable="d"
Title="BindingErrorsWindow" Height="150" Width="400">
<Window.Resources>
<ControlTemplate x:Key="LayerViewTemplate" TargetType="{x:Type local:LayerView}">
<ItemsControl x:Name="PART_Layers"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Layers}"/>
</ControlTemplate>
</Window.Resources>
<Grid>
<local:LayerView>
<local:LayerView.Layers>
<TextBlock Text="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType={x:Type local:LayerView}}}"/>
</local:LayerView.Layers>
</local:LayerView>
</Grid>
</Window>
In order to accurately diagnose the cause of your problem, I need more complete code to reproduce the problem.
What is the best way of using the same viewmodel to support multiple views?
(Yes..I have read "MVVM (with WPF)-Binding Multiple Views to the SameViewModel", but if I read it correctly, the answer involved creating different viewmodels which is not what I want to do here).
In the below code the StringViewModel supports the InkStringView when the InkStringView consists of defined rows. I wish now to define a second view that consists of columns but must keep the same datacontext of StringViewModel. In the second view, the controls have different positions and sizes that the StringViewModel calculates but thier function and purpose remain the same. It would be ideal if the module that creates the StringViewModels in Strings, an observablecollection, does not know what view will be used leaving the final decision to the xaml of the usercontrol.
My question is how to design either the StringViewModel and/or the DataTemplate to allow for different views and calculations based on that view by changing only the DataTemplate.
(I tried inheriting the StringViewModel to different viewmodels, each viewmodel specific to its view, but it did not work).
Thanks in advance for any help or suggestions. Or is their a better way?
Example:
<DataTemplate DataType="{x:Type vm:StringViewModel}">
<v:InkStringView_2 /> <----CHANGING THE VIEW TO COLUMNS. ViewModel needs
</DataTemplate> to perform calculations specific to the view.
The usercontrol is:
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:StringViewModel}">
<v:InkStringView />
</DataTemplate>
</UserControl.Resources>
<Grid>
<ItemsControl
ItemsSource="{Binding Strings}" >
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Margin" Value="{Binding Margin}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
</UserControl>
just a suggestion:
you have a viewmodel MyViewmodel which you wanna display with different DataTemplates. then for me a easy way would be to create "new" Classes with "no" implementation
public class MyT1 : MyViewmodel {}
public class MyT2 : MyViewmodel {}
public class MyT3 : MyViewmodel {}
so now all MyT1, MyT2, MyT3 have the same methods and stuff, but you can create a datatemplate foreach of them
<DataTemplate DataType="{x:Type vm:MyT1}">
<v:T1View />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MyT2}">
<v:T2View />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MyT3}">
<v:T3View />
</DataTemplate>
or you dont go the Viewmodel First approach and do View First and then you can choose the view you want with your Datacontext
I've had a similar problem, the way I've tackled it is by using ControlTemplate so:
In your View:
<Control>
<Control.Resources>
<Style TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<!-- Here you put you view it could be a UC if you want -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style
</Control.Resources>
</Control>
Then in your Style.Resources you can define Triggers or DataTriggers to change the Template property of your Control. This way you keep the ViewModel as it is and you just change the Views that are getting the data. Hope this makes sense, if anything give us a shout and I'll put more info in.
HTH
Not an answer, but a simple solution.
Redefine Strings, the ItemsSource, as
ObservableCollection<StringViewModelBase> Strings
then create children of StringViewModelBase as
public class StringByRowViewModel : StringViewModelBase
then change the DataTemplate in the resources to be
<DataTemplate DataType="{x:Type vm:StringByRowViewModel}">
<v:InkStringByRowView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:StringByColumnViewModel}" >
<v:InkStringByColumnView />
</DataTemplate>
and lastly, I see no choice but to change the module that builds the Strings (at least as far as the viewmodel goes). Instead of
StringViewModelBase svm = new StringViewModelBase(text,color, w);
use
StringByColumnViewModel svm = new StringByColumnViewModel(text,color,w);
with
Strings.Add(svm);
Apparently, the ObservableCollection only needs a common parent and the Data Type is still preservered in the XAML.
I am trying to create an Image Button in WPF. What I have done is
Create a user control inherited from Button and declare a dependency property in it which supposed to have the required image.
In resource dictionary declare the xaml template for it.
Pass relative and full path to that dependency property, full path works but relative not.
User Control Class
public class ButtonWithImage : Button
{
public ImageSource ButtonImage
{
get { return (ImageSource)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonImageProperty =
DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(ButtonWithImage));
}
Resource Dictionary Code
<Style x:Key="ButtonWithImageStyle" TargetType="complaintRegister:ButtonWithImage">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="complaintRegister:ButtonWithImage">
<StackPanel>
<Image Source="{Binding ButtonImage, RelativeSource={RelativeSource TemplatedParent}}" Width="50" Height="50"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
XAML
<complaintRegister:ButtonWithImage x:Name="ButtonAdd" Content="Add New Complaint" Style="{StaticResource ButtonWithImageStyle}"
ButtonImage="Resources\Plus.png"
Width="150" Height="75" Margin="10 0" Command="{Binding NewCommand}">
</complaintRegister:ButtonWithImage>
Error
It does displays the image in design mode, but at runtime throws this exception
Cannot locate resource 'resources/plus.png'
I can't help but thinking it is trying to resolve 'resources/plus.png' instead of 'Resources/Plus.png.
I bet there is a binding error displayed in output window. :)
How about you post us that error message here.
Btw, in such cases its more propriate to use TemplateBinding instead of RelativeSource TemplatedParent.
TemplateBinding is lets say it like this, specified to work well in control's template. :)
Check those links out:
http://msdn.microsoft.com/en-us/library/ms742882(v=vs.110).aspx
http://www.codeproject.com/Tips/599954/WPF-TemplateBinding-with-ControlTemplate
Edit :
Ahhh you are talking about location of your image.
By default, the Image control only recognize compiled resource image when using XAML to specify its Source property. But, we can use converter or custom markup extension to achieve this goal. The following links contain information about data binding converter and markup extension.
Therefore set that png to build action -> resource and rebuild your solution or use this:
<Image>
<Image.Source>
<BitmapImage UriSource="../Relative/Path/To/Image.png" />
</Image.Source>
</Image>
Though if you wish to use MVVM on this and change path based on data I suggest you to use Bindings:
<Image Source="{Binding MyPath}" Height="50"... />
How, in WPF, do you hide the validation error template adornment (red box by default) when you hide a control? When I hide my controls (to facilitate switching between views) the error adornment sticks around.
Even more difficult, how do I do this using MVVM?
The default ControlTemplate for the Validation.ErrorTemplate has an AdornedElementPlaceholder which in turn has a reference to its AdornedElement. It looks like this
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</ControlTemplate>
From here would could bind the Visibility of the Border to the Visibility of the AdornedElementPlaceholder.AdornedElement to link their Visibility. Then we make all the Control's that has this problem use this Validation.ErrorTemplate instead of the default one. Here's an example
Xaml
<Window.Resources>
<ControlTemplate x:Key="ValidationErrorTamplate">
<Border Visibility="{Binding ElementName=placeHolder,
Path=AdornedElement.Visibility}"
BorderBrush="Red"
BorderThickness="1">
<AdornedElementPlaceholder x:Name="placeHolder"/>
</Border>
</ControlTemplate>
</Window.Resources>
<TextBox ...
Validation.ErrorTemplate="{StaticResource ValidationErrorTamplate}">
Update
To reference the parent UserControl in the binding you can
1.For a specific control you can walk up the logical tree using the Parent Property
Example: If the TextBox is located in a StackPanel in the UserControl we can reference it with Parent.Parent
<UserControl ...>
<StackPanel>
<TextBox ...
Validation.ErrorTemplate="{StaticResource ValidationErrorTamplate2}">
<ControlTemplate x:Key="ValidationErrorTamplate2">
<Border Visibility="{Binding ElementName=placeHolder,
Path=AdornedElement.Parent.Parent.Visibility}"
BorderBrush="Red"
BorderThickness="1">
<AdornedElementPlaceholder x:Name="placeHolder"/>
</Border>
</ControlTemplate>
2.For a more dynamic approach you can use a ResourceDictionary with a code behind file where you make use of the Loaded event for the Border. In it, you walk up the visual tree to find the parent UserControl and use that as the source for the Binding
ValidationErrorTemplateDictionary.xaml
<ResourceDictionary x:Class="ValidationErrorVisibility.ValidationErrorTemplateDictionary"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="ValidationErrorTamplate3">
<Border BorderBrush="Red"
BorderThickness="1"
Loaded="ValidationAdorner_Loaded">
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</ResourceDictionary>
ValidationErrorTemplateDictionary.xaml.cs
public partial class ValidationErrorTemplateDictionary
{
private void ValidationAdorner_Loaded(object sender, RoutedEventArgs e)
{
Border adornedBorder = sender as Border;
Binding visibilityBinding = new Binding("Visibility");
UIElement adornedElement = ((AdornedElementPlaceholder)adornedBorder.Child).AdornedElement;
UserControl parentUserControl = GetVisualParent<UserControl>(adornedElement);
visibilityBinding.Source = parentUserControl;
adornedBorder.SetBinding(Border.VisibilityProperty, visibilityBinding);
}
public static T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
Your UserControl
<UserControl ...>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ValidationErrorTemplateDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel>
<TextBox ...
Validation.ErrorTemplate="{StaticResource ValidationErrorTamplate3}">
I've just had to solve this very problem, for visibility AND opacity.
I did it by creating an inherited attached property which I data bind the ErrorTemplate visibility and opacity to. On the parent element (the actual element that is fading in and out / being collapsed) I simply bind the new attached properties to visibility and opacity respectively.
This method uses WPF's logical tree and existing property value inheritance to solve the problem without code behind, or specific knowledge by your template of what the visibility-controlling parent will be.
In hindsight, I could have created a single attached property of type FrameWorkElement which I can then use to bind on any property on the parent element. This approach would involve less binding and less code to achieve while providing a little more flexibility. Perhaps an attached property already exists to let you do the same thing.
You can read all about attached properties right here: http://msdn.microsoft.com/en-us/library/ms749011.aspx
Alternatively, this is a good stack:
How exactly do Attached Properties work in WPF?
Using the MVVM-pattern you set the DataContext to a specific ViewModel. Now is there any way to tell the XAML the type of the DataContext so that it will validate my bindings?
Looking for something like the typed viewdata in ASP.NET MVC.
You can write each individual binding in a strongly-typed way:
<TextBox Text="{Binding Path=(vm:Site.Contact).(vm:Contact.Name)}" />
However this would not validate the fact that TextBox DataContext is of type ViewModel.Site (and I think this is not possible, but I may be wrong).
No, the current spec does not have strong typing in Xaml. I believe that with .Net 4.0, Xaml should be seeing the capacity for generics. With that, I would think it should be much easier to have strong typing in Xaml.
No. FrameworkElement.DatatContext is the dependency property that enables data binding is of type object.
As pointed out by others, you can specify the expected type of a DataContext for a special template called a DataTemplate. Many controls such as ItemsControl, ControlControl provide access to DataTemplates to allow you to set the visual representation's expectations of the DataContext's type.
Bryan is correct, he did not test his code.
The correct application of a typed DataTemplate looks like this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" />
</Window>
ContentPresenter inherits directly from FrameworkElement and does not have a Template property. In addition, the Template property commonly refers to Control.Template of type ControlTemplate which is something entirely different than a DataTemplate.
I think Bryan was thinking of the ContentControl which is one of the two root control types (the other being ItemsControl). ContentControl does in fact inherit from Control. Therefore we can specify the Template property on it if we so choose.
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
<ControlTemplate x:Key="ControlSkin" TargetType="{x:Type ContentControl}">
...
</ControlTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" Template="{StaticResource ControlSkin}" />
</Window>
I personally declare a static PropertyPath for each property in my viewmodel the reference this using x:static as the binding path -
e.g
public class MyViewModel
{
public static PropertyPath MyPropertyPath = new PropertyPath("MyProperty");
public bool MyProperty{get; set;}
}
xaml : {Binding Path={x:Static local:MyViewModel.MyPropertyPath}}
This way all my bindings get validated on build.
Try this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding}" Template="{StaticResource TypedTemplate}" />
</Window>
I haven't tested this code but it should give you the idea. The content presenter will display the current DataContext which will use the DataTemplate. This isn't strongly typed in the compiler but will throw a runtime error immediately on load (in the window's InitializeComponent). You should be able to catch this easily in your testing if something breaks.