Какая первая идея приходит на ум, когда кто-то упоминает закругленные углы и 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>
Он будет правильно отображать границу, но не будет серого фона и измененного переднего плана, когда элемент управления явно установлен как неактивный.