Какая первая идея приходит на ум, когда кто-то упоминает закругленные углы и WPF? Возможно Граница . Об этом стоит подумать, но как применить его к элементу управления TextBox ?
Есть два способа достичь того, что вы хотите.
Способ А:
Самым очевидным было бы создание границы вокруг самого элемента управления. Что-то вроде этого:
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Black" Margin="91,192,150,79"> <TextBox Background=”Transparent” BorderThickness="0" Height="35" Name="txtContents" Width="254" /> </Border>
Это должно сделать первоначальный трюк — элемент управления TextBox не имеет границы вокруг него ( свойство BorderThickness установлено в 0), а содержащая его граница устанавливает правильное округление, цвет и толщину.
Выглядит хорошо, но самое интересное происходит, когда вы решаете, что этот конкретный TextBox не должен быть включен, поэтому вы устанавливаете для свойства IsEnabled значение False.
Что случилось с остальной частью белого пространства между границей и областью письма? Не похоже, что мы хотим, чтобы он вел себя так. И вот где второй способ создания закругленных углов спасает день.
Способ Б:
Это немного сложнее, но дает лучший результат. Он переопределяет шаблон элемента управления по умолчанию для TextBox . Но как мне узнать, как выглядит шаблон по умолчанию?
Поскольку невозможно изменить только одну часть шаблона, весь шаблон должен быть переопределен. Чтобы избежать потери функциональности, я собираюсь получить шаблон по умолчанию и использовать его с небольшими изменениями.
Чтобы получить стиль по умолчанию (который встраивает шаблон элемента управления) для элемента управления TextBox, я использую метод GetStyle :
string GetStyle(Type t)
{
FrameworkElement element = (FrameworkElement)Activator.CreateInstance(t);
object styleName = element.GetValue(FrameworkElement.DefaultStyleKeyProperty);
Style style = Application.Current.TryFindResource(styleName) as Style;
StringWriter stringContainer = new StringWriter();
XmlTextWriter xmlWriter = new XmlTextWriter(stringContainer);
xmlWriter.Formatting = Formatting.Indented;
System.Windows.Markup.XamlWriter.Save(style, xmlWriter);
return stringContainer.ToString();
}
Поскольку я использую элемент управления по умолчанию, без каких-либо пользовательских стилей, я могу просто создать экземпляр TextBox и использовать его тип в качестве параметра для метода GetStyle :
TextBox t = new TextBox(); Debug.Print(GetStyle(t.GetType()));
Вывод должен выглядеть так:
<Style TargetType="TextBox" xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation"">http://schemas.microsoft.com/winfx/2006/xaml/presentation"</a> xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml"">http://schemas.microsoft.com/winfx/2006/xaml"</a> xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:mwt="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<Style.BasedOn>
<Style TargetType="TextBoxBase">
<Style.Resources>
<ResourceDictionary />
</Style.Resources>
<Setter Property="TextElement.Foreground">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.ControlTextBrushKey}" />
</Setter.Value>
</Setter>
<Setter Property="Panel.Background">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.WindowBrushKey}" />
</Setter.Value>
</Setter>
<Setter Property="Border.BorderBrush">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,20" MappingMode="Absolute">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#FFABADB3" Offset="0.05" />
<GradientStop Color="#FFE2E3EA" Offset="0.07" />
<GradientStop Color="#FFE3E9EF" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Border.BorderThickness">
<Setter.Value>
<Thickness>1,1,1,1</Thickness>
</Setter.Value>
</Setter>
<Setter Property="Control.Padding">
<Setter.Value>
<Thickness>1,1,1,1</Thickness>
</Setter.Value>
</Setter>
<Setter Property="UIElement.AllowDrop">
<Setter.Value>
<s:Boolean>True</s:Boolean>
</Setter.Value>
</Setter>
<Setter Property="FrameworkElement.FocusVisualStyle">
<Setter.Value>
<x:Null />
</Setter.Value>
</Setter>
<Setter Property="ScrollViewer.PanningMode">
<Setter.Value>
<x:Static Member="PanningMode.VerticalFirst" />
</Setter.Value>
</Setter>
<Setter Property="Stylus.IsFlicksEnabled">
<Setter.Value>
<s:Boolean>False</s:Boolean>
</Setter.Value>
</Setter>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="TextBoxBase">
<mwt:ListBoxChrome Background="{TemplateBinding Panel.Background}" BorderBrush="{TemplateBinding Border.BorderBrush}" BorderThickness="{TemplateBinding Border.BorderThickness}" RenderMouseOver="{TemplateBinding UIElement.IsMouseOver}" RenderFocused="{TemplateBinding UIElement.IsKeyboardFocusWithin}" Name="Bd" SnapsToDevicePixels="True">
<ScrollViewer Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</mwt:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled">
<Setter Property="Panel.Background" TargetName="Bd">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.ControlBrushKey}" />
</Setter.Value>
</Setter>
<Setter Property="TextElement.Foreground">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
</Setter.Value>
</Setter>
<Trigger.Value>
<s:Boolean>False</s:Boolean>
</Trigger.Value>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.BasedOn>
<Style.Resources>
<ResourceDictionary />
</Style.Resources>
</Style>
Здесь много разметки XAML, но все, что нам нужно, это шаблон элемента управления:
<ControlTemplate TargetType="TextBoxBase">
<mwt:ListBoxChrome Background="{TemplateBinding Panel.Background}" BorderBrush="{TemplateBinding Border.BorderBrush}" BorderThickness="{TemplateBinding Border.BorderThickness}" RenderMouseOver="{TemplateBinding UIElement.IsMouseOver}" RenderFocused="{TemplateBinding UIElement.IsKeyboardFocusWithin}" Name="Bd" SnapsToDevicePixels="True">
<ScrollViewer Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</mwt:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled">
<Setter Property="Panel.Background" TargetName="Bd">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.ControlBrushKey}" />
</Setter.Value>
</Setter>
<Setter Property="TextElement.Foreground">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
</Setter.Value>
</Setter>
<Trigger.Value>
<s:Boolean>False</s:Boolean>
</Trigger.Value>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Теперь оболочку ListBoxChrome следует удалить и вместо нее использовать Border с назначенным свойством CornerRadius . Модифицированный шаблон выглядит так:
<ControlTemplate TargetType="TextBoxBase" x:Key="txt">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Black" x:Name="Bd" Background="{TemplateBinding Panel.Background}">
<ScrollViewer Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled">
<Setter Property="Panel.Background" TargetName="Bd">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.ControlBrushKey}" />
</Setter.Value>
</Setter>
<Setter Property="TextElement.Foreground">
<Setter.Value>
<DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
</Setter.Value>
</Setter>
<Trigger.Value>
<s:Boolean>False</s:Boolean>
</Trigger.Value>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Кроме того, я добавил x: Key к заголовку шаблона, чтобы я мог идентифицировать его в своем приложении. Теперь этот шаблон можно вставить в раздел Ресурсы для приложения WPF. Поскольку я тестирую это в оконном приложении WPF, я вставлю этот шаблон в Windows.Resources .
Также следует добавить ссылку на пространство имен s (используется для триггера IsEnabled ):
xmlns:s="clr-namespace:System;assembly=mscorlib"
Теперь я могу ссылаться на шаблон для TextBox внутри окна:
<TextBox Template="{StaticResource txt}" Background="Transparent" BorderThickness="0" Height="35" Name="textBox1" Width="254" />
Так и должно быть. Если вам не нужно изменять внешний вид элемента управления, когда он отключен, вы можете просто удалить триггер IsEnabled из шаблона, чтобы все, что у вас будет, это:
<ControlTemplate TargetType="TextBoxBase" x:Key="txt">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Black" x:Name="Bd" Background="{TemplateBinding Panel.Background}">
<ScrollViewer Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
Он будет правильно отображать границу, но не будет серого фона и измененного переднего плана, когда элемент управления явно установлен как неактивный.