In UWP I am trying to change default template of tooltip.
I have managed it, but now I need to set tooltip arrow to point out to the control it belongs to.
My style is defined as this:
<Style TargetType="ToolTip">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundChromeHighBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ToolTipBorderThemeThickness}" />
<Setter Property="FontFamily" Value="Roboto" />
<Setter Property="FontSize" Value="{ThemeResource ToolTipContentThemeFontSize}" />
<Setter Property="Padding" Value="40,40,40,35"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Grid Background="Transparent">
<Grid
MinWidth="100"
MinHeight="90"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Padding="15"
Background="Transparent">
And so on and on...
But now I am trying to make UserControl bind with TemplateBinding property.
I have created UserControl that have some dependecncy property.
Like this:
public PlacementMode TooltipPlacement
{
get { return (PlacementMode)GetValue(TooltipPlacementProperty); }
set { SetValue(TooltipPlacementProperty, value); CalculateArrowVisibility(); }
}
public static readonly DependencyProperty TooltipPlacementProperty =
DependencyProperty.Register("TooltipPlacement", typeof(PlacementMode), typeof(ArrowDown), null);
CalculateArrowVisibility() is a method that will calculate the arrow location depending on TooltipPlacement.
And that control is in style defined as this:
<local:ArrowDown x:Name="arrow" TooltipPlacement="{TemplateBinding Placement}"/>
But it is not bound, I have tried other TemplateProperties but no luck also.
Where is the problem here?
You are very close excpet the way you delcare the dependency property is wrong.
You should never modify its getter and setter. Instead, call your CalculateArrowVisibility method inside its property changed callback like this -
public PlacementMode TooltipPlacement
{
get => (PlacementMode)GetValue(TooltipPlacementProperty);
set => SetValue(TooltipPlacementProperty, value);
}
public static readonly DependencyProperty TooltipPlacementProperty =
DependencyProperty.Register("TooltipPlacement", typeof(PlacementMode), typeof(ArrowDown),
new PropertyMetadata(null, TooltipPlacementChangedCallback));
private static void TooltipPlacementChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var self = (ArrowDown)d;
self.CalculateArrowVisibility();
}
Related
I know there are many posts dealing with this topic, but none that address my specific need. I want to define an override to the default style of the WPF Textbox ContextMenu. My application is global with user defined multi-language. That means all the literals in my app are really buttons and the user can enable a setting which allows a button click to display a dialog to enter the text for different languages. This works throughout my app including other ContextMenus. However, my solution for the Textbox context menu does everything right but will not close when the button is pressed. How can I get the buttons in the MenuItem style/template to close the Context Menu? Here is the style for the ContextMenu.
<Style x:Key="MyContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Background" Value="{DynamicResource MyPopupBackgroundBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource MyBorderBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border x:Name="Border" Background="{TemplateBinding Background}" BorderThickness="1"
CornerRadius="6" BorderBrush="{TemplateBinding BorderBrush}">
<ScrollViewer x:Name="ScrollViewer" VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Hidden" Margin="4,4,4,4">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MyMenuItemStyle" TargetType="{x:Type ctls:MyMenuItem}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="StaysOpenOnClick" Value="False"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="#FF494949"/>
<Setter Property="ButtonImageSource" Value="{StaticResource EditBlue}" />
<Setter Property="MaxImageSize" Value="20" />
<Setter Property="ButtonText" Value="Text" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ctls:MyMenuItem}">
<Grid Margin="0,2,0,0">
<ctls:MyBasicButton Style="{StaticResource MyImageAndTextButtonStyle}"
HorizontalAlignment="Left" FontSize="16"
ButtonName="{TemplateBinding ButtonName}"
CollectionKey="{TemplateBinding CollectionKey}"
Background="Transparent" BorderThickness="0"
Content="{TemplateBinding ButtonText}"
CanUseAlternateText="{TemplateBinding CanUseAlternateText}"
Image="{TemplateBinding ButtonImageSource}"
MaxImageSize="{TemplateBinding MaxImageSize}"
Command="{Binding Path=Command, RelativeSource={RelativeSource TemplatedParent}}">
</ctls:MyBasicButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is the resource for the ContextMenu template.
<system:String x:Key="CopyString">Copy</system:String>
<system:String x:Key="CutString">Cut</system:String>
<system:String x:Key="PasteString">Paste</system:String>
<ContextMenu x:Key="CutCopyPasteContextMenu" Style="{StaticResource MyContextMenuStyle}">
<ctls:MyMenuItem Style="{StaticResource MyMenuItemStyle}"
Command="ApplicationCommands.Cut" InputGestureText=""
StaysOpenOnClick="False"
ButtonImageSource="{StaticResource CutBlue}"
ButtonText="{Binding Source={StaticResource CutString}, Converter={StaticResource StringToMenuItemText}}">
</ctls:MyMenuItem>
<ctls:MyMenuItem Style="{StaticResource MyMenuItemStyle}"
Command="ApplicationCommands.Copy" InputGestureText=""
StaysOpenOnClick="False"
ButtonImageSource="{StaticResource CopyBlue}"
ButtonText="{Binding Source={StaticResource CopyString}, Converter={StaticResource StringToMenuItemText}}">
</ctls:MyMenuItem>
<ctls:MyMenuItem Style="{StaticResource MyMenuItemStyle}"
Command="ApplicationCommands.Paste" InputGestureText=""
StaysOpenOnClick="False"
ButtonImageSource="{StaticResource PasteBlue}"
ButtonText="{Binding Source={StaticResource PasteString}, Converter={StaticResource StringToMenuItemText}}">
</ctls:MyMenuItem>
</ContextMenu>
I have a custom MenuItem control with dependency properties that support binding between my custom Button control and the MenuItem. Here is that code.
public class MyMenuItem : MenuItem
{
public string ButtonName
{
get { return (string)base.GetValue(ButtonNameProperty); }
set { SetValue(ButtonNameProperty, value); }
}
public static readonly DependencyProperty ButtonNameProperty =
DependencyProperty.Register("ButtonName", typeof(string), typeof(MyMenuItem));
public string ButtonText
{
get { return (string)base.GetValue(ButtonTextProperty); }
set { SetValue(ButtonTextProperty, value); }
}
public static readonly DependencyProperty ButtonTextProperty =
DependencyProperty.Register("ButtonText", typeof(string), typeof(MyMenuItem));
public ImageSource ButtonImageSource
{
get { return GetValue(ButtonImageSourceProperty) as ImageSource; }
set { SetValue(ButtonImageSourceProperty, value); }
}
public static readonly DependencyProperty ButtonImageSourceProperty =
DependencyProperty.Register("ButtonImageSource", typeof(ImageSource), typeof(MyMenuItem));
public double MaxImageSize
{
get { return (double)base.GetValue(MaxImageSizeProperty); }
set { base.SetValue(MaxImageSizeProperty, value); }
}
public static readonly DependencyProperty MaxImageSizeProperty =
DependencyProperty.Register("MaxImageSize", typeof(double), typeof(MyMenuItem));
public string CollectionKey
{
get { return (string)base.GetValue(CollectionKeyProperty); }
set { base.SetValue(CollectionKeyProperty, value); }
}
public static readonly DependencyProperty CollectionKeyProperty =
DependencyProperty.Register("CollectionKey", typeof(string), typeof(MyMenuItem));
public bool CanUseAlternateText
{
get { return (bool)GetValue(CanUseAlternateTextProperty); }
set { SetValue(CanUseAlternateTextProperty, value); }
}
public static readonly DependencyProperty CanUseAlternateTextProperty =
DependencyProperty.Register("CanUseAlternateText", typeof(bool), typeof(MyMenuItem));
}
Finally, Here is the style for my Textbox using my custom ContextMenu named CutCopyPasteContextMenu.
<Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Background" Value="{DynamicResource MyDataLabelBackgroundBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource MyTextBrush}"/>
<Setter Property="CaretBrush" Value="{DynamicResource MyTextBrush}"/>
<Setter Property="SelectionBrush" Value="{DynamicResource MySelectedBrush}"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="{DynamicResource MyMainFont}" />
<Setter Property="Height" Value="28"/>
<Setter Property="Padding" Value="0,0,4,0"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource MyBorderBrush}"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="Continue"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="IsReadOnly" Value="False"/>
<Setter Property="ToolTipService.InitialShowDelay" Value="2000"/>
<Setter Property="ToolTipService.BetweenShowDelay" Value="0"/>
<Setter Property="ToolTipService.ShowDuration" Value="60000"/>
<Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
<Setter Property="ContextMenu" Value="{StaticResource CutCopyPasteContextMenu}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" CornerRadius="4"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false"
Margin="4,-1,0,0"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{DynamicResource MyDisabledBackgroundBrush}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="true"/>
<Condition Property="IsReadOnly" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource MyBorderHighlightBrush}"/>
</MultiTrigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource MyBorderHighlightBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
Here is what the ContextMenu looks like when the app is run. All the menu item functions work correctly and I am allowed to define the dynamic language text for the menu item buttons as I designed.
Can someone please tell me why the ContextMenu will not close? I think it might have something to do with my Button control absorbing the click event of the MenuItem but I am just guessing.
I've created a custom control called ActionButton inherited from Button. I've added a couple of dependency properties which work perfectly, however, I cannot get the binding to the Command property to work. When the application runs, the Command property always returns null.
Could someone please tell me what I'm doing wrong?
Here's some of the code which I hope should suffice.
// Custom control
public class ActionButton : Button
{
static ActionButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ActionButton), new FrameworkPropertyMetadata(typeof(ActionButton)));
}
// Some dependency properties go here
}
// In Generic.xaml
<Style TargetType="{x:Type controls:ActionButton}">
<Setter Property="Width" Value="48"/>
<Setter Property="Height" Value="48"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:ActionButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Border.ToolTip>
<StackPanel Margin="{DynamicResource ToolTipMargin}" MaxWidth="{DynamicResource MaxToolTipWidth}">
<TextBlock Style="{DynamicResource ToolTipHeaderStyle}" Text="{TemplateBinding ToolTipHeader}"/>
<Separator Visibility="Hidden"/>
<TextBlock Style="{DynamicResource ToolTipContentStyle}" Text="{TemplateBinding ToolTipText}"/>
</StackPanel>
</Border.ToolTip>
<Grid>
<Ellipse x:Name="BackEllipse" Stroke="{DynamicResource MahApps.Brushes.Accent}" StrokeThickness="0" Fill="{DynamicResource MahApps.Brushes.Accent}"/>
<Ellipse x:Name="FillEllipse" Stroke="{DynamicResource MahApps.Brushes.Accent}" StrokeThickness="3"/>
<TextBlock x:Name="BlockIconTextBox" Text="{TemplateBinding Icon, Converter={StaticResource FontIconConverter}}" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="BackEllipse" Property="Opacity" Value="0.6"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="BackEllipse" Property="Opacity" Value="0.0"/>
<Setter TargetName="BlockIconTextBox" Property="Opacity" Value="0.6"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="FillEllipse" Property="Opacity" Value="0.3"/>
<Setter TargetName="BlockIconTextBox" Property="Opacity" Value="0.3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
// View base class...
public class View : UserControl
{
public View()
{
ActionButtons = new ObservableCollection<ActionButton>();
}
public static readonly DependencyProperty ActionButtonsProperty = DependencyProperty.Register(nameof(ActionButtons), typeof(ObservableCollection<ActionButton>), typeof(View));
public ObservableCollection<ActionButton> ActionButtons
{
get => (ObservableCollection<ActionButton>)GetValue(ActionButtonsProperty);
set => SetValue(ActionButtonsProperty, value);
}
}
// Markup in a view...
<local:View
x:Class="Vesuvius.TeleCalc.Windows.Views.SettingsView"
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:Vesuvius.TeleCalc.Windows.Views"
xmlns:viewModels="clr-namespace:Vesuvius.TeleCalc.Windows.ViewModels"
xmlns:controls="clr-namespace:Vesuvius.TeleCalc.Windows.Controls"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="1024"
Title="Settings"
>
<local:View.DataContext>
<viewModels:SettingsViewModel />
</local:View.DataContext>
<local:View.ActionButtons >
<!-- This is where things start to go wrong (I think) -->
<controls:ActionButton Icon="Color" ToolTipHeader="Reset theme" ToolTipText="Reset theme to default values." Command="{Binding ResetThemeCommand}"/>
</local:View.ActionButtons>
<!-- I have removed the rest for brevity -->
// In SettingsViewModel...
public SettingsViewModel()
{
ResetThemeCommand = CommandFactory.Create(ResetTheme);
}
public ICommand ResetThemeCommand { get; }
private void ResetTheme(object parameter)
{
// Do stuff here
}
// The issue...
public partial class SettingsView
{
public SettingsView()
{
InitializeComponent();
// The Command of SettingsViewModel.ResetThemeCommand is always null, so I have to resort to this nasty hack...
ActionButtons[0].Command = ((SettingsViewModel)DataContext).ResetThemeCommand;
// It's also worth noting, that the dependency properties ToolTipHeader and ToolTipText of the ResetThemeCommand are working properly.
}
}
this is my button code
public class MetroButton : Button
{
public static readonly DependencyProperty MoseOverBrushProperty;
public static readonly DependencyProperty PressedBrushProperty;
public MetroButton():base()
{
var resource = new ResourceDictionary
{
Source = new Uri("/Parking.Component.Ui;component/Styles/ButtonMetro.xaml",
UriKind.RelativeOrAbsolute)
};
Style = resource["ButtonMetro"] as Style;
//SetResourceReference(StyleProperty, Style);
}
static MetroButton()
{
MoseOverBrushProperty = DependencyProperty.Register("MoseOverBrush", typeof(Brush), typeof(MetroButton));
PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(MetroButton));
}
public Brush MoseOverBrush
{
get { return (Brush)base.GetValue(MoseOverBrushProperty); }
set { base.SetValue(MoseOverBrushProperty, value); }
}
public Brush PressedBrush
{
get { return (Brush)base.GetValue(PressedBrushProperty); }
set { base.SetValue(PressedBrushProperty, value); }
}
}
and I use this style for my button
<Style x:Key="ButtonMetro" TargetType="{ x:Type LochalUI:MetroButton}">
<Setter Property="Foreground" Value="White" />
<Setter Property="MoseOverBrush" Value="#FF3F62FD"/>
<Setter Property="PressedBrush" Value="#FF000099"/>
<Setter Property="Background" Value="#FF6B9AFF"/>
<Setter Property="FontSize" Value="15" />
<Setter Property="FontFamily" Value="B Yekan" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type LochalUI:MetroButton}">
<Border x:Name="border" CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding Path=PressedBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
but the problem is there when i put color for my button background like below code:
<UI:MetroButton HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="132" Height="107" Background="#FF09CD00" >
<Grid>
<Label Content="تنظیمات" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5,5,5,12" Foreground="White" Margin="5"/>
</Grid>
</UI:MetroButton>
the IsMouseOver changer color and IsPressed Triggers not work.
(I don't want use static resource in my setters)
Changing other properties has no effect just changing background made this problem.
I found the answer problem was in 2 place:
first one when we use trigger in
<ControlTemplate.Triggers/>
you have sure you set your setter set property on the currect object
and the secend one is in the binding we have change
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}"
to
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource TemplatedParent}}"
because MoseOverBrush is parrent property not the ControlTemplate property
I have a dependency property in my user control called IsPromptShown:
public static DependencyProperty IsPromptShownProperty = DependencyProperty.
Register("IsPromptShown", typeof(bool), typeof(AutoCompleteSearchBox), new
PropertyMetadata(true));
public bool IsPromptShown
{
get { return (bool)GetValue(IsPromptShownProperty); }
set { SetValue(IsPromptShownProperty, value); }
}
That custom control contains a TextBox. That text box doesn't have any value assigned to its Text property:
<TextBox Name="_searchTextBox" Grid.Row="0" VerticalAlignment="Top"
GotFocus="SearchTextBox_GotFocus" LostFocus="SearchTextBox_LostFocus"
TextChanged="SearchTextBox_TextChanged"/>
Now I'm setting the following trigger in the hosting window:
<Trigger Property="IsPromptShown" Value="True">
<!--<Setter Property="FontStyle" Value="Italic"/>-->
<Setter Property="TextBox.Text" Value="Seek"/>
</Trigger>
The commented line that sets FontStyle works but the second that sets TextBox.Text doesn't. I also have been trying to set Foreground property and that also have failed. What is going on?
I had problems similar to this when I started using WPF. You just have to look at things in a different way. Instead of looking at the value of the IsPromptShown property and trying to change a property of the TextBox in a Trigger in the UserControl, do it the other way around. Look at the value of the IsPromptShown property and try to change the property of the TextBox in a Trigger in the TextBox.
<TextBox Name="_searchTextBox" Grid.Row="0" VerticalAlignment="Top"
GotFocus="SearchTextBox" LostFocus="SearchTextBox_LostFocus"
TextChanged="SearchTextBox_TextChanged">
<TextBox.Style>
<Style>
<Setter Property="TextBox.Text" Value="Default value if required" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsPromptShown, ElementName=This}"
Value="True">
<Setter Property="TextBox.Text" Value="Seek" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Note that for this to work, you will need to add Name=This to the declaration of your UserControl. This just lets the Framework know where to find the IsPromptShown property... you could just as easily use a RelativeSource Binding for this if you prefer.
I think the Problem is that you can´t access the TextProperty of the Textbox in your UserControl from outside!
Try creating a Text DependencyProperty that sets the Value of the Textbox inside the UserControl and set this Property in the Trigger!
I found the solution. It is not trivial, but works. Assume that we have a custom UserControl that contains a TextBox and ather controls. We want to be capable of assigning style to each internal control depending on some bool UserControl.IsSomething dependency property. First we have to declare another dependency property Style UserControl.AlternativeStyle. Then we attach an event handler to IsSomething in order to toggle current Style and AlternativeStyle when IsSomething changed:
public static DependencyProperty AlternativeStyleProperty =
DependencyProperty.Register(
"AlternativeStyle",
typeof(Style),
typeof(MyUserControl));
public Style AlternativeStyle
{
get { return (Style)GetValue(AlternativeStyleProperty); }
set { SetValue(AlternativeStyleProperty, value); }
}
public static DependencyProperty IsSomethingProperty =
DependencyProperty.Register(
"IsSomething",
typeof(bool),
typeof(MyUserControl),
new PropertyMetadata(true, IsSomethingProperty_Changed));
public bool IsSomething
{
get { return (bool)GetValue(IsSomethingProperty); }
set { SetValue(IsSomethingProperty, value); }
}
private static void IsSomethingProperty_Changed(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// Swap styles
// e.g.
// var tempStyle = Style;
// Style = AlternativeStyle;
// AlternativeStyle = tempStyle;
}
The harder part is about setting AlternativeStyle outside the custom UserControl. Below is how to achieve that in XAML of window that hosts our custom control:
<Window.Resources>
<Style x:Key="DefaultTextBoxStyle" TargetType="TextBox">
<Setter Property="Background" Value="Red"/>
</Style>
<Style x:Key="DefaultUserControlStyle" TargetType="local:MyUserControl">
<Style.Resources>
<Style TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
<Setter Property="Background" Value="Blue"/>
</Style>
</Style.Resources>
</Style>
<Style x:Key="AlternativeUserControlStyle" TargetType="local:MyUserControl" BasedOn="{StaticResource DefaultUserControlStyle}">
<Style.Resources>
<Style TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
<Setter Property="Background" Value="Green"/>
</Style>
</Style.Resources>
</Style>
<Style TargetType="local:MyUserControl" BasedOn="{StaticResource DefaultUserControlStyle}">
<Setter Property="AlternativeStyle" Value="{StaticResource AlternativeUserControlStyle}"/>
</Style>
</Window.Resources>
The above style will be added automaticly to all instances of MyUserControl and when we change IsSomething value, then alternative style will be applied to the owner of changed IsSomething.
I've got an editable ComboBox, and when text is added which is too long, it appears like this:
How can I make the textbox start from the beginning of the string?
TextBox txt = sender as TextBox;
txt.Text = "[Children]";
<Style TargetType="TextBox">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="BorderBrush" Value="Silver"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border" Padding="1" Background="#FFFFFF" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#EEEEEE"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#EEEEEE"/>
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ComboBox:
ComboBox cmbValue1 = new ComboBox();
cmbValue1.IsTextSearchEnabled = false;
cmbValue1.IsEditable = true;
cmbValue1.Width = 70;
TextBox txtEdit = (TextBox)((sender as ComboBox).Template.FindName("PART_EditableTextBox", (sender as ComboBox)));
txtEdit.Tag = selection;
You need to set the selection start back to 0 in order to make it "left aligned" once the user types something in.
Do this via the TextBoxBase.PreviewLostKeyboardFocus event - here's some XAML:
<ComboBox Name="ComboBox1"
IsEditable="True"
TextBoxBase.PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus_1">
And the event itself:
private void TextBox_PreviewLostKeyboardFocus_1(object sender, KeyboardFocusChangedEventArgs e)
{
TextBox txtEdit = (TextBox)((sender as ComboBox).Template.FindName("PART_EditableTextBox", (sender as ComboBox)));
txtEdit.SelectionStart = 0;
}
I think this should give enough for you to adapt to your application. I tested in an empty WPF app and it works whether you press TAB or click on another part of the UI with the mouse.
Edit:
Here's how to add the event in code:
Not sure how your app is structured, but again, this works for me:
private void Window_Initialized_1(object sender, EventArgs e)
{
ComboBox cmbValue1 = new ComboBox();
cmbValue1.IsTextSearchEnabled = false;
cmbValue1.IsEditable = true;
cmbValue1.Width = 70;
cmbValue1.PreviewLostKeyboardFocus += TextBox_PreviewLostKeyboardFocus_1;
this.MyCanvas.Children.Add(cmbValue1);
}
(Using same event handler I posted above in this answer)
Edit:
Here is a link to the working project: Note -- it works if the text in editable ComboBox is not selected when you lose focus (i.e., after you type something or just unselect). I'll try and fix that.
http://23.23.250.9/wpfresource.zip