I trying to create a custom textbox control in windows 8 XAML.
I've right clicked on my project -> Add -> New Item
I've then selected Templated Control and entered the name MyTextBox
I have then made this class derive from TextBox and added a test Method called Hello. So it now looks like this:
public sealed class MyTextBox : TextBox
{
public MyTextBox()
{
this.DefaultStyleKey = typeof(MyTextBox);
}
public void Hello()
{
//Do something here!
}
}
Within my project a file has also been added called Generic.xaml with the following style:
<Style TargetType="local:MyTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyTextBox">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So within here I tried adding BasedOn="TextBox" with the Style tag:
<Style TargetType="local:WatermarkTextBox" BasedOn="TextBox">
This doesn't work.
What do I need to do to create this custom TextBox and then how do I use it within my XAML
This is for a Windows RT so the XAML maybe different from WPF.
One of best article and my favorite : Building a deployable custom control for XAML Metro style apps
Here is MSDN sample app : XAML user and custom controls sample
UPDATE 1 :
<Style TargetType="local:WatermarkTextBox" BasedOn="TextBox">
You don't have to specify BasedOn attribute. If you are developing watermark textbox then I would recommend you to check Callisto's watermark textbox code.
Generic.xaml
WatermarkTextBox.cs
BasedOn values can be pointed through StaticResources like BasedOn={StaticResource DefaultTextBoxStyle}
I have a NumericOnlyTextBox implementation. Instead of making it BasedOn textbox I've added a textbox and all its dependencies.
In example:
<Style TargetType="local:NumericOnlyTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NumericOnlyTextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<TextBox x:Name="TextBoxPart"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EnteredText, Mode=TwoWay}"
MaxLength="{TemplateBinding MaxLength}">
</TextBox>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsTabStop" Value="False"></Setter>
</Style>
Related
I'm trying to create a control that uses a RevealBorderBrush as its border brush in XAML. I want to use the correct TargetTheme value for the brush, so I'm trying to bind to my application's current ActualTheme value. I'm using a templated control to do this. My C# code behind file is just the empty constructor that inherits from Control and sets the default style key. The following is my Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Plank">
<Style TargetType="local:PlankPL" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:PlankPL">
<Border
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}">
<Border.BorderBrush>
<RevealBorderBrush TargetTheme="{Binding Source=local:App, Path=ActualTheme}"/>
</Border.BorderBrush>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I'm pretty sure the binding statement is incorrect, but I'm not sure how to write it.
I haven't tested this but it should work. Name your control then use the below code, replace ControlTemplateName with what you used.
You can’t bind background data in ResourceDictionary, it doesn’t work.
It is recommended to write a UserControl and bind the RevealBorderBrush property of Border like following.
MyUserControl1.xaml:
<UserControl..>
<StackPanel>
<Border BorderThickness="3">
<Border.BorderBrush>
<RevealBorderBrush TargetTheme="{x:Bind theme1}"/>
</Border.BorderBrush>
</Border>
<TextBox Text = "text demo text "/>
</StackPanel>
</UserControl>
MyUserControl1.xaml.cs:
public sealed partial class MyUserControl1 : UserControl
{
private ApplicationTheme theme1;
public MyUserControl1()
{
this.InitializeComponent();
theme1 = Application.Current.RequestedTheme;
}
}
I have designed some WPF CustomControl and I wrote a ControlTemplate for them, and i assigned ControlTemplates to those controls through styles :
for Example:
public class BootstrapText : TextBox
{
protected override void OnLostFocus(RoutedEventArgs e)
{
// ...
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
// ...
base.OnTextChanged(e);
}
public static readonly DependencyProperty RegexProperty = DependencyProperty.Register("Regex", typeof(string), typeof(BootstrapText), new PropertyMetadata(null));
public bool IsValid
{
// ...
}
public string Regex
{
// ...
}
static BootstrapText()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BootstrapText), new FrameworkPropertyMetadata(typeof(BootstrapText)));
}
}
and ControlTemplate (Actually Style) is Something like:
<Style TargetType="Controls:BootstrapText" BasedOn="{StaticResource FontBase}">
<Setter Property="Padding" Value="2"/>
<Setter Property="Width" Value="240"/>
<Setter Property="SnapsToDevicePixels" Value="True"></Setter>
<Setter Property="Background" Value="#FEFEFE"/>
<!--<Setter Property="VerticalContentAlignment" Value="Center"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Controls:BootstrapText">
<Grid>
<Border Name="container" HorizontalAlignment="Left" Padding="{TemplateBinding Padding}" Height="{TemplateBinding Height}" BorderThickness="1" BorderBrush="#ccc" Background="{TemplateBinding Background}">
<ScrollViewer Padding="0" x:Name="PART_ContentHost" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" TextBlock.TextAlignment="Center" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
While i have no x:Key="SomeKey" in the style. this style will affects all BootstrapTexts.
This is Good. But I have another problem. Somewhere in a Form, I want to Style these Controls and Set some Margin and Padding for them but leave the defaults as they are. but my Control will be disappeared !!!
I think twice styling will hides the default style.
In the standard Controls like TextBox, Button, ... whenever we write some style, everything is good and style simply sets our properties.
I think, I must assign my ControlTemplate directly to the CustomControl Because i must get ride of this Styling process. but I don't know how ?
A default Style for a control should be define in a ResourceDictionary called generic.xaml and this ResourceDictionary should be located in a folder called Themes at the root of the assembly in which the control is defined by default.
These names are by convention so just move your Style to Themes/generic.xaml.
How can I change the border width of a combobox programatically.
I tried :
combobox.BorderWidth = ...
but It did not work
Ilan
I found out from your answers the following solution :
<Window.Resources>
<Style x:Key="ComboBoxStyleKey" x:Name="ComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Border x:Name="ContentPresenterBorder"
BorderBrush="{TemplateBinding Property=BorderBrush}"
BorderThickness="{TemplateBinding Property=BorderThickness}"
Background="{TemplateBinding Property=Background}"
CornerRadius="3">
<Grid>
<ToggleButton x:Name="DropDownToggle" />
<ContentPresenter x:Name="ContentPresenter" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Using this style I managed to change the border thickness
But now I have a new problem
The ToggleButton and the ContentPresenter does not work.
I want them to have the default behavior.
Is there a way to assign the default behavior to them (Something like style="default style")?
Thanks,
You can do it like this,
this.comboBox.BorderThickness = new Thickness(1, 1, 1, 3);
Template
<Style TargetType="{x:Type local:Viewport}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Viewport}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas x:Name="PART_Canvas" IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
And the code in OnApplyTemplate
content = this.Template.FindName("PART_Canvas", this) as FrameworkElement;
the content returns always null, why it doesn't work?
if I replace with this, the program quits directly
content = this.ItemsPanel.FindName("PART_Canvas", this) as FrameworkElement;
With FindName you can find only elements declared in a Template. ItemsPanel is not part of that template. ItemsControl puts ItemsPanel into ItemsPresenter place holder via which you can access your Canvas but first you need to name ItemsPresenter in your template:
<ControlTemplate TargetType="{x:Type local:Viewport}">
<Border>
<ItemsPresenter x:Name="PART_ItemsPresenter"/>
</Border>
</ControlTemplate>
then, using VisualTreeHelper get your Canvas, but I think earliest place when you can call code below is when FrameWorkElement is Loaded. This is my example:
public class MyListBox : ListBox
{
public MyListBox()
{
AddHandler(FrameworkElement.LoadedEvent, new RoutedEventHandler(ControlIsLoaded));
}
private void ControlIsLoaded(object sender, RoutedEventArgs e)
{
var canvas = VisualTreeHelper.GetChild(this.Template.FindName("PART_ItemsPresenter", this) as DependencyObject, 0);
}
}
I'm trying to access at a Control in a Template. For this, I redefined control CalendarDayButton:
<Window.Resources>
<Style x:Key="myStyleDayButtonCalendar" TargetType="{x:Type CalendarDayButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CalendarDayButton}">
<Grid Name="gridCalendar">
<ContentControl Margin="5,1,5,1" Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Calendar CalendarDayButtonStyle="{StaticResource myStyleDayButtonCalendar}" Name="myCalendar" SelectedDatesChanged="Calendar_SelectedDatesChanged_1" />
</Grid>
It's OK for that. But when I want to access at my control GRID. Impossible:
private void Calendar_SelectedDatesChanged_1(object sender, SelectionChangedEventArgs e)
{
Grid gridInTemplate = (Grid)myCalendar.Template.FindName("gridCalendar", myCalendar) as Grid;
}
My grid is null. So, I tried with an other Control. With a Button:
<Window.Resources>
<Style x:Key="myStyleButton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Name="myButton">
<Ellipse Fill="DarkBlue"></Ellipse>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Calendar CalendarDayButtonStyle="{StaticResource myStyleDayButtonCalendar}" Name="myCalendar" SelectedDatesChanged="Calendar_SelectedDatesChanged_1" />
<Button Style="{StaticResource myStyleButton}" Name="myButton2" Margin="92,99,518,338" Click="myButton2_Click_1"></Button>
</Grid>
Code behind:
private void myButton2_Click_1(object sender, RoutedEventArgs e)
{
Grid gridInTemplate = (Grid)myButton2.Template.FindName("myButton", myButton2);
}
And here gridInTemplate is NOT NULL. Why in the case dayCalendarButton gridInTemplate is NULL? I would like to avoid to use a VisualTreeHelper.
In your case, you're trying to find a CalendarDayButton in Calendar. But these are two different controls. For example, we have a style for the calendar:
<Style TargetType="{x:Type Calendar}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Calendar}">
<StackPanel x:Name="PART_Root" HorizontalAlignment="Center">
<CalendarItem x:Name="PART_CalendarItem" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In the code we can access to StackPanel like that:
private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
StackPanel StackPanelInTemplate = (StackPanel)MyCalendar.Template.FindName("PART_Root", MyCalendar) as StackPanel;
MessageBox.Show(StackPanelInTemplate.Name);
}
But it is in the calendar style not exists CalendarDayButton. This a separate control. At the CalendarDayButton we can only get style:
Style MyCalendarDayButton = (Style)MyCalendar.CalendarDayButtonStyle;
But not a template. The construction of the form:
CalendarDayButton MyCalendarDayButton = FindChild<CalendarDayButton>(MyCalendar, "gridCalendar");
It will not work. Does not work for the reason that it is not in the same visual tree as the calendar.
Conclusion: maybe just access to CalendarDayButton will not work because it is not see in the visual tree of calendar (have designed so developers). Although I may be mistaken. I have a similar problem encountered when working with DatePicker. There can not be simply so to get access to some parts of DatePicker, for example: get access to Watermark - http://matthamilton.net/datepicker-watermark.
The decision depends on why you need it this button. Try to move your functionality in triggers like Control, Style or create your own control, inherited from the CalendarDayButton class (that tiresome). Our use converters, example:
<Grid x:Name="CalendarDayButtonGrid">
<Grid.ToolTip>
<MultiBinding Converter="{StaticResource HighlightDate}">
<MultiBinding.Bindings>
<Binding />
<Binding RelativeSource="{RelativeSource FindAncestor,
AncestorType={x:Type Calendar}}" />
</MultiBinding.Bindings>
</MultiBinding>
</Grid.ToolTip>
<!-- End addition -->
</Grid>
P.S. Here you are told how to use WPF ToolKit access to CalendarDayButton: http://codesticks.wordpress.com/tag/wpf-toolkit/