Hiding validation adornment when hiding a control - wpf

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?

Related

TemplatedParent Binding in Resources of ControlTemplate

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.

How to bind to parent position changes in WPF using XAML and no code behind

I have a visio-like interfact but have actual model data behind some of the elements. The elements can be moved by the user.
I use a contentcontrol on a canvas whereby the viewmodels of the elements are places in the content which can then be displayed differently depending on their type but using the same contentcontrol. It is simple to bind the view to the different properties in the viewmodel. However, I have to save the position in the model, and I cannot find a binding solution.
1) The Application.Save Command is handled in the main view model, so I do not have access to the view there. That means I must save the postion data when the elements are moved, or is there a better approach?
2) Assuming that I am right with 1), I am looking to avoid code behind, i.e. I do not want the contentcontrol to deal with the elements that they have in their content. However, so far the code behind version is all I could come up with:
My code behind solution so far:
All model elements implement an interface:
public interface IViewElement
{
String Position { get; set; }
}
And in the contentcontrol:
void ContentControl_LayoutUpdated(object sender, EventArgs e)
{
IViewElement content = this.Content as IViewElement;
content.Position = new Point(Diagram.GetLeft(this), Diagram.GetTop(this)).ToString();
}
The XAML:
<Style TargetType="{x:Type diagram:Item}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type diagram:Item}">
<Grid Canvas.Top="{Binding ElementName=PART_ContentPresenter, Path=Content.Position, Mode=TwoWay}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ContextMenu="{x:Null}">
<!-- PART_ContentPresenter -->
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<DataTemplate DataType="{x:Type local:ViewModel}">
<StackPanel>
...
</StackPanel>
Just encapsulate the codebehind you've used in a Behavior
Why are you using a string to store the position? Use either a Point or two decimal values, and then bind your ContentControl's Canvas.Top and Canvas.Left position to these values using two-way binding.
It will automatically update the model when the Top and Left positions change.
Edit:
Here's an example:
<ContentControl Canvas.Top="{Binding ContentModel.Top, Mode=TwoWay}"
Canvas.Left="{Binding ContentModel.Left, Mode=TwoWay}"
Content="{Binding ContentModel}" />

How to use template binding inside data template in custom control (Silverlight)

I am trying to create control which will take ItemsSource and InnerTemplate and will show all the items wrapped in CheckBoxes.
The control has 2 dependency properties:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CheckBoxWrapperList), null);
public static readonly DependencyProperty InnerTemplateProperty = DependencyProperty.Register("InnerTemplate", typeof(DataTemplate), typeof(CheckBoxWrapperList), null);
and here is the template:
<ControlTemplate TargetType="local:CheckBoxWrapperList">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="wrapper">
<CheckBox>
<ContentPresenter ContentTemplate="{TemplateBinding InnerTemplate}" Content="{Binding}" />
</CheckBox>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemTemplate="{StaticResource wrapper}" ItemsSource="{TemplateBinding ItemsSource}" />
</Grid>
</ControlTemplate>
However, this approach does not work.
Binding in the ControlPresenter.ContentTemplate using TemplateBinding does not work.
However, when I don't use template binding and reference the template as static resource, then it works as expected.
Why cannot I use the template binding inside the content presenter in datatemplate?
What am I missing here? Any special markup required?
Is there a way to achieve the expected behavior?
Thanks in advance.
Silverlight and WPF
You can get around this with a relative source binding:
Instead of:
{TemplateBinding InnerTemplate}
You would use:
{Binding RelativeSource={RelativeSource AncestorType=local:CheckBoxWrapperList}, Path=InnerTemplate}
It's a bit messier but it works.
WinRT
WinRT doesn't have AncestorType. I've got something that works but it's kind of horrifying.
You can use an attached property to store a TemplateBinding value and then access it using ElementName...
<ControlTemplate TargetType="local:CheckBoxWrapperList">
<Grid x:Name="TemplateGrid" magic:Magic.MagicAttachedProperty="{TemplateBinding InnerTemplate}">
<Grid.Resources>
<DataTemplate x:Key="wrapper">
<CheckBox>
<ContentPresenter ContentTemplate="{Binding ElementName=TemplateGrid, Path=(magic:Magic.MagicAttachedProperty)}" Content="{Binding}" />
</CheckBox>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemTemplate="{StaticResource wrapper}" ItemsSource="{TemplateBinding ItemsSource}" />
</Grid>
</ControlTemplate>
I don't know if there's a better way for WinRT.
TemplateBinding can only be used within a ControlTemplate, you're using it within a DataTemplate. (The fact that the DataTemplate is within a ControlTemplate doesn't matter)

Conditional XAML

For easy of development I'm using a ViewBox to wrap all content inside a Window. This is because my development machine has a smaller screen than the deployment machine so using a ViewBox allows for better realisation of proportion. Obviously there is no reason for it to be there on Release versions of the code. Is there an easy method to conditionally include/exclude that 'wrapping' ViewBox in XAML?
E.g.
<Window>
<Viewbox>
<UserControl /*Content*/>
</Viewbox>
</Window>
create two control templates in a resources dictionary somewhere accesible.
they should look like this
<ControlTemplate x:key="debug_view">
<ViewBox>
<ContentPresenter Content={Binding} />
</ViewBox>
</ControlTemplate>
<ControlTemplate x:key="release_view">
<ContentPresenter Content={Binding} />
</ControlTemplate>
then you could use this in your main view
<Window>
<ContentControl Template="{StaticResource debug_view}">
<UserControl /*Content*/ ...>
</ContentControl>
</Window>
then to switch back and forth just change the lookup key in the StaticResource Binding from 'debug_view' to 'release_view'
if you wanted to make it more dynamic you could additionally do this:
<Window>
<ContentControl Loaded="MainContentLoaded">
<UserControl /*Content*/ ...>
</ContentControl>
</Window>
then in your codebehind
void MainContentLoaded(object sender, RoutedEventArgs e)
{
ContentControl cc = (ContentControl) sender;
#if DEBUG
sender.Template = (ControlTemplate) Resources["debug_view"];
#else
sender.Template = (ControlTemplate) Resources["release_view"];
#endif
}
this way depending on whether or not the DEBUG symbol is defined different templates will be selected.

Exposing a sub-control in a UserControl for use in XAML

I have a UserControl that contains a TreeView. I want the user to be able to set the properties of the inner TreeView control via XAML and I'm not sure how to do that.
I've tried creating a public property on the UserControl to the TreeView, but that only allows me to set a SelectedItemChanged trigger.
I'd like to do something like:
<ExampleUserControl>
<ExampleUserControl.TreeView.ItemTemplate>
...
</ExampleUserControl.TreeView.ItemTemplate>
</ExampleUserControl>
Or:
<ExampleUserControl TreeView.ItemsSource="{Binding Foo}" />
I would prefer not to create properties in the UserControl for each TreeView property, and I don't want to force the user to define the control in C#.
As for passing multiple properties to the child control in your user control, you can always expose a Style property.
ie ChildStyle
For the ItemsSource unless you use [Josh Smith's Element Spy / Data Context Spy / Freezable][1] trick, you will have a disconnect on DataContexts.
So either you employ those tricks or simply have 2 properties.
1) the ItemsSource
2) the ChildStyle
The xaml ends up...
<ChildTreeAnswer:MyControl ItemsSource="{Binding Items}">
<ChildTreeAnswer:MyControl.ChildStyle>
<Style>
<Setter Property="ItemsControl.ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black"
BorderThickness="1"
Margin="5">
<TextBlock Text="{Binding }" />
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ChildTreeAnswer:MyControl.ChildStyle>
</ChildTreeAnswer:MyControl>
Then in your user control do... (I used a listbox for simplicity sake)
<ListBox ItemsSource="{Binding ItemsSource}"
Style="{Binding ChildStyle}" />

Resources