I created the following xaml:
<Button x:Name="PriceButton">
<Button.Template>
<ControlTemplate>
<Border x:Name="ButtonBorder"
CornerRadius="2"
Background="{StaticResource DarkReflectionBrush}"
BorderBrush="Black"
BorderThickness="1" HorizontalAlignment="Stretch">
<ContentPresenter
VerticalAlignment="Center"
HorizontalAlignment="Left">
<ContentPresenter.Content>
<Grid x:Name="ContentGrid" HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="15*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="Price" Grid.Column="0" FontFamily="Calibri" FontSize="8"
VerticalAlignment="Bottom" Text="{Binding Path=PriceText}"
Foreground="{Binding Path=PriceColor}" ></TextBlock>
<TextBlock x:Name="Main" Grid.Column="1" FontFamily="Calibri" FontSize="14"
Text="{Binding Path=MainText}"
Foreground="{Binding Path=MainTextColor}" />
<TextBlock x:Name="Vol" Grid.Column="2" FontFamily="Calibri" FontSize="8"
VerticalAlignment="Bottom" Text="{Binding Path=VolatilityText}"
Foreground="{Binding Path=VolatilityColor}" />
</Grid>
</ContentPresenter.Content>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsPressed" Value="true">
<Setter TargetName="ButtonBorder" Property="Background" Value="{StaticResource PressedBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Here's the Code Behind:
public static readonly DependencyProperty PriceTextProperty =
DependencyProperty.Register(
"PriceText",
typeof (string),
typeof (LivePriceVolButton),
new FrameworkPropertyMetadata(string.Empty, OnPriceTextChanged));
public string PriceText {
get {
return (string)GetValue(PriceTextProperty);
}
set {
SetValue(PriceTextProperty, value);
}
}
public static readonly DependencyProperty PriceColorProperty =
DependencyProperty.Register(
"PriceColor",
typeof (Color),
typeof (LivePriceVolButton),
new FrameworkPropertyMetadata(Colors.White));
public Color PriceColor {
get {
return (Color) GetValue(PriceColorProperty);
}
set {
SetValue(PriceColorProperty, value);
}
}
public static readonly DependencyProperty MainTextProperty =
DependencyProperty.Register(
"MainText",
typeof (string),
typeof (LivePriceVolButton),
new FrameworkPropertyMetadata(string.Empty));
public string MainText {
get {
return (string) GetValue(MainTextProperty);
}
set {
SetValue(MainTextProperty, value);
}
}
public static readonly DependencyProperty MainTextColorProperty =
DependencyProperty.Register(
"MainTextColor",
typeof(Color),
typeof(LivePriceVolButton),
new FrameworkPropertyMetadata(Colors.White));
public Color MainTextColor {
get {
return (Color) GetValue(MainTextColorProperty);
}
set {
SetValue(MainTextColorProperty, value);
}
}
public static readonly DependencyProperty VolatilityTextProperty =
DependencyProperty.Register(
"VolatilityText",
typeof(string),
typeof(LivePriceVolButton),
new FrameworkPropertyMetadata(string.Empty));
public string VolatilityText {
get {
return (string) GetValue(VolatilityTextProperty);
}
set {
SetValue(VolatilityTextProperty, value);
}
}
public static readonly DependencyProperty VolatilityColorProperty =
DependencyProperty.Register(
"VolatilityColor",
typeof(Color),
typeof(LivePriceVolButton),
new FrameworkPropertyMetadata(Colors.White));
public Color VolatilityColor {
get {
return (Color) GetValue(VolatilityColorProperty);
}
set {
SetValue(VolatilityColorProperty, value);
}
}
When I insert my user control onto a form like this
<my:LivePriceVolButton Margin="43.03,0,0,32" x:Name="livePriceVolButton1"
xmlns:my="clr-namespace:MultiTextBlockButton" HorizontalAlignment="Left"
Width="91.703" Height="30" VerticalAlignment="Bottom"
MainText="MID" MainTextColor="LightBlue" PriceColor="Red" PriceText="1234.56"
VolatilityText="12.2%" VolatilityColor="Aqua" />
I don't see anything in the button at all. Any ideas?
Thanks
You have to set the DataContext for the Button to be equal to the parent UserControl for your Bindings to work. Try something like this:
<UserControl x:Name="uc" ...>
<Button x:Name="PriceButton" DataContext="{Binding ElementName=uc}">
<!--Other code here...-->
</Button>
</UserControl>
I also see that you're using "Color" as the Type for some of your DependencyProperties. I suggest you change them to "Brush" instead. Otherwise the related bindings (e.g. Foreground="{Binding VolatilityColor}") won't work.
Related
I'm trying to bind a property from a user control to a window but get error
Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'.
When I was searching the error usually the people didn't create a Dependency Property or that the problem may be because the two Way binding but didn't find a solution
I know that if I give the usercontrol a name and then get the value but I want to use binding
UserControl.xaml
<UserControl x:Name="ctb" x:Class="ChatClient.CustomControls.CustomTextBox"
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:ChatClient.CustomControls"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="350">
<Grid>
<Border CornerRadius="8"
Background="#3e4147">
<Grid>
<TextBox VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
Background="Transparent"
x:Name="TextBox"
TextWrapping="Wrap"
BorderThickness="0"
Foreground="Gray"
CaretBrush="Gray"
Margin="8,0,0,0"
Text="{Binding ElementName=ctb, Path=InputText}">
</TextBox>
<TextBlock IsHitTestVisible="False"
Text="{Binding ElementName=ctb, Path=PlaceHolder}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Foreground="DarkGray">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=TextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Border>
</Grid>
</UserControl>
UserControl.xaml.cs
public CustomTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty InputTextProp = DependencyProperty.Register(
nameof(InputText),
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(OnValueChangedText));
public static readonly DependencyProperty PlaceHolderProp = DependencyProperty.Register("PlaceHolder", typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(null));
public string InputText
{
get { return (string)GetValue(InputTextProp); }
set { SetValue(InputTextProp, value); }
}
public string PlaceHolder
{
get { return (string)GetValue(PlaceHolderProp); }
set { SetValue(PlaceHolderProp, value); }
}
private static void OnValueChangedText(DependencyObject sender,DependencyPropertyChangedEventArgs e)
{
CustomTextBox customTextBox = sender as CustomTextBox;
customTextBox.InputText = (string)e.NewValue;
}
MainWindow.xaml
<customcontrols:CustomTextBox InputText="{Binding UserName}"
PlaceHolder="UserName"
Margin="0,0,0,20" />
Dependency property name must end with "Property", not "Prop":
public static readonly DependencyProperty InputTextProperty = DependencyProperty.Register
(
nameof(InputText),
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(string.Empty)
);
fix it for all usages and all properties.
OnValueChangedText callback implemented incorrectly and should be removed from metadata
My question is based on one of the answer to this post:
Where is the WPF Numeric UpDown control?
answered by Mr. Squirrel.Downy.
What i would like to accomplish is a numeric updown control that increases/decreases with a larger amount when the buttons are pressed for a longer time, otherwise the increase/decrease is the normal amount. Also when the max/min is reached the buttons should disable.
I have a style based on a Slider which contains 2 buttons of type HoldButton (up/down, derived from RepeatButton) and a readonly TextBlock for the Value.
In the HoldButton i have 2 dependency properties for an ICommand. These are ClickAndHoldCommand and ClickCommand which are executed from either the OnPreviewMouseLeftButtonDown() or OnPreviewMouseLeftButtonUp() depending on the length of the mouse button press. In the xaml these are bound to Slider.IncreaseLarge and Slider.IncreaseSmall respectively.
How can i disable the up button when the max is reached and disable the down button when the min is reached? The difficulty is that when i for example disable the slider, the up mouse event does not work anymore...
<Style TargetType="{x:Type Slider}" x:Key="NumericUpDown">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton Grid.Row="2" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="+">
</usercontrols:HoldButton>
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton Grid.Row="4" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="-">
</usercontrols:HoldButton>
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public partial class HoldButton : RepeatButton
{
private bool buttonIsHeldPressed;
public HoldButton()
{
InitializeComponent();
buttonIsHeldPressed = false;
this.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
// RepeatButton fires click event repeatedly while button is being pressed!
this.Click += HoldButton_Click;
}
private void HoldButton_Click(object sender, RoutedEventArgs e)
{
Trace.WriteLine("HoldButton_Click()");
if (EnableClickHold)
{
if (numberButtonRepeats > 2)
{
ClickAndHoldCommand.Execute(this.CommandParameter);
e.Handled = true;
buttonIsHeldPressed = true;
}
numberButtonRepeats++;
}
}
public bool EnableClickHold
{
get { return (bool)GetValue(EnableClickHoldProperty); }
set { SetValue(EnableClickHoldProperty, value); }
}
public ICommand ClickAndHoldCommand
{
get { return (ICommand)GetValue(ClickAndHoldCommandProperty); }
set { SetValue(ClickAndHoldCommandProperty, value); }
}
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for ClickAndHoldCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickAndHoldCommandProperty =
DependencyProperty.Register("ClickAndHoldCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for ClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickCommandProperty =
DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for EnableClickHold. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnableClickHoldProperty =
DependencyProperty.Register("EnableClickHold", typeof(bool), typeof(HoldButton), new PropertyMetadata(false));
// Using a DependencyProperty as the backing store for MillisecondsToWait. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MillisecondsToWaitProperty =
DependencyProperty.Register("MillisecondsToWait", typeof(int), typeof(HoldButton), new PropertyMetadata(0));
public int MillisecondsToWait
{
get { return (int)GetValue(MillisecondsToWaitProperty); }
set { SetValue(MillisecondsToWaitProperty, value); }
}
private int numberButtonRepeats;
private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (EnableClickHold)
{
numberButtonRepeats = 0;
if(!buttonIsHeldPressed)
{
ClickCommand?.Execute(this.CommandParameter);
}
buttonIsHeldPressed = false;
}
}
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("OnPreviewMouseLeftButtonDown()");
if (EnableClickHold)
{
// When numberButtonRepeats comes above 1 then the button is considered to be pressed long
if (numberButtonRepeats > 1)
{
ClickAndHoldCommand?.Execute(this.CommandParameter);
}
numberButtonRepeats++;
}
}
}
<UserControl x:Class="Views.TemperatureControlView"
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:Views"
xmlns:cal="http://www.caliburnproject.org"
xmlns:controls="clr-namespace:UserControls"
mc:Ignorable="d"
d:DesignHeight="250" d:DesignWidth="150">
<Slider Minimum="{Binding MinimumTemperature}"
Maximum="{Binding MaximumTemperature}"
SmallChange="{Binding TemperatureTinySteps}"
LargeChange="{Binding TemperatureSmallSteps}"
Value="{Binding ControlValue}"
Style="{StaticResource NumericUpDown}" />
</UserControl>
You should extend the Slider control and implement the logic there.
Finally name the RepeatButton elements and move the Style to the Generic.xaml file.
public class CustomSlider : Slider
{
static CustomSlider()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomSlider), new FrameworkPropertyMetadata(typeof(CustomSlider)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_IncreaseButton = GetTemplateChild(nameof(this.PART_IncreaseButton)) as UIElement;
this.PART_DecreaseButton = GetTemplateChild(nameof(this.PART_DecreaseButton)) as UIElement;
}
protected override void OnValueChanged(double oldValue, double newValue)
{
base.OnValueChanged(oldValue, newValue);
if (this.PART_IncreaseButton == null
|| this.PART_DecreaseButton == null)
{
return;
}
this.PART_IncreaseButton.IsEnabled = newValue < this.Maximum;
this.PART_DecreaseButton.IsEnabled = newValue > this.Minimum;
}
private UIElement PART_IncreaseButton { get; set; }
private UIElement PART_DecreaseButton { get; set; }
}
Generic.xaml
Name the HoldButton elements "PART_IncreaseButton" and "PART_IncreaseButton" so that you can find them easily in the template.
<Style TargetType="{x:Type CustomSlider}">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton x:Name="PART_IncreaseButton"
Grid.Row="2"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="+" />
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton x:Name="PART_DecreaseButton"
Grid.Row="4"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="-" />
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I'm doing something similar to Multiple Content Presenters in a WPF User control.
I'm creating a visual widget for a conditional if statement, and I want there to be both true and false content. Where it differs from the original post is that I want to be able to have multiple items in the true and false content. Whereas I can bind ContentPresenter's Content property, ItemsPresenter has no similar thing to bind to.
Is there a way around this that I can build into the control (not just have the calling XAML wrap it in a Grid/StackPanel/etc.)?
public class ConditionalBlock : Control
{
static ConditionalBlock ()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ConditionalBlock), new FrameworkPropertyMetadata(typeof(ConditionalBlock)));
}
public ConditionalBlock ()
{
Background = new SolidColorBrush(Color.FromRgb(0x45, 0x0F, 0x45));
}
public static readonly DependencyProperty LeftWidthProperty = DependencyProperty.Register("LeftWidth", typeof(double), typeof(ConditionalBlock), new UIPropertyMetadata(20.0));
public double LeftWidth
{
get => (double) GetValue(LeftWidthProperty);
set => SetValue(LeftWidthProperty, value);
}
public static readonly DependencyProperty BottomHeightProperty = DependencyProperty.Register("BottomHeight", typeof(double), typeof(ConditionalBlock), new UIPropertyMetadata(10.0));
public double BottomHeight
{
get => (double) GetValue(BottomHeightProperty);
set => SetValue(BottomHeightProperty, value);
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(ConditionalBlock), new UIPropertyMetadata("If"));
public string Title
{
get => (string) GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
public static readonly DependencyProperty HasSettingsProperty = DependencyProperty.Register("HasSettings", typeof(bool), typeof(ConditionalBlock), new UIPropertyMetadata(true));
public bool HasSettings
{
get => (bool) GetValue(HasSettingsProperty);
set => SetValue(HasSettingsProperty, value);
}
public static readonly DependencyProperty TrueTitleProperty = DependencyProperty.Register("TrueTitle", typeof(string), typeof(ConditionalBlock), new UIPropertyMetadata("True"));
public string TrueTitle
{
get => (string) GetValue(TrueTitleProperty);
set => SetValue(TrueTitleProperty, value);
}
public static readonly DependencyProperty TrueContentProperty = DependencyProperty.Register("TrueContent", typeof(object), typeof(ConditionalBlock), null);
public object TrueContent
{
get => GetValue(TrueContentProperty);
set => SetValue(TrueContentProperty, value);
}
public static readonly DependencyPropertyKey HasTrueItemsProperty = DependencyProperty.RegisterReadOnly("HasTrueItems", typeof(bool), typeof(ConditionalBlock), new UIPropertyMetadata(false));
public bool HasTrueItmes => TrueContent != null;
public static readonly DependencyProperty ElseHeightProperty = DependencyProperty.Register("ElseHeight", typeof(double), typeof(ConditionalBlock), new UIPropertyMetadata(10.0));
public double ElseHeight
{
get => (double) GetValue(ElseHeightProperty);
set => SetValue(ElseHeightProperty, value);
}
public static readonly DependencyProperty FalseTitleProperty = DependencyProperty.Register("FalseTitle", typeof(string), typeof(ConditionalBlock), new UIPropertyMetadata("False"));
public string FalseTitle
{
get => (string) GetValue(FalseTitleProperty);
set => SetValue(FalseTitleProperty, value);
}
public static readonly DependencyProperty FalseContentProperty = DependencyProperty.Register("FalseContent", typeof(object), typeof(ConditionalBlock), null);
public object FalseContent
{
get => GetValue(FalseContentProperty);
set => SetValue(FalseContentProperty, value);
}
public static readonly DependencyPropertyKey HasFalseItemsProperty = DependencyProperty.RegisterReadOnly("HasFalseItems", typeof(bool), typeof(ConditionalBlock), new UIPropertyMetadata(false));
public bool HasFalseItmes => FalseContent != null;
}
<Style TargetType="{x:Type local:ConditionalBlock}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ConditionalBlock}">
<Grid Background="White"
Margin="0,1">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--This is the top bar-->
<Border Grid.Row="0"
Grid.ColumnSpan="3"
CornerRadius="4"
Background="{TemplateBinding Background}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="7,0,0,0"
VerticalAlignment="Center"
Text="{TemplateBinding Title}"
Foreground="White" />
<Image
Grid.Column="1"
Source="{StaticResource SettingsIcon}"
Visibility="{TemplateBinding HasSettings, Converter={StaticResource BooleanVisibilityConverter}}"
VerticalAlignment="Center"
MaxWidth="10"
Margin="0,1,5,0"
Stretch="Uniform" />
</Grid>
</Border>
<!--This is the true block-->
<Grid
Grid.Row="1"
Grid.Column="0"
Width="{TemplateBinding LeftWidth}"
Visibility="{TemplateBinding HasTrueItems, Converter={StaticResource BooleanVisibilityConverter}}"
Margin="0,-3.5"
Background="{TemplateBinding Background}">
<TextBlock
VerticalAlignment="Center"
Text="{TemplateBinding TrueTitle}"
Foreground="White">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"></RotateTransform>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
<Grid
Grid.Row="1"
Grid.Column="1"
Visibility="{TemplateBinding HasTrueItems, Converter={StaticResource BooleanVisibilityConverter}}"
Margin="-1"
Background="{TemplateBinding Background}">
<Border Margin="1,1,-1,1"
CornerRadius="2,0,0,2"
Background="White" />
</Grid>
<Grid
Grid.Row="1"
Grid.Column="2"
Visibility="{TemplateBinding HasTrueItems, Converter={StaticResource BooleanVisibilityConverter}}"
Margin="0,0,0,1">
<ContentPresenter Content="{TemplateBinding TrueContent}" />
</Grid>
<!--This is the else bar-->
<Border Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="3"
Height="{TemplateBinding ElseHeight}"
CornerRadius="4"
Background="{TemplateBinding Background}" />
<!--This is the false block-->
<Grid
Grid.Row="3"
Grid.Column="0"
Width="{TemplateBinding LeftWidth}"
Visibility="{TemplateBinding HasFalseItems, Converter={StaticResource BooleanVisibilityConverter}}"
Margin="0,-3.5"
Background="{TemplateBinding Background}">
<TextBlock VerticalAlignment="Center"
Text="{TemplateBinding FalseTitle}"
Foreground="White">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"></RotateTransform>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
<Grid
Grid.Row="3"
Grid.Column="1"
Visibility="{TemplateBinding HasFalseItems, Converter={StaticResource BooleanVisibilityConverter}}"
Margin="-1"
Background="{TemplateBinding Background}">
<Border Margin="1,1,-1,1"
CornerRadius="2,0,0,2"
Background="White" />
</Grid>
<Grid
Grid.Row="3"
Grid.Column="2"
Visibility="{TemplateBinding HasFalseItems, Converter={StaticResource BooleanVisibilityConverter}}"
Margin="0,0,0,1">
<ContentPresenter Content="{TemplateBinding FalseContent}" />
</Grid>
<!--This is the bottom bar-->
<Border Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="3"
Height="{TemplateBinding BottomHeight}"
CornerRadius="4"
Background="{TemplateBinding Background}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I did a custom button in WPF and I added some properties to use to in style. But some stuff doesn't work correctly
Control.
public class CustomButton : Button
{
public static readonly DependencyProperty TextProperty;
public static readonly DependencyProperty ImageProperty;
public static readonly DependencyProperty ImageRollvoerProperty;
public static readonly DependencyProperty TextMarginProperty;
static CustomButton()
{
TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CustomButton), new UIPropertyMetadata(null));
ImageProperty = DependencyProperty.Register("Image", typeof(ImageSource), typeof(CustomButton), new UIPropertyMetadata(null));
ImageProperty = DependencyProperty.Register("ImageRollvoer", typeof(ImageSource), typeof(CustomButton), new UIPropertyMetadata(null));
TextMarginProperty = DependencyProperty.Register("TextMargin", typeof(string), typeof(CustomButton), new UIPropertyMetadata(null));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public ImageSource ImageImageRollvoer
{
get { return (ImageSource)GetValue(ImageRollvoerProperty); }
set { SetValue(ImageRollvoerProperty, value); }
}
public string TextMargin
{
get { return (string)GetValue(TextMarginProperty); }
set { SetValue(TextMarginProperty, value); }
}
}
Style
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:XposButton}">
<Grid ClipToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="{TemplateBinding Height}" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{TemplateBinding Width}" />
</Grid.ColumnDefinitions>
<local:ClippingBorder Grid.Row="0" Grid.Column="0" BorderThickness="0" CornerRadius="10" Background="{StaticResource OxxoMetroBackground}" BorderBrush="{StaticResource OxxoMetroBorder}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<local:ClippingBorder Background="{StaticResource OxxoMetroBackground}" BorderBrush="{StaticResource BorderBrush}" BorderThickness="2" CornerRadius="9">
<local:ClippingBorder x:Name="Overlay" ClipToBounds="True" Background="Transparent" BorderBrush="{StaticResource OxxoMetroBorder}" BorderThickness="0">
<Image Name="btnImage" Source="{TemplateBinding Image}" Style="{StaticResource ImageUninhabitable}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" />
</local:ClippingBorder>
</local:ClippingBorder>
</local:ClippingBorder>
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" Margin="{TemplateBinding TextMargin}" Text="{TemplateBinding Text}" Style="{DynamicResource TextBlockMenuImg}" FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
View
<ctrl:CustomButton FontFamily="Roboto" Margin="17" TextMargin="0,20,0,0" Width="150" Image="{Binding Path=ImageUrl}" Text="{Binding ShowName}" x:Name="btnMenu" Style="{StaticResource CustomButtonStyle}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path= DataContext.SelectMenuCommand}" CommandParameter="{Binding}" />
The Image, and text are correctly displayed, the FontFamily, and TextMargin doesn't work correctly.
The binding on Margin probably isn't working because the property should be defined as a Thickness type rather than String.
public Thickness TextMargin
I've been banging my head against this problem for 3 days and haven't been able to find an answer.
I've got an application written in WPF (dot Net 4.5) and I'm working with Teststack.White trying to write some automated GUI test cases. The developers have given x:Names to some of the controls and they show up through Inspect/WPF Inspector/Visual UI Automation Verify as the AutomationId just fine.
There are other controls that are buried a bit deeper that I've been tasked with giving automation ids to (mostly as an exercise so I can get more familiar with the back-end). This is where I've been banging my head against things.
I've tried giving the controls the AutomationProperties.AutomationId (and .Name) attribute. I've given AutomationProperties a name space definition. As well I've made sure SWA.Peers is referenced.
I haven't tried using property setters in XAML as they don't make much sense currently and I'm hoping don't need to write stuff in C# to set them (if I do, I'll do it but just hoping).
One of my co-workers sat down and we pulled out the bare minimum setup that isn't exposing the Automation ID property (unfortunately he as well as the other devs are drawing a blank as to why this isn't happening). It looks like:
MainWindow.xaml:
<Window x:Class="TestAutomationUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:autoProp="clr-namespace:System.Windows.Automation;assembly=PresentationCore"
xmlns:common="clr-namespace:Asi.Ui.Common"
Title="Test UI (Pick me)" Height="455.075" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/IconLabelButton.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel x:Name="PresentationRoot">
<common:IconLabelButton x:Name="TestButton" autoProp:AutomationProperties.AutomationId="TestButtonClick" Text="Stuff" Margin="245,0,214.4,0" RenderTransformOrigin="4.648,0.588" Height="32">
</common:IconLabelButton>
</StackPanel>
</Window>
IconLabelButton.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:autoProp="clr-namespace:System.Windows.Automation;assembly=PresentationCore"
xmlns:common="clr-namespace:Asi.Ui.Common">
<Style TargetType="common:IconLabelButton">
<Setter Property="Template" Value="{DynamicResource Asi.Ui.Common.IconLabelButton}" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="FontWeight" Value="{DynamicResource Mobius.UI.Resources.Fonts.WeightLight}"/>
<Setter Property="Spacing" Value="10" />
</Style>
<ControlTemplate x:Key="Asi.Ui.Common.IconLabelButton" TargetType="common:IconLabelButton">
<Border Background="{TemplateBinding Background}" Height="30">
<Button Style="{DynamicResource Mobius.UI.Resources.Styles.IconButton}" Margin="0" Padding="0" HorizontalContentAlignment="Stretch" HorizontalAlignment="Left"
Command="{TemplateBinding Command}" CommandParameter="{TemplateBinding CommandParameter}" Foreground="{TemplateBinding Foreground}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="22" />
<ColumnDefinition Width="{TemplateBinding Spacing}" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="22" Height="22">
<Path Margin="1" Height="20" Width="20" Fill="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ContentControl}}}" Data="{TemplateBinding Icon}" Stretch="Fill"/>
<Path Margin="1" Height="20" Width="20" Fill="{TemplateBinding AdornerIconFill}" Data="{TemplateBinding AdornerIcon}" Stretch="Fill"/>
</Grid>
<TextBlock Grid.Column="2" Text="{TemplateBinding Text}" VerticalAlignment="Center" Foreground="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ContentControl}}}" FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}" FontStretch="{TemplateBinding FontStretch}" FontWeight="{TemplateBinding FontWeight}"/>
</Grid>
</Button>
</Border>
</ControlTemplate>
</ResourceDictionary>
IconLabelButton.xaml.cs:
using System;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace Asi.Ui.Common
{
public class IconLabelButton : Control
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(IconLabelButton), new PropertyMetadata(default(string)));
public static readonly DependencyProperty AdornerIconProperty =
DependencyProperty.Register("AdornerIcon", typeof(object), typeof(IconLabelButton), new PropertyMetadata(default(object)));
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(object), typeof(IconLabelButton), new PropertyMetadata(default(object)));
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(IconLabelButton), new PropertyMetadata(default(ICommand)));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(IconLabelButton), new PropertyMetadata(default(ICommand)));
public static readonly DependencyProperty SpacingProperty =
DependencyProperty.Register("Spacing", typeof(GridLength), typeof(IconLabelButton), new PropertyMetadata(default(GridLength)));
public static readonly DependencyProperty IconButtonSizeProperty =
DependencyProperty.Register("IconButtonSize", typeof(GridLength), typeof(IconLabelButton), new PropertyMetadata(default(GridLength)));
public static readonly DependencyProperty AdornerIconFillProperty =
DependencyProperty.Register("AdornerIconFill", typeof(Brush), typeof(IconLabelButton), new PropertyMetadata(default(Brush)));
static IconLabelButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(IconLabelButton), new FrameworkPropertyMetadata(typeof(IconLabelButton)));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public object AdornerIcon
{
get { return GetValue(AdornerIconProperty); }
set { SetValue(AdornerIconProperty, value); }
}
public object Icon
{
get { return GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public GridLength Spacing
{
get { return (GridLength)GetValue(SpacingProperty); }
set { SetValue(SpacingProperty, value); }
}
public GridLength IconButtonSize
{
get { return (GridLength)GetValue(IconButtonSizeProperty); }
set { SetValue(IconButtonSizeProperty, value); }
}
public Brush AdornerIconFill
{
get { return (Brush)GetValue(AdornerIconFillProperty); }
set { SetValue(AdornerIconFillProperty, value); }
}
}
}
I apologize if this is an easy question (or if I'm not asking the right one). I'm an entry-level programmer who only has a cursory familiarity with WPF/XAML.
Thanks in advance for the help! (Hopefully it's any easy fix!)
Update: After doing some further digging on UI Automation Providers, it looks like all custom controls have to be written to support AutomationProperties. Of course talking with the devs that isn't case.
I'll post more information when I get the solution figured out.
There is no AutomationPeer for your control. So your AutomationId setup does not set anything.
Override OnCreateAutomationPeer in your control codebehind like this:
protected override AutomationPeer OnCreateAutomationPeer()
{
return new IconLabelButtonAutomationPeer(this);
}
where your new IconLabelButtonAutomationPeer looks like this:
public class IconLabelButtonAutomationPeer : FrameworkElementAutomationPeer
{
public IconLabelButtonAutomationPeer(IconLabelButton owner)
: base(owner)
{
}
}