views:

167

answers:

2

I want to add some elements to a TextBox by using a Template with the extra elements and the original TextBox inserted in the right spot. I'm trying to use the AdornedElementPlaceholder just like you would do when making a Validation.ErrorTemplate But the AdornedElement is not showing up. I have simplified the example as much as possible:

<TextBox Text="Border me with my Template">
     <TextBox.Template>
      <ControlTemplate TargetType="{x:Type TextBox}">
       <Border BorderBrush="Green" BorderThickness="1">
        <AdornedElementPlaceholder/>
       </Border>
      </ControlTemplate>
     </TextBox.Template>
    </TextBox>  

The result is just a green box around the space that should be my textbox!

+1  A: 

Unfortunately, that's just not how it works. However, it's not much more difficult than that. If you want to add a green border around a text box by changing the control template, it's going to look a bit more like this:

<ControlTemplate TargetType="TextBox">
    <Border x:Name="Border"
            CornerRadius="2"
            Background="{TemplateBinding Background}"
            BorderBrush="Green"
            BorderThickness="1"
            SnapsToDevicePixels="True">
        <ScrollViewer x:Name="PART_ContentHost"/>
    </Border>
</ControlTemplate>

The important part in there is the ScrollViewer named PART_ContentHost. The TextBox looks for that guy when the template is applied and uses it to host the text, caret, etc. What I think you're missing is that when you override the Template for an element, you're overriding the entire control template. It's unfortunate that you can't just change bits and pieces, but that's the truth of the matter.

Of course, if you want to maintain the original look and feel of the TextBox, such that it still looks like a Win7 TextBox for instance, you'd need to do a bit more in the ControlTemplate.

For what it's worth, it looks like the template you were trying to apply would work if you're talking about using an Adorner. It's similar to how the validation templates work in WPF, but that's a whole-nother story.

Oh, and for a much simpler way to change the border to green, you should be able to just set the BorderBrush property on the TextBox itself. Of course, I really don't know exactly what you're going for.

-- HTH Dusty

dustyburwell
What I really want to do is to make a balloon popup that enables me to write more text into a text box than I have space for in the UI. I would like to do this while still being able to set the original properties of the textbox in xaml, and I'm not sure I can do that if I make a custom control with a "Text" dependency property (I would loose the Forecolor, fontstrength and so on. I just made the border-example to make it simple. Thank's for the reply! I will have a go at it!
Tau
This probably works, but I need a referenct to the original textbox to make some mouse-over events. I probably need to do a usercontrol where i inherit from a textbox? I would just have been neat if it worked with the AdornedElementPlaceholder.
Tau
Using a Custom control based on TextBox is likely the most straightforward way to accomplish what you're going for. An alternative may be to actually use an Adorner: http://msdn.microsoft.com/en-us/library/ms743737.aspx They're actually a very powerful concept. Maybe, if you had a screen shot of something similiar to what you're trying to do I could help a little more.
dustyburwell
A: 

I ended up doing a Custom Control based on a HeaderedContent Control.

It will allow the user to click or hoover over some content and then show a bubble containing some other or the same content.

Usage:

<JsCustomControls:BaloonBox LabelText="{Binding Note}" WaterMark="Click to write Note" Type="ClickToShow">
<JsCustomControls:BaloonBox.Content>
<TextBox AcceptsReturn="True" Text="{Binding Path=Note}" TextWrapping="Wrap"></TextBox>
</JsCustomControls:BaloonBox.Content>
</JsCustomControls:BaloonBox>

The code for the User control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;

namespace JsCustomControls
{
    public enum BaloonBoxTypeEnum
    {
        ClickToShow,
        MouseOverToShow,
        Manual
    }

    [TemplatePart(Name = BaloonBox.HeaderElement, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = BaloonBox.ContentElement, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = BaloonBox.PopUpElement, Type = typeof(Popup))]
    [TemplatePart(Name=BaloonBox.LabelElement,Type=typeof(Label))]
    public class BaloonBox : HeaderedContentControl
    {
        DispatcherTimer PopupTimer = new DispatcherTimer();

        private const string HeaderElement = "PART_HeaderContentControl";
        private const string ContentElement = "PART_ContenContentControl";
        private const string PopUpElement = "PART_PopUp";
        private const string LabelElement = "PART_HeaderLabel";

        private ContentPresenter headerContentControl;
        private ContentPresenter contentContentControl;
        private Popup popUp;
        private Label headerLabel;

        static BaloonBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BaloonBox), new FrameworkPropertyMetadata(typeof(BaloonBox))); 
        }
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            headerContentControl = GetTemplateChild(HeaderElement) as ContentPresenter;
            contentContentControl = GetTemplateChild(ContentElement) as ContentPresenter;
            popUp = GetTemplateChild(PopUpElement) as Popup;
            headerLabel = GetTemplateChild(LabelElement) as Label;
            if (headerContentControl != null) headerContentControl.MouseDown += new MouseButtonEventHandler(headerContentControl_MouseDown);
            if(headerLabel!=null)headerLabel.MouseDown+=new MouseButtonEventHandler(headerContentControl_MouseDown);
            if (headerContentControl != null) headerContentControl.MouseMove += new MouseEventHandler(headerContentControl_MouseMove);
            if(headerLabel!=null)headerLabel.MouseMove+=new MouseEventHandler(headerContentControl_MouseMove);
            PopupTimer = new DispatcherTimer();
            PopupTimer.Tick += new EventHandler(PopupTimer_Tick);
            if(string.IsNullOrEmpty(LabelText))
            {
                if (headerLabel != null) headerLabel.Foreground = Brushes.Gray;
                if (headerLabel != null) headerLabel.Content = WaterMark;
            }
            else
            {
                if (headerLabel != null) headerLabel.Foreground = Brushes.Black;
                if (headerLabel != null) headerLabel.Content = LabelText;
            }
        }

        void headerContentControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (Type == BaloonBoxTypeEnum.MouseOverToShow)
            {
                if (popUp != null) popUp.IsOpen = true;
                PopupTimer.Interval = new TimeSpan(0, 0, 0, 2);
                PopupTimer.Start();
            }
        }
        void headerContentControl_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (Type == BaloonBoxTypeEnum.ClickToShow)
            {
                if (popUp != null) popUp.IsOpen = true;
                PopupTimer.Interval = new TimeSpan(0, 0, 0, 3);
                PopupTimer.Start();
            }

        }
        void PopupTimer_Tick(object sender, EventArgs e)
        {
            if (!headerContentControl.IsMouseOver && !contentContentControl.IsMouseOver && !contentContentControl.IsKeyboardFocusWithin)
            {
                PopupTimer.Stop();
                popUp.IsOpen = false;
            }
        }

        public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen",
                                                                                               typeof (bool),
                                                                                               typeof (BaloonBox),
                                                                                               new FrameworkPropertyMetadata
                                                                                                   (new PropertyChangedCallback
                                                                                                        (OnIsOpenChanged)));
        public bool IsOpen
        {
            get { return (bool) GetValue(IsOpenProperty); }
            set{SetValue(IsOpenProperty,value);}
        }

        private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BaloonBox baloonBox = (BaloonBox)d;
            baloonBox.popUp.IsOpen =(bool)e.NewValue;          
        }

        public static readonly DependencyProperty WaterMarkProperty = DependencyProperty.Register("WaterMark",
                                                                                                  typeof (string),
                                                                                                  typeof (BaloonBox),
                                                                                                  new FrameworkPropertyMetadata
                                                                                                      (new PropertyChangedCallback
                                                                                                           (OnWatermarkChanged)));


        public string WaterMark
        {
            get { return (string)GetValue(WaterMarkProperty); }
            set { SetValue(WaterMarkProperty,value); }
        }

        private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        public static readonly DependencyProperty LabelTextProperty = DependencyProperty.Register("LabelText",
                                                                                                  typeof (string),
                                                                                                  typeof (BaloonBox)
                                                                                                  ,new FrameworkPropertyMetadata(new PropertyChangedCallback(OnLabelTextChanged)));

        public string LabelText
        {
            get { return (string) GetValue(LabelTextProperty); }
            set
            {
                SetValue(LabelTextProperty,value);
                headerLabel.Content = value;
            }
        }
        private static void OnLabelTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BaloonBox baloonBox = (BaloonBox)d;
            if (string.IsNullOrEmpty(e.NewValue.ToString()))
            {
                if (baloonBox.headerLabel != null) baloonBox.headerLabel.Foreground = Brushes.Gray;
                if (baloonBox.headerLabel != null) baloonBox.headerLabel.Content = baloonBox.WaterMark;
            }
            else
            {
                if (baloonBox.headerLabel != null) baloonBox.headerLabel.Foreground = Brushes.Black;
                if (baloonBox.headerLabel != null) baloonBox.headerLabel.Content = e.NewValue;
            }


        }
        public static readonly DependencyProperty BaloonBoxTypeProperty = DependencyProperty.Register(
            "BaloonBoxType", typeof(BaloonBoxTypeEnum), typeof(BaloonBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnBaloonBoxTypeChanged)));

        public BaloonBoxTypeEnum Type
        {
            get { return (BaloonBoxTypeEnum) GetValue(BaloonBoxTypeProperty);}
            set { SetValue(BaloonBoxTypeProperty,value);}
        }

        private static void OnBaloonBoxTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //who cares? 
        }
    }
}


                    </Border>
                    <Popup x:Name="PART_PopUp" HorizontalOffset="-50" VerticalOffset="-5" AllowsTransparency="True" IsOpen="False" PopupAnimation="Slide" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header}">
                        <Grid Height="150" Width="250" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="2"></ColumnDefinition>
                                <ColumnDefinition Width="0.050*"></ColumnDefinition>
                                <ColumnDefinition Width="0.900*"></ColumnDefinition>
                                <ColumnDefinition Width="0.050*"></ColumnDefinition>
                                <ColumnDefinition Width="2"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="2"></RowDefinition>
                                <RowDefinition Height="0.300*"></RowDefinition>
                                <RowDefinition Height="0.700*"></RowDefinition>
                                <RowDefinition Height="0.100*"></RowDefinition>
                                <RowDefinition Height="2"></RowDefinition>
                            </Grid.RowDefinitions>
                            <Path Grid.Row="1" Grid.RowSpan="3" Grid.Column="1" Grid.ColumnSpan="3" Data="M79,279 L119,279 135,263 135,279 319,279 319,368.5 78.5,368.5 z" Fill="#FFFDFDB3" Stretch="Fill" Stroke="Black" UseLayoutRounding="False">
                            </Path>
                            <ScrollViewer Grid.Row="2" Grid.Column="2" Grid.RowSpan="1" HorizontalScrollBarVisibility="Auto"  VerticalScrollBarVisibility="Auto">
                                <ContentPresenter x:Name="PART_ContenContentControl" Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"/>
                            </ScrollViewer>
                        </Grid>
                    </Popup>
                </Grid>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Tau