WPF Dependency Property In Nested User Control - c#

I have the following WPF Control which is used to Show A Text With An Image Beside it
XAML Code
<UserControl x:Class="WFWorkSpaceWPF.UserControls.StackedImageTextCtl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Name="StackedImageText"
>
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ElementName=StackedImageText, Path=ImageSource}" />
<TextBlock Text="{Binding ElementName=StackedImageText, Path=Text}" />
</StackPanel>
</Grid>
CS
public partial class StackedImageTextCtl : UserControl
{
public StackedImageTextCtl()
{
InitializeComponent();
}
#region "Properties"
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StackedImageTextCtl), new UIPropertyMetadata(""));
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
#endregion
}
In my project i want to reuse this control in other three user controls where it will be added as a part of these controls, As you see that the StackedImageTextCtl exposes Two Properties (Text and Image Source) that the parent User Controls needs to provide to it and these three controls will take the value from the container window throw XAML, I know that One of the ways to do so is to replicate defining the properties in each of these three user control and Using AddOwner functionality, but i am looking for a more better approach that won't require any repeat in code, can anyone please direct me to such way?.

This is your UserControl:
<UserControl ...>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" Width="16"/>
<TextBlock Text="{Binding Text}" />
</StackPanel>
</UserControl>
and its code:
in addition to Text and ImageSource, there is State which represents the Add/Edit/delete state of this control, and there is StackCtlState which is an attached property and when attached to a FrameworkElement it represents the Add/Edit/delete state of that control.
public StackedImageTextCtl()
{
InitializeComponent();
DataContext = this;
}
//Text Dependency Property
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
//ImageSource Dependency Property
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
//State Dependency Property
public AddEditDelete State
{
get { return (AddEditDelete)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(AddEditDelete), typeof(StackedImageTextCtl), new UIPropertyMetadata(AddEditDelete.Add));
public static AddEditDelete GetStackCtlState(DependencyObject obj)
{
return (AddEditDelete)obj.GetValue(StackCtlStateProperty);
}
public static void SetStackCtlState(DependencyObject obj, AddEditDelete value)
{
obj.SetValue(StackCtlStateProperty, value);
}
public static readonly DependencyProperty StackCtlStateProperty =
DependencyProperty.RegisterAttached("StackCtlState", typeof(AddEditDelete), typeof(StackedImageTextCtl), new UIPropertyMetadata(AddEditDelete.Add));
I also define an enum:
public enum AddEditDelete { Add, Edit, Delete }
In Window xaml:
Each Button or ToggleButton have their attached property StackCtlState set to the desired value and also have their style set to one of styles for Button or ToggleButton.
Then these styles add a StackedImageTextCtl to the contents of styled button/togglebutton in the proper way that resources can be reused. (if you set just Content and don't set Template instead it will only show the contents of the last Button or last ToggleButton) The added StackedImageTextCtl has a State equal to the attached value of its TemplatedParent which is Button or ToggleButton.
At last the style uses Trigger to set the values for Text and Image, based on State of the StackedImageTextCtl.
<Window...
xmlns:myNamespace="clr-namespace:WpfApplication1">
<Window.Resources>
<Style TargetType="{x:Type myNamespace:StackedImageTextCtl}">
<Style.Triggers>
<Trigger Property="State" Value="Add">
<Setter Property="Text" Value="ADD"/>
<Setter Property="ImageSource" Value="/blue_1.jpg"/>
</Trigger>
<Trigger Property="State" Value="Edit">
<Setter Property="Text" Value="EDIT"/>
<Setter Property="ImageSource" Value="/blue_2.jpg"/>
</Trigger>
<Trigger Property="State" Value="Delete">
<Setter Property="Text" Value="DELETE"/>
<Setter Property="ImageSource" Value="/blue_3.jpg"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="stackButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button>
<myNamespace:StackedImageTextCtl State="{TemplateBinding myNamespace:StackedImageTextCtl.StackCtlState}"/>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="stackToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton>
<myNamespace:StackedImageTextCtl State="{TemplateBinding myNamespace:StackedImageTextCtl.StackCtlState}"/>
</ToggleButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Style="{StaticResource stackButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Add"/>
<Button Style="{StaticResource stackButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Edit"/>
<ToggleButton Style="{StaticResource stackToggleButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Delete"/>
</StackPanel>

Related

Binding cannot be converted to type System.String

I'm trying to bind a property from a user control to a window but get error
Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'.
When I was searching the error usually the people didn't create a Dependency Property or that the problem may be because the two Way binding but didn't find a solution
I know that if I give the usercontrol a name and then get the value but I want to use binding
UserControl.xaml
<UserControl x:Name="ctb" x:Class="ChatClient.CustomControls.CustomTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ChatClient.CustomControls"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="350">
<Grid>
<Border CornerRadius="8"
Background="#3e4147">
<Grid>
<TextBox VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
Background="Transparent"
x:Name="TextBox"
TextWrapping="Wrap"
BorderThickness="0"
Foreground="Gray"
CaretBrush="Gray"
Margin="8,0,0,0"
Text="{Binding ElementName=ctb, Path=InputText}">
</TextBox>
<TextBlock IsHitTestVisible="False"
Text="{Binding ElementName=ctb, Path=PlaceHolder}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Foreground="DarkGray">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=TextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Border>
</Grid>
</UserControl>
UserControl.xaml.cs
public CustomTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty InputTextProp = DependencyProperty.Register(
nameof(InputText),
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(OnValueChangedText));
public static readonly DependencyProperty PlaceHolderProp = DependencyProperty.Register("PlaceHolder", typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(null));
public string InputText
{
get { return (string)GetValue(InputTextProp); }
set { SetValue(InputTextProp, value); }
}
public string PlaceHolder
{
get { return (string)GetValue(PlaceHolderProp); }
set { SetValue(PlaceHolderProp, value); }
}
private static void OnValueChangedText(DependencyObject sender,DependencyPropertyChangedEventArgs e)
{
CustomTextBox customTextBox = sender as CustomTextBox;
customTextBox.InputText = (string)e.NewValue;
}
MainWindow.xaml
<customcontrols:CustomTextBox InputText="{Binding UserName}"
PlaceHolder="UserName"
Margin="0,0,0,20" />
Dependency property name must end with "Property", not "Prop":
public static readonly DependencyProperty InputTextProperty = DependencyProperty.Register
(
nameof(InputText),
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(string.Empty)
);
fix it for all usages and all properties.
OnValueChangedText callback implemented incorrectly and should be removed from metadata

WPF Custom control binding image source

I have a custom control based on a button, and I put an image inside. I can set the source of the image in the xaml, but if I try and bind it, it doesn't work.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl">
<Style TargetType="{x:Type local:MyCustomControl}" BasedOn = "{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="pathname"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This works just fine, however if I replace the <Image Source="pathname"/> with <Image Source={Binding MyImage, RelativeSource={RelativeSource Self}}"/>, and reference a Delegate Property in the class, it breaks.
MyCustomControl.cs
public class MyCustomControl : Button
{
static DependencyProperty m_myimage = null;
private DependencyProperty MyImageProperty
{
get
{
return m_myimage;
}
}
public BitmapImage MyImage
{
get
{
return (BitmapImage)GetValue(MyImageProperty);
}
set
{
SetValue(MyImageProperty, value);
}
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
MyImage = new BitmapImage(new Uri(pathname));
}
private static void RegisterDependencyProperties()
{
if (m_myimage == null)
{
m_myimage = DependencyProperty.Register("MyImage",
typeof(BitmapImage), typeof(MyCustomControl), null);
}
}
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
new FrameworkPropertyMetadata(typeof(MyCustomControl)));
RegisterDependencyProperties();
}
}
How can I get it to work?
Figured it out myself after about 2 hours. The xaml binding should look like,
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="{Binding MyImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Note that the binding relative resource went from RelativeSource={RelativeSource Self} to RelativeSource={RelativeSource TemplatedParent}

Bind control property from the inner control

I have a control that contains another control. InnerControl contains some property like ButtonVisibility. The simplified InnerControl code sample is below
public class InnerControl : ControlBase
{
//...
public Visibility ButtonVisibility
{
get { return (Visibility)GetValue(ButtonVisibility); }
set { SetValue(ButtonVisibility, value); }
}
public static readonly DependencyProperty ButtonVisibility =
DependencyProperty.Register(
"ButtonVisibility",
typeof (Visibility),
typeof (InnerControl),
new PropertyMetadata(Visibility.Collapsed));
}
This is the code of MainControl which contains InnerControl as a Dependency Property
public class MainControl : ControlBase
{
//...
public InnerControl MyInner
{
get { return (InnerControl) GetValue(MyInnerProperty); }
set { SetValue(MyInnerProperty, value); }
}
public static readonly DependencyProperty MyInnerProperty =
DependencyProperty.Register(
"MyInner",
typeof (InnerControl),
typeof (MainControl),
new PropertyMetadata(null, OnMyInnerPropertyChanged));
private static void OnMyInnerPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//...
}
}
I have two different templates for this controls in the Generic.xaml
<Style TargetType="local:InnerControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:InnerControl">
<Grid x:Name="ContainerGrid">
<!-- some markup -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:MainControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MainControl">
<Grid x:Name="RootGrid">
<Button x:Name="MyButton" Visibility="{TemplateBinding ???}"/>
<ContentControl x:Name="MyInnerControl" Content="{TemplateBinding MyInner}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is the way how could I use this controls
<controls:MainControl>
<controls:MainControl.MyInner>
<controls:InnerControl ButtonVisibility="Visible">
<!-- some markup -->
</controls:InnerControl>
</controls:MainControl.MyInner>
<!-- some markup -->
</controls:MainControl>
I need to change the state of my MainControl after button's click. That's why it is located in MainControl template.
So, could any body gives me an advise how to bind ButtonVisibility from the MyInner to the MyButton on MainControl?
You need to bind directly to the "ButtonVisibility" property of InnerControl, like this:
<Button x:Name="MyButton"
Visibility="{Binding MyInner.ButtonVisibility,
RelativeSource={RelativeSource TemplatedParent}}"/>
Like #james mentioned, you will have to use RelativeSource to bind to either TemplatedParent or to any Type in the visual tree by specifying type like:
<Button x:Name="MyButton" Content="MyButton"
Visibility="{Binding Path=MyInner.ButtonVisibility,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type controls:MainControl}}}"/>
WPF AttachedProperty helps to change the state of your main control from your inner control.

WPF Custom Control - Button Event

I start the WPF today, I'm trying to implement a custom control.
My problem is that I can not select an element in the template.
My code:
[Generic.xaml]
<Style x:Key="{x:Type local:ImageButton}" TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<StackPanel>
// -> My image
<Image Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"></Image>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
[ImageButton.cs]
public class ImageButton : Button
{
public Image imageOff { get; set; }
public Image imageOn { get; set; }
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public ImageButton()
: base()
{
this.MouseEnter += new MouseEventHandler(SetImageON);
}
public void SetImageON(object sender, MouseEventArgs e)
{
//Here i wanna change my image from StackPanel
}
}
Am I on the good way ? How can I change that image?
Give a name to your image, like x:Name="PART_Image". Then in your code behind override OnApplyTemplate method and store it into a variable.
private Image PART_Image;
public override void OnApplyTemplate()
{
PART_Image = this.GetTemplatedChild("PART_Image");
}
In this case you can access it in your method like:
this.PART_Image.Visibility = Visibility.Collapsed;
sender is your control so you could also right this.SetCurrentValue.
public void SetImageON(object sender, MouseEventArgs e)
{
ImageButton btn = (ImageButton)sender;
btn.SetCurrentValue(TagProperty,imageOn.Source);
}
it seems as though you might wan't to just save the source of your image in the control , instead of the entire image.
But if you wan't to use an Image object in code like this
xaml : in your template .
<StackPanel x:Name="myPanel" />
cs : in your control :
myPanel.Children.Add(imageOn);
You can achieve what you want with a Trigger:
<Style x:Key="{x:Type local:ImageButton}" TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<StackPanel>
<Image Source="{Binding Path=imageOff.Source, RelativeSource={RelativeSource TemplatedParent}}">
<Image.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Image.Source" Value="{Binding Path=imageOn.Source, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</Image.Triggers>
</Image>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
That way you don't need the event in the code-behind. If you wanted to, you could make imageOff and imageOn DependencyProperties, that way you could define the source images in xaml too:
public class ImageButton : Button {
public static readonly DependencyProperty imageOffProperty = DependencyProperty.Register("imageOff", typeof(Image), typeof(ImageButton), new PropertyMetadata(null));
public Image imageOff {
get { return (Image)GetValue(imageOffProperty); }
set { SetValue(imageOffProperty, value); }
}
public static readonly DependencyProperty imageOnProperty = DependencyProperty.Register("imageOn", typeof(Image), typeof(ImageButton), new PropertyMetadata(null));
public Image imageOn {
get { return (Image)GetValue(imageOnProperty); }
set { SetValue(imageOnProperty, value); }
}
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
}
So when you declare an ImageButton in xaml, you can do something like this:
<local:ImageButton imageOn="/Resource/Path/To/imageOn.png" imageOff="/Resource/Path/To/imageOff.png" />

Troubleshooting Binding Error 4

I'm getting the following Binding errors on my code and I don't know how to troubleshoot them. The bindings were generated by VS. I've tried adding presentation.tracesources (which is in the code below) but I get the same output as before.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='ClimateSolutions.SuperTB', AncestorLevel='1''. BindingExpression:Path=myName; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='ClimateSolutions.SuperTB', AncestorLevel='1''. BindingExpression:Path=isRequired; DataItem=null; target element is 'SuperTB' (Name='email'); target property is 'NoTarget' (type 'Object')
Here's my XAML:
<TextBox x:Class="ClimateSolutions.SuperTB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="53" Width="296" FontSize="32"
xmlns:local="clr-namespace:ClimateSolutions"
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
HorizontalAlignment="Left" Name="Blarg">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="Gray" FontSize="24" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:SuperTB, AncestorLevel=1}, Path=myName, diagnostics:PresentationTraceSources.TraceLevel=High}">
</TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding Path=isRequired, RelativeSource={RelativeSource FindAncestor, AncestorType=local:SuperTB, AncestorLevel=1}}" Value="False">
<Setter Property="Text" Value="100" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
and here's the CS for SuperTB:
namespace ClimateSolutions
{
/// <summary>
/// Interaction logic for SuperTB.xaml
/// </summary>
public partial class SuperTB : TextBox, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String Property)
{
var anEvent = this.PropertyChanged;
if (anEvent != null)
{
anEvent(this, new PropertyChangedEventArgs(Property));
}
}
private String MyName = "Unicorns!";
private static DependencyProperty myNameProperty = DependencyProperty.Register("myName", typeof(String), typeof(SuperTB));
public String myName
{
get { return MyName; }
set { MyName = value; NotifyPropertyChanged("myName"); }
}
DependencyProperty isRequiredProperty = DependencyProperty.Register("isRequired", typeof(Boolean), typeof(SuperTB));
public Boolean isRequired
{
get { return (Boolean)GetValue(isRequiredProperty); }
set { SetValue(isRequiredProperty, value); }
}
public SuperTB()
{
InitializeComponent();
myName = "Unicorns!";
}
}
}
EDIT : I have updated the code according to your comment. To summarize, since this is a custom control, you are less dependant on the MVVM pattern to build your component logic (and thus use code behind in you component) as soon as your componennt itself meets this needs (to be sort, make its properties to be as much bindable as you can). For example, in the updated code, you can now bind the default property, but you can also imagine exposing properties to set the foreground colors used to diaplay control name when there is no value, and so forth.
I tried several things with you original code (included solution provided by J cooper) and nothing seemed to work. It seems that there is a lot of issues with your code.
I managed to approach a solution by making your textbox a custom control.
Here is the Generic.xaml (the visual definition of your control) :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Question_6514447">
<Style TargetType="{x:Type local:SuperTB2}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SuperTB2}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_Input">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsRequired}" Value="False">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DefaultTextValue}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And here is the code behind of the control :
[TemplatePart(Name = "PART_Input")]
public class SuperTB2 : Control
{
private TextBox PART_Input;
static SuperTB2()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SuperTB2), new FrameworkPropertyMetadata(typeof(SuperTB2)));
}
public SuperTB2()
{
Loaded += SuperTb2Loaded;
}
public override void OnApplyTemplate()
{
PART_Input = GetTemplateChild("PART_Input") as TextBox;
if (PART_Input != null)
{
PART_Input.GotFocus += PartInputGotFocus;
PART_Input.LostFocus += PartInputLostFocus;
}
}
void PartInputLostFocus(object sender, RoutedEventArgs e)
{
if (PART_Input.Text == string.Empty)
{
PART_Input.Text = Name;
PART_Input.Foreground = new SolidColorBrush(Colors.Gray);
}
}
void PartInputGotFocus(object sender, RoutedEventArgs e)
{
if (PART_Input.Text.Equals(Name))
{
PART_Input.Text = string.Empty;
PART_Input.Foreground = new SolidColorBrush(Colors.Black);
}
}
void SuperTb2Loaded(object sender, RoutedEventArgs e)
{
if (PART_Input.Text == string.Empty)
{
PART_Input.Text = Name;
PART_Input.Foreground = new SolidColorBrush(Colors.Gray);
}
}
private static DependencyProperty myNameProperty =
DependencyProperty.Register("MyName", typeof(string), typeof(SuperTB2), new PropertyMetadata("Unicorns !", NameChanged));
private static void NameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public string MyName
{
get { return (string)GetValue(myNameProperty); }
set { SetValue(myNameProperty, value); }
}
DependencyProperty isRequiredProperty =
DependencyProperty.Register("IsRequired", typeof(bool), typeof(SuperTB2), new PropertyMetadata(false, IsReqChanged));
private static void IsReqChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public bool IsRequired
{
get { return (bool)GetValue(isRequiredProperty); }
set { SetValue(isRequiredProperty, value); }
}
public string DefaultTextValue
{
get { return (string)GetValue(DefaultTextValueProperty); }
set { SetValue(DefaultTextValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DefaultTextValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultTextValueProperty =
DependencyProperty.Register("DefaultTextValue", typeof(string), typeof(SuperTB2), new UIPropertyMetadata("100"));
}
And an example of use of the component :
<Grid>
<StackPanel>
<Question_6514447:SuperTB2 x:Name="FirstName" IsRequired="true" DefaultTextValue="200"/>
</StackPanel>
</Grid>
With this updated code, I think you can acheive almost all the behaviors your needed !
Hope this will help !
Do not use relative source in your binding expressions. Relative source is used to access elements higher in the element tree. It seems as though you were using it in terms of object inheritance.
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="Gray" FontSize="24" Text="{Binding Path=myName, diagnostics:PresentationTraceSources.TraceLevel=High}">
</TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding Path=isRequired}" Value="False">
<Setter Property="Text" Value="100" />
</DataTrigger>

Categories

Resources