Attached behavior binding to element in controltemplate - wpf

I am adding an attached behaviour to a slider which will cause it to scroll some content when the thumb is dragged and held over a specific region. (Can't use a straightforward IsMouseOver trigger as the Slider Thumb has MouseCapture.)
The behaviour has 3 properties:
#region IsScrollHoverProperty
public static readonly DependencyProperty IsScrollHoverProperty = DependencyProperty.RegisterAttached(
"IsScrollHover",
typeof(Boolean),
typeof(ScrollHoverAreaBehaviour),
new UIPropertyMetadata(false));
#endregion
#region ScrollLeftRectProperty
public static readonly DependencyProperty ScrollLeftRectProperty = DependencyProperty.RegisterAttached(
"ScrollLeftRect",
typeof(Rectangle),
typeof(ScrollHoverAreaBehaviour),
new UIPropertyMetadata(null));
#endregion
#region ScrollRightRectProperty
public static readonly DependencyProperty ScrollRightRectProperty = DependencyProperty.RegisterAttached(
"ScrollRightRect",
typeof(Rectangle),
typeof(ScrollHoverAreaBehaviour),
new UIPropertyMetadata(null));
#endregion
The IsScrollHoverProperty is being set to true when the user drags the slider, this is all done in the Slider's ControlTemplates.Triggers, and works correctly.
When it's set to true the callback is going to hook PreviewMouseEnterHandlers into the two Rectangles to detect when the mouse enters them.
The Rectangles in question are also defined in the Slider's controltemplate thusly:
<StackPanel Grid.Row="0" Grid.RowSpan="3" HorizontalAlignment="Left" Orientation="Horizontal">
<Rectangle Width="40" Fill="#AAAAAAAA" Name="ScrollLeftRect"/>
</StackPanel>
<StackPanel Grid.Row="0" Grid.RowSpan="3" HorizontalAlignment="Right" Orientation="Horizontal">
<Rectangle Width="40" Fill="#AAAAAAAA" Name="ScrollRightRect"/>
</StackPanel>
The problem I have is binding these Rectangles to the attached ScrollRightRect and ScrollLeftRect Properties. I have tried a few things and suspect I have made a stupid binding error or am trying to do something not allowed. I am currently binding them in the controltemplate.triggers as follows:
<Trigger Property="local:ScrollHoverAreaBehaviour.IsScrollHover" Value="False">
<Setter Property="local:ScrollHoverAreaBehaviour.ScrollLeftRect" Value="{Binding ElementName=ScrollLeftRect}"/>
<Setter Property="local:ScrollHoverAreaBehaviour.ScrollRightRect" Value="{Binding ElementName=ScrollRightRect}"/>
<Setter TargetName="ScrollLeftRect" Property="Fill" Value="Red"/>
<Setter TargetName="ScrollRightRect" Property="Fill" Value="Red"/>
</Trigger>
I know this Trigger is being tripped as the rectangles fill Red as expected.
Can anyone spot what I'm doing wrong from these snippets?
Thanks in advance.
Rob

First, let's confirm you're not doing anything wrong, and the problem has nothing to do with the attached behaviors.
<Button>
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="Yellow">
<StackPanel>
<TextBlock x:Name="theText" Text="Hello" />
<ContentPresenter />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Content" Value="{Binding ElementName=theText, Path=Text}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
This snippet should cause "Hello" to appear twice when I mouse over the button, but it doesn't, and I get the same error as you:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=theText'. BindingExpression:Path=Text; DataItem=null; target element is 'Button' (Name=''); target property is 'Content' (type 'Object')
This is explainable - once the binding is set on the Button, it won't be able to find a control named 'theText', because the Button lives in a different NameScope.
An alternative
Some WPF controls need to do something similar to you - they assume that a specific control exists in the tree that they will interact with. But they don't use properties - they use names.
Start by giving the controls a name - the convention is to use "PART_" prefix:
<Rectangle ... Name="PART_ScrollLeftRect" />
Now put code like this in your callback when IsScrollHover is set:
private static void IsScrollHoverSetCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (Slider) d;
if ((bool)e.NewValue == false)
return;
target.ApplyTemplate();
var leftRectangle = target.Template.FindName("PART_ScrollLeftRect", target);
var rightRectangle = target.Template.FindName("PART_ScrollRightRect", target);
// Do things with the rectangles
}
Note that depending on when the IsScrollHost property is set, the template might not be ready yet. In that case, you might want to subscribe to the Loaded or similar event, and then call ApplyTemplate().
Although it might seem more complicated, it has one nice benefit: the markup will be simpler. A designer using Blend won't have to remember to wire up those complicated triggers, they just have to name the controls correctly.
The use of the PART_ prefix is a WPF convention, and normally used along with the TemplatePart attribute. An example of this is the TextBox. When you override the template of a TextBox, it won't function until you add a control named PART_ContentHost.
Update: I just blogged about template parts here: http://www.paulstovell.com/wpf-part-names

Related

Accessing a static object from Style/template in Generic.xml?

I have a double called LoadAnimAngle which simply holds the angle of a spinning loading icon, which gets rotated over time. This variable is defined in my MainViewModel class. I'm using this same variable across all places that has a spinning loading icon.
I need it inside a custom control that is defined in Generic.xml with a style/template. Here is the part where I'm binding to LoadAnimAngle:
<v:ColoredImage Image="{StaticResource LoadingIcon}" Color="{StaticResource DarkBlueClick}" RenderTransformOrigin="0.5, 0.5" VerticalAlignment="Center" Width="32" Height="32" Margin="0,0,0,0" Visibility="{Binding IsBusy, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibility}}">
<v:ColoredImage.RenderTransform>
<RotateTransform Angle="{Binding MainViewModel.LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}}}"/> //here is the error
</v:ColoredImage.RenderTransform>
</v:ColoredImage>
The custom control has a property that is binding to my instance of MainViewModel, like so:
public MainViewModel MainViewModel { get { return MainViewModel.instance; } }
Inside the constructor of MainViewModel I simply set:
instance = this;
The problem is that Generic.xml gets loaded before my MainViewModel class, causing the instance to be null for the frame before the graphics have loaded, after everything is done loaded, everything works. How could I resolve this problem?
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=MainViewModel.LoadAnimAngle; DataItem=null; target element is 'RotateTransform' (HashCode=66939890); target property is 'Angle' (type 'Double')
Notice: I do understand that the error is harmless and does not effect anything for the end user, however seeing that error every time I debug causes me emotional pain.
I need to somehow load MainViewModel before Generic, OR, tell xaml to not try to get the data from LoadAnimAngle until MainViewModel != null.
EDIT
I get the same error after I made changes so that I do not directly bind to the instance of MainViewModel. So I think my evaluation of the case of the problem is wrong.
I added
public double LoadAnimAngle
{
get
{
if (MainViewModel.instance != null)
{
return MainViewModel.instance.LoadAnimAngle;
}
return 0;
}
}
to the view model (instead of return MainViewModel.instance)
Then I changed the binding to:
Angle="{Binding Path=LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}}"
I get the same error:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=LoadAnimAngle; DataItem=null; target element is 'RotateTransform' (HashCode=21529561); target property is 'Angle' (type 'Double')
If the problem is not that the MainViewModel.instance is NULL, then what is it that causes the problem? I have problems decoding the language in the error message. What exactly is wrong and why?
EDIT 2
Relevant context (?)
<Style TargetType = "{x:Type v:ComPortButton}" >
<Setter Property = "Background" Value = "{StaticResource Milky}"/>
<Setter Property = "ColorPalette" Value = "{StaticResource MilkyPalette}"/>
<Setter Property = "Foreground" Value = "{StaticResource Black}"/>
<Setter Property = "BorderColor" Value = "{StaticResource Milky}"/>
<Setter Property="IsBasicTextButton" Value="False"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type v:ComPortButton}">
<Grid>
<Grid Visibility="{Binding Path=IsBasicTextButton, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource InverseBooleanToVisibility}}">
<Border BorderBrush="{TemplateBinding BorderColor}" Background="{TemplateBinding Background}" Width="128" Height="140" BorderThickness="1"/>
//REMOVED IREELEVANT CODE
<v:ColoredImage Image="{StaticResource LoadingIcon}" Color="{StaticResource DarkBlueClick}" RenderTransformOrigin="0.5, 0.5" VerticalAlignment="Center" Width="32" Height="32" Margin="0,0,0,0" Visibility="{Binding IsBusy, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibility}}">
<v:ColoredImage.RenderTransform>
<RotateTransform Angle="{Binding MainViewModel.LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}}}"/> //here is the error
</v:ColoredImage.RenderTransform>
</v:ColoredImage>
</Grid>
//REMOVED IRRELEVANT CONTROL
</Grid>
//REMOVED IRRELEVANT CONTEXT MENU
</Grid>
//REMOVED IRRELEVANT TRIGGERS
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT 3
The source of the error seems to be completely different from I first thought. The error seems to have something to do with RenderTransform, because I can access the property without errors from other places.
Like this:
// NO ERROR FOR TEXT BLOCK
<TextBlock Text="{Binding MainViewModel.LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}}"/>
<v:ColoredImage Image="{StaticResource LoadingIcon}" Color="{StaticResource DarkBlueClick}" RenderTransformOrigin="0.5, 0.5" VerticalAlignment="Center" Width="32" Height="32" Margin="0,0,0,0" Visibility="{Binding IsBusy, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibility}}">
<v:ColoredImage.RenderTransform>
// ERROR FOR ROTATETRANSFORM
<RotateTransform Angle="{Binding MainViewModel.LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}}"/>
</v:ColoredImage.RenderTransform>
</v:ColoredImage>
But I also get the error when I do not reference MainViewModel. I created a new property like this:
public double LoadAnimAngle
{
get
{
return 0;
}
}
Then I used it in the Template like this:
<v:ColoredImage Image="{StaticResource LoadingIcon}" Color="{StaticResource DarkBlueClick}" RenderTransformOrigin="0.5, 0.5" VerticalAlignment="Center" Width="32" Height="32" Margin="0,0,0,0" Visibility="{Binding IsBusy, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibility}}">
<v:ColoredImage.RenderTransform>
<RotateTransform Angle="{Binding LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}}"/>
</v:ColoredImage.RenderTransform>
</v:ColoredImage>
But i get the EXACT same error!
So, the property works, everything works. It's just that RenderTransform is like outside of the VisualTree for the first frame when it is instantiated? Or something like that, i guess? Something different is happening in RenderTransform that makes it so it doesnt like my binding.
And i probably wasnt clear about the structure.
ComPortButton is a Custom Control (.cs file with Template/Style in Generic.xml).
ComPortButton uses ComPortVM as it's DataContext.
I want to access the spinning value globally, different controls, different windows, different everything, globally.
I have a MainViewModel in which i currently store the value, since it gives global access, since it
EDIT 4
Solved it and posted the solution below
After i figured it out that it was RenderTransform that was the problem and not anything else it was easy to find solutions online, seems that many people have had the same problem.
Here is the Thread that helped me solve it
The problem had something to do with VisualTree, that RenderTransform in the Template isnt hooked up to the VisualTree before the entire Control is loaded. Or something like that.
When binding like this to RotateTransform:
<v:ColoredImage.RenderTransform>
<RotateTransform Angle="{Binding LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}}"/>
</v:ColoredImage.RenderTransform>
The problem occurs. But for some reason that i did not understand, you can get rif of the error by binding to RenderTransform instead. But for that you need a Converter.
[ValueConversion(typeof(double), typeof(RotateTransform))]
public class AngleToTransform : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return new RotateTransform((double)value);
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
Then use the converter like so:
<v:ColoredImage RenderTransform="{Binding MainViewModel.LoadAnimAngle, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource AngleToTransform}}"/>
Your control must be independent from the concrete view model type. Instead, bind internals of the control to dependency properties on this control. Then let the external view model bind to this properties (or set them locally).
This way you remove the tight coupling between the control and the DataContext, which drastically simplifies the implementation of the control. It also allows the control to be reused with any DataContext (view model).
ComPortButton.cs
class ComPortButton : Control
{
public double Angle
{
get => (double)GetValue(AngleProperty);
set => SetValue(AnglePropertyKey, value);
}
protected static readonly DependencyProperty AnglePropertyKey = DependencyProperty.RegisterReadOnly(
"Angle",
typeof(double),
typeof(ComPortButton),
new PropertyMetadata(default));
public static readonly DependencyProperty AngleProperty = AnglePropertyKey..DependencyProperty;
public double ProgressPercentage
{
get => (double)GetValue(ProgressPercentageProperty);
set => SetValue(ProgressPercentageProperty, value);
}
public static readonly DependencyProperty ProgressPercentageProperty = DependencyProperty.Register(
"ProgressPercentage",
typeof(double),
typeof(ComPortButton),
new PropertyMetadata(default(double), OnProgressPercentageChanged));
private static void OnProgressPercentageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double percentage = (double)e.NewValue / 100;
// Enforce an angle between 0°-360°
this.Angle = Math.Max(0, Math.Min(360, 360 * percentage));
}
}
Generic.xaml
<Style TargetType="ComPortButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type v:ComPortButton}">
<v:ColoredImage>
<v:ColoredImage.RenderTransform>
<RotateTransform Angle="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Angle}" />
</v:ColoredImage.RenderTransform>
</v:ColoredImage>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage example
<Window>
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<ComPortButton ProgressPercentage="{Binding ProgressPercentageValue}" />
</Window>

Expander.IsExpanded Binding breaking after first click

am probably doing something but can't figure out my problem. Any help would be really appreciated:
I am having a CustomControl called Section. A Section is collapsible and therefore its ControlTemplate contains an Expander. The Expanders IsExpanded-Property is bound to the Section's IsExpanded Property via TemplateBinding.
When setting IsExpanded on a Section the Expander collapses, but using the toggleButton within the Expander appears to break that binding. Probably by setting a local value to the Expander's IsExpanded-Property. Anyways, after changing the Expander state via Mouse the binding breaks and setting the Section's IsExpanded doesn't do anything.
This however does not happen when putting an Expander into a view and binding its IsExpanded-Property to some DP in the View.
Also notworthy: Snoop does not show any Bindings on the Expander's IsExpanded-Property. It only shows the Value-Source is ParentTemplate. As soon as I click the ToggleButton to Change IsExpanded the Value-Source changes to Local (possibly breaking the former Binding?)
Section.cs:
public class Section : Control
{
static Section()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Section), new FrameworkPropertyMetadata(typeof(Section)));
}
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(
"IsExpanded", typeof (bool), typeof (Section), new PropertyMetadata(default(bool)));
public bool IsExpanded
{
get { return (bool) GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
}
Generic.xaml Style:
<Style TargetType="{x:Type local:Section}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Section}">
<Expander Header="Test" IsExpanded="{TemplateBinding IsExpanded}" >
<Rectangle Fill="Aqua" Height="200" Width="200" />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any ideas?
So I found the answer which is pretty basic knowledge actually:
TemplateBindings are always ONEWAY no matter what the MetaData states...
Using:
IsExpanded="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
fixed my problem...

Apply the focus rectangle to a child element

In a control I'm currently working on, when an item is focused, the default focus rectangle spans the entire row and all of its visible child items. I know how to hide that. But I still want such a focus indicator when the item has the keyboard focus. I've read about the IsKeyboardFocused property but that is true also when the item was clicked by the mouse. So I guess I need to use the FocusVisualStyle somehow. But I can't figure out how.
Here's what the default focus looks like:
And this is what it should like:
Here's my XAML code for the control template:
<Border ...>
<ContentPresenter FocusManager.IsFocusScope="True"
Content="{TemplateBinding HeaderedContentControl.Header}"
ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
ContentStringFormat="{TemplateBinding HeaderedItemsControl.HeaderStringFormat}"
ContentSource="Header"
Name="PART_Header" .../>
</Border>
<!-- Additional border glare inside the item -->
<Border BorderThickness="1" BorderBrush="#80ffffff" Margin="1"
SnapsToDevicePixels="True" CornerRadius="2"/>
<!-- Focus rectangle inside the item -->
<Rectangle StrokeDashArray="1 2" StrokeThickness="1" Stroke="Black"
SnapsToDevicePixels="True" Margin="2"
Visibility="Hidden" Name="FocusRectangle"
FocusVisualStyle="{StaticResource FocusStyle}"/>
There is already a focus rectangle in my XAML that is invisible by default. With FocusVisualStyle or anything, it shall be made visible. But I didn't manage to do that. It's either visible at any focus, or never.
I have found a workaround for the issue. It looks the same to me, but I'm not completely sure whether it's the correct way to do it. I'm using the FocusRectangle from above and care for showing and hiding it myself.
Here's the trigger that manages the focus rectangle visibility:
<!-- Show the focus rectangle when the item is focused -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Controls:TreeViewExItem.IsKeyboardMode" Value="True"/>
<Condition Property="Controls:TreeViewExItem.IsFocused" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="FocusRectangle" Property="Visibility" Value="Visible"/>
</MultiTrigger>
Then I have added a new property to the TreeViewExItem that indicates whether the last input came from the mouse or the keyboard. This could possibly be extended to touch or stylus, but I don't have such devices to test.
public static DependencyProperty IsKeyboardModeProperty =
DependencyProperty.Register(
"IsKeyboardMode",
typeof(bool),
typeof(TreeViewExItem),
new FrameworkPropertyMetadata(false, null));
public bool IsKeyboardMode
{
get
{
return (bool) GetValue(IsKeyboardModeProperty);
}
set
{
SetValue(IsKeyboardModeProperty, value);
}
}
This property is passed to each item from the parent control through binding:
<!-- Pass on the TreeViewEx' IsKeyboardMode value to each item because
we couldn't access it otherwise in the triggers -->
<Setter Property="IsKeyboardMode"
Value="{Binding (Controls:TreeViewEx.IsKeyboardMode),
RelativeSource={RelativeSource
AncestorType={x:Type Controls:TreeViewEx}}, Mode=OneWay}" />
The same IsKeyboardMode property is added to the TreeViewEx parent control and here comes my magic:
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (!IsKeyboardMode)
{
IsKeyboardMode = true;
//Debug.WriteLine("Changing to keyboard mode from PreviewKeyDown");
}
}
protected override void OnPreviewKeyUp(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (!IsKeyboardMode)
{
IsKeyboardMode = true;
//Debug.WriteLine("Changing to keyboard mode from PreviewKeyUp");
}
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
if (IsKeyboardMode)
{
IsKeyboardMode = false;
//Debug.WriteLine("Changing to mouse mode");
}
}
This reacts on the preview events of keyboard and mouse to set the appropriate input mode. Only if the last input came from a keyboard, the focus rectangle is visible.

How to set a converter in a style but not the path?

I'm trying to create a style for a textbox which I want to be able to use throughout my code. My style defines a converter in the binding of the Text property but does not set its path because my bound data may not be named the same wherever I use this style.
<Style x:Key="CustomTextBox" BasedOn="{StaticResource {x:Type TextBox}}"
TargetType="{x:Type TextBox}">
<Setter Property="Text">
<Setter.Value>
<Binding>
<Binding.Converter>
<CustomTextBoxConverter/>
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
</Style>
And then the customTextBox would be use like so :
<TextBox Height="28" Name="txtRate" Style="{StaticResource CustomTextBox}"
MaxLength="5" Text="{Binding Path=BoundData}"/>
When I write the code above, I get an execption that "Two-way binding requires Path or XPath.".
I even tried to create an attached properties that is used in the style binding to reflect this value in the style but I couldn't get working either. See next :
<Converters:SomeConvertingFunction x:Key="CustomTextConverter"/>
<local:CustomAttachedProperties.ReflectedPath x:Key="ReflectedPath"/>
<Style x:Key="CustomTextBox" BasedOn="{StaticResource {x:Type TextBox}}"
TargetType="{x:Type TextBox}">
<Setter Property="Text">
<Setter.Value>
<Binding Path=ReflectedPath Converter=CustomTextConverter/>
</Setter.Value>
</Setter>
</Style>
Used in a page like so :
<TextBox Height="28" Name="txtRate" Style="{StaticResource CustomTextBox}"
MaxLength="5" CustomAttachedProperty="contextBoundDataAsString"/>
The code for the attached property is :
Public Class CustomAttachedProperties
Public Shared ReadOnly ReflectedPathProperty As DependencyProperty =
DependencyProperty.RegisterAttached("ReflectedPath", GetType(String),
GetType(CustomAttachedProperties))
Public Shared Sub SetReflectedPath(element As UIElement, value As String)
element.SetValue(ReflectedPathProperty, value)
End Sub
Public Shared Function GetReflectedPath(element As UIElement) As String
Return TryCast(element.GetValue(ReflectedPathProperty), String)
End Function
End Class
When I try to using the above code it compiles fine but it does not seem to do anything on my XAML, like it might be creating different instances of the CustomAttachedProperty.
Sorry for the lenghty question but I thought it should be easy to create custom controls that have their own default converters with WPF... I'm confused!
You can create a UserControl that does this quite simply:
<UserControl x:Class="namespace.MyCustomConverterTextBox">
<TextBlock Text="{Binding Text, Converter={StaticResource yourconverter}}"/>
</UserControl>
Then declare Text as a DependencyProperty in code-behind:
public partial class MyCustomConverterTextBox : UserControl
{
public string Text {
get{return (string) GetValue(TextProperty);}
set{SetValue(TextProperty, value);}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyCustomConverterBox));
}
This should be enough to let you use it in your xaml:
<local:MyCustomConverterTextBox Text="{Binding YourBinding}" />
I didn't run this code so there might be typos, but it should be enough to give you an idea how to go about this.
The best way to do this in my opinion, is to create a new class that inherits from TextBox, then override the PropertyMetadata for the Text property, allowing yourself an opportunity to change the value in a Coerce callback.

Control's parent is null when placed inside a ContentControl

I've got a simple control derived from ContentControl with 3 properties.
My problem comes when I try to perform a control.TransformToVisual() with a control that is placed inside MainContent. It always brings up an ArgumentNullException.
My guess is this due to the control having a null Parent property. Is there a simple way to way around this?
C#
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(LabelledControl), null);
public static readonly DependencyProperty ValidationContentProperty =
DependencyProperty.Register("ValidationContent", typeof(object), typeof(LabelledControl), null);
public static readonly DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(object), typeof(LabelledControl), null);
XAML
<Style TargetType="local:LabelledControl">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LabelledControl">
<StackPanel Margin="0 10 0 0">
<StackPanel Orientation="Vertical">
<dataInput:Label Content="{TemplateBinding LabelText}" FontWeight="Bold" FontSize="12" IsTabStop="False"/>
<ContentControl Content="{TemplateBinding ValidationContent}" IsTabStop="False"/>
</StackPanel>
<ContentControl x:Name="_contentControl" Content="{TemplateBinding MainContent}" IsTabStop="False"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Have you tried using the ContentPresenter class instead of the ContentControl class within your ControlTemplate to present those properties within the template? I am not sure if it is related to your ArgumentNullException, but typically the content of a ContentControl is exposed on the template via a ContentPresenter.
Since your control derives from ContentControl the ContentPresenter will automatically bind the Content and ContentTemplate properties for you to whatever the Content property is set to. You could also manually bind the Content property of the ContentPresenter to your ValidationContent property.
I am not sure why you are defining a MainContent property when the base ContentControl already gives you a Content property to use, maybe that is a second piece of content you are trying to expose.

Resources