Bind to a TemplatedParent's name in WPF? - wpf

I'm trying to create a WPF Custom Control which will bind itself to it's templated parent's x:Name property. The code which I believe should do this which was also generated by the Binding Maker in Visual Studio, is as follows:
Binding RelativeSource={RelativeSource TemplatedParent}, Path=Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay
But this produces no result. I can replace the binding with any plain text and the desired behavior (the text overlays itself on the control when and only when the user has typed nothing into the control), but my goal here is to make this work as a sort of tooltip so the user knows what the field is supposed to be (as defined in the field's x:Name property).
Here's my full xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SuperTB">
<Style TargetType="{x:Type local:SuperTextB}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SuperTextB}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus}">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="Gray" FontSize="24" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsRequired}"></TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsRequired}" Value="True">
<Setter Property="BorderThickness" Value="4" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the control's c#:
public class SuperTextB : Control
{
static SuperTextB()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SuperTextB), new FrameworkPropertyMetadata(typeof(SuperTextB)));
}
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(String), typeof(SuperTextB));
public string Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
private static DependencyProperty myNameProperty =
DependencyProperty.Register("MyName", typeof(string), typeof(SuperTextB), new PropertyMetadata("Unicorns !", NameChanged));
private static void NameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public string MyName
{
get { return (string)GetValue(myNameProperty); }
set { SetValue(myNameProperty, value); }
}
DependencyProperty isRequiredProperty =
DependencyProperty.Register("IsRequired", typeof(bool), typeof(SuperTextB), new PropertyMetadata(false, IsReqChanged));
private static void IsReqChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public bool IsRequired
{
get { return (bool)GetValue(isRequiredProperty); }
set { SetValue(isRequiredProperty, value); }
}
}

Related

WPF style overwritten when content applied to UserControl

I have a UserContol for a hyperlink. I need to dynamically set the content of the button to a database value.
I have looked at this link [1]:Custom button user control style overwritten when content is set and it seems I have most everything from that post. But I get errors when I try to set the content in the user control. I am not a WPF programmer so any help in small words would be appreciated.
USER CONTROL
`
<UserControl x:Class="DDC.Controls.LinkButton"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
x:Name="UserControl">
<UserControl.Resources>
<Style x:Key="LinkButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Padding" Value="5,0,0,0" />
<Setter Property="ToolTip" Value="Click to Follow Link" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter
VerticalAlignment="Top">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextDecorations" Value="Underline" />
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="LightGray" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Button Name ="btnLinkAddress"
Style="{DynamicResource LinkButtonStyle}"
Content="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type LinkButton}}}"
Width="Auto"
Height="Auto"
UseLayoutRounding="True"
Click="LinkButton_Click" >
</Button>
</Grid>
</UserControl>
C# Class
public partial class LinkButton : UserControl
{
public new static DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object), typeof(LinkButton));
public new object Content
{
get { return (string)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
private void LinkButton_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
TextBlock tb = (TextBlock)btn.Content;
Uri uri = new System.Uri(tb.Text);
// Uri uri = new System.Uri(btn.Content.ToString());
Process.Start(new ProcessStartInfo(uri.AbsoluteUri));
e.Handled = true;
}
public LinkButton()
{
InitializeComponent();
}
ERRORS
I get these errors when trying to set the content in the user control
Content="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type LinkButton}}}"`
LinkButton is not supported in a Windows Presentation Foundation (WPF) project.
Content="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Logical tree depth exceeded while traversing the tree. This could indicate a cycle in the tree.
Rename the property to something else than Content:
public partial class LinkButton : UserControl
{
public static DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(object), typeof(LinkButton));
public object ButtonContent
{
get { return (string)GetValue(ButtonContentProperty); }
set { SetValue(ButtonContentProperty, value); }
}
private void LinkButton_Click(object sender, RoutedEventArgs e)
{
...
}
public LinkButton()
{
InitializeComponent();
}
}
...and bind to it like this:
<Button Name ="btnLinkAddress"
Style="{DynamicResource LinkButtonStyle}"
Content="{Binding Path=ButtonContent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Width="Auto"
Height="Auto"
UseLayoutRounding="True"
Click="LinkButton_Click" >
If you want to set the AncestorType to LinkButton, you should define a namespace mapping:
<Button xmlns:local="clr-namespace:DDC.Controls"
Name ="btnLinkAddress"
Style="{DynamicResource LinkButtonStyle}"
Content="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type local:LinkButton}}}"
Width="Auto"
Height="Auto"
UseLayoutRounding="True"
Click="LinkButton_Click" >

WPF TextBox doesn`t update binded source

I've got a textbox with custom style and binded to it property. If i change value in textbox property doesn't changes but if property changed from code textbox updates right.
I guess it's Mode=TwoWay missing somewhere but I can't figure out where as if I clear style frm xaml it works fine.
Form xaml:
<TextBox x:Name="tbFrames" Height="27" Canvas.Left="53" TextWrapping="Wrap" Text="{Binding Path=frames, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Canvas.Top="185" Width="64" Style="{DynamicResource StyleTextBox}" PreviewTextInput="AnimationValidationTextBox"/>
Style xaml:
<Style x:Key="StyleTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Height" Value="27" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Border
x:Name="PART_border"
Background="Transparent"
BorderBrush="#FF434346"
BorderThickness="1"
/>
<TextBox Foreground="#FFBABAC7" BorderThickness="0" Background="Transparent" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="PART_border" Property="Background" Value="#FF414146" />
<Setter TargetName="PART_border" Property="BorderBrush" Value="#FF5A5A5D" />
<Setter TargetName="PART_border" Property="Opacity" Value="0.7" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
INotifyPropertyChanged is implemented:
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
And looks like working as like I`ve said before if I remove my style from control source updetes.
At first in style textbox binding was:
<TextBox Foreground="#FFBABAC7" BorderThickness="0" Background="Transparent" Text="{TemplateBinding Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
And it doesn't worked so I've changed it to RelativeSource and TwoWay mode but there are still no result.
What can be wrong?
PreviewTextInputCode is :
private void AnimationValidationTextBox(object sender, TextCompositionEventArgs e)
{
if (!animationOpened)
{
e.Handled = true;
return;
}
NumberValidationTextBox(sender, e);
}
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
And property declared as:
private int _frames;
public int frames
{
get => _frames;
set
{
_frames = value;
OnPropertyChanged("frames");
}
}
And with default style frames updates both way with all this checking.

TextWrapping in DataGrid with ItemSource with collection of ExpandoObjects

I have some trouble getting my cells to Wrap, they just go out of bounds, when I look at the visual tree I can see that inside the contentpresenter it sees a text block which I try to set as a style on the content presenter, but when I look at the properties it says no wrap.
I don't know how to do it with a data template, cause its an list of expando objects that is bound to the gridview, where the dynamic properties is bound by the column name. So I don't see how I could bind text="{binding dynamicproperty}. Therefore I tried to change it on the content presenter.
<Grid HorizontalAlignment="Center" Grid.Row="1">
<DataGrid HeadersVisibility="None"
ItemsSource="{Binding SheetData}"
Background="Transparent"
AutoGenerateColumns="False"
ColumnWidth="*"
IsHitTestVisible="False"
BorderThickness="0,2,0,0"
UseLayoutRounding="True"
BorderBrush="White"
RowBackground="Transparent"
MaxWidth="700"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalGridLinesBrush="White"
VerticalGridLinesBrush="White"
Behaviors:GridViewBindingBehavior.ColumnsCollection="{Binding ColumnData}"
VerticalAlignment="Top"
HorizontalContentAlignment="Left"
Width="auto">
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock Margin="5,0" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type DataGridRow}},
Path=Item.RowIndex}"/>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="8, 7"/>
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="White" />
<Setter Property="Background" Value="#f1f2f4" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}" >
<Setter Property="TextWrapping" Value="WrapWithOverflow"/>
<Setter Property="Height" Value="auto"/>
<Setter Property="Width" Value="auto"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
</DataGrid>
</Grid>
Edit:
Since I don't think i am able to use a DataTemplate with the TextBlock within, due to the way i bind the columns, from my attached behavior, maybe there is a way to add the Text wrapping from code. I used the attached behavior from here.
Here is my attached behavior:
public class GridViewBindingBehavior
{
public static readonly DependencyProperty ColumnsCollectionProperty = DependencyProperty.RegisterAttached("ColumnsCollection", typeof(ObservableCollection<DataGridColumns>), typeof(GridViewBindingBehavior), new PropertyMetadata(OnColumnsCollectionChanged));
public static void SetColumnsCollection(DependencyObject o, ObservableCollection<ColumnDefinition> value)
{
o.SetValue(ColumnsCollectionProperty, value);
}
public static ObservableCollection<ColumnDefinition> GetColumnsCollection(DependencyObject o)
{
return o.GetValue(ColumnsCollectionProperty) as ObservableCollection<ColumnDefinition>;
}
private static void OnColumnsCollectionChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
DataGrid gridView = o as DataGrid;
if (gridView == null)
{
return;
}
gridView.Columns.Clear();
if (gridView.ItemsSource == null)
{
return;
}
ObservableCollection<ExpandoObject> objExpando = (ObservableCollection<ExpandoObject>)gridView.ItemsSource;
if (e.NewValue == null)
{
return;
}
var collection = e.NewValue as ObservableCollection<DataGridColumns>;
if (collection == null)
{
return;
}
foreach (var column in collection)
{
var gridViewColumn = GetDataColumn(column);
gridView.Columns.Add(gridViewColumn);
}
}
private static DataGridTextColumn GetDataColumn(DataGridColumns columnName)
{
var column = new DataGridTextColumn();
column.IsReadOnly = true;
column.Header = columnName.DisplayColumnName;
Binding binding = new Binding();
binding.Converter = new ColumnValueConverter();
binding.ConverterParameter = columnName.BindingPropertyName;
column.Binding = binding;
return column;
}
}
Edit 2:
I managed to get it to work with a value converter. The only thing is they way i find the actual row I am on is so ugly, is there a way to bind the datagrid to the second binding. I see the datagrid has CurrentColumn property, but i think that is for selections, and its null when inside the converter.
updated datagrid:
<DataGrid HeadersVisibility="None"
ItemsSource="{Binding SheetData}"
Background="Transparent"
AutoGenerateColumns="False"
ColumnWidth="*"
BorderThickness="0,2,0,0"
UseLayoutRounding="True"
BorderBrush="White"
RowBackground="Transparent"
MaxWidth="700"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalGridLinesBrush="White"
VerticalGridLinesBrush="White"
Behaviors:GridViewBindingBehavior.ColumnsCollection="{Binding ColumnData}"
VerticalAlignment="Top"
HorizontalContentAlignment="Left"
Width="auto">
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock Margin="5,0" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type DataGridRow}},
Path=Item.RowIndex}"/>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="8, 7"/>
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="White" />
<Setter Property="Background" Value="#f1f2f4" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<TextBlock TextWrapping="WrapWithOverflow" >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource columnConver}">
<Binding Path="." />
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}" Path="." />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
</DataGrid>
Ugly value converter:
public class ColumnMultiValueConverter : IMultiValueConverter
{
private static int columnIndex = 0;
private static int lastCount = 0;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var row = values[0] as IDictionary<string, object>;
if (row == null)
return row;
var datagrid = values[1] as DataGrid;
if (columnIndex >= datagrid.Columns.Count || lastCount != datagrid.Columns.Count)
columnIndex = 0;
var result = (row[datagrid.Columns[columnIndex].Header.ToString()]);
columnIndex++;
lastCount = datagrid.Columns.Count;
return (result as GridCell)?.Value?.ToString();
}
This maybe needs to be a new questions since the wrapping is done.
Just my code have become very ugly.

Why must I call Focus() after my button click event for seamless behavior?

I have a SearchTextBox custom control, pictured here:
When the mouse clicks on it, that label disappears. If the user clicks away and there is no text, the label reappears (if the user clicks away but leaves text there, the text stays and the label stays hidden). This button replaces the image when the user starts typing:
When the user clicks the button, the text is cleared.
That's all working correctly. The odd behavior I'm talking about is that when the clear button is clicked, the label flashes on the control for a split-second before disappearing (it should remain hidden the entire time). Because the Multitrigger in the XAML uses IsFocused, I thought maybe I could fix the problem simply by calling Focus() in the code-behind before the Click event finishes. This seems a little hacky, but it actually worked. My question is, why do I have to do that to make it work "right?"
Snippet from Generic.xaml:
<SolidColorBrush x:Key="TextBoxBorder" Color="#ababab"/>
<Style TargetType="{x:Type ui:SearchTextBox}">
<Setter Property="AllowDrop" Value="True" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="LabelText" Value="Search for..." />
<Setter Property="LabelTextColor" Value="Gray" />
<Setter Property="Padding" Value="1" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ui:SearchTextBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" />
</Grid.ColumnDefinitions>
<Label x:Name="LabelText"
Grid.Column="0"
Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelTextColor}"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelText}"
Padding="0"
Margin="5,0,0,0"
FontStyle="Italic"
VerticalAlignment="Center"
Visibility="Hidden" />
<ScrollViewer Grid.Column="0" Panel.ZIndex="1" x:Name="PART_ContentHost" Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Margin="5,0,0,0" Padding="0"/>
<Image x:Name="Image" Grid.Column="1" Visibility="Hidden" Source="search2.png" Width="15" Height="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<Button x:Name="PART_Button" Grid.Column="1" Width="15" Height="15">
<Border HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Source="searchstop.png" />
</Border>
</Button>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Text" Value="">
<Setter TargetName="Image" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_Button" Property="Visibility" Value="Hidden" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Text" Value="" />
<Condition Property="IsFocused" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="LabelText" Property="Visibility" Value="Visible" />
<Setter TargetName="PART_ContentHost" Property="Visibility" Value="Hidden" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
SearchTextBox.cs:
public class SearchTextBox : TextBox
{
public static readonly DependencyProperty LabelTextProperty;
public static readonly DependencyProperty LabelTextColorProperty;
public static readonly DependencyProperty HasTextProperty;
private static readonly DependencyPropertyKey HasTextPropertyKey;
public static readonly DependencyProperty SourceProperty;
private static readonly DependencyProperty IsMouseLeftButtonDownProperty;
static SearchTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchTextBox), new FrameworkPropertyMetadata(typeof(SearchTextBox)));
LabelTextProperty = DependencyProperty.Register("LabelText", typeof(string), typeof(SearchTextBox));
LabelTextColorProperty = DependencyProperty.Register("LabelTextColor", typeof(Brush), typeof(SearchTextBox));
HasTextPropertyKey = DependencyProperty.RegisterReadOnly("HasText", typeof(bool), typeof(SearchTextBox), new PropertyMetadata());
HasTextProperty = HasTextPropertyKey.DependencyProperty;
SourceProperty = DependencyProperty.Register("Source", typeof(ImageSource), typeof(SearchTextBox));
IsMouseLeftButtonDownProperty = DependencyProperty.Register("IsMouseLeftButtonDown", typeof(bool), typeof(SearchTextBox), new PropertyMetadata());
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
HasText = Text.Length != 0;
}
public string LabelText
{
get { return (string)GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
public Brush LabelTextColor
{
get { return (Brush)GetValue(LabelTextColorProperty); }
set { SetValue(LabelTextColorProperty, value); }
}
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public bool HasText
{
get { return (bool)GetValue(HasTextProperty); }
private set { SetValue(HasTextPropertyKey, value); }
}
public bool IsMouseLeftButtonDown
{
get { return (bool)GetValue(IsMouseLeftButtonDownProperty); }
private set { SetValue(IsMouseLeftButtonDownProperty, value); }
}
public override void OnApplyTemplate()
{
Button b = GetTemplateChild("PART_Button") as Button;
if (b != null)
{
b.Click += OnClick;
}
base.OnApplyTemplate();
}
private void OnClick(object sender, RoutedEventArgs e)
{
Text = "";
Focus();
e.Handled = true;
}
}
The flashing most likely occurs because the focus is moving away from your control to the clear button if you press it.
You can fix that be making the clear button non-focusable, i.e.
<Button x:Name="PART_Button" Focusable="False" ... />
Then the focus does not move when you press the button.

WPF : give me a best way for icon button

We can easily make an icon button using a control template like the following code:
<Style x:Key="IconButton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Image x:Name="Background" Source="/UOC;component/TOOLBAR_BUTTON_NORMAL.png"/>
<Image Source="/UOC;component/ICON_SLICER.gif" Width="20" Height="20" Margin="0,-10,0,0"/>
<TextBlock Foreground="White" FontSize="9" Text="{TemplateBinding Button.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,15,0,0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Source" TargetName="Background" Value="/UOC;component/TOOLBAR_BUTTON_OVER.png"/>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Source" TargetName="Background" Value="/UOC;component/TOOLBAR_BUTTON_CLICK.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But i think it's not a productive way in practice. because i can't make a number of styles for each one of icon buttons. (ex. let's assume three buttons in App:'open' button, 'close' button and 'navigate' button. these buttons have different icon sets. i can't make styles like 'IconButton_Close', 'IconButton_Open', 'IconButton_Nav'. it's too stupid.)
UserControl may be an answer. but i think it's not a smart way for that. because if i make UserControl, it'll be just a wrapper of the Button control. it's not a right way.
So, give me the best way for icon button.
thanks.
The correct way to do this would be to define a custom button class, like so:
public class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(ImageSource),
typeof(MyButton), new FrameworkPropertyMetadata(null));
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceHoverProperty = DependencyProperty.Register("ImageSourceHover", typeof(ImageSource),
typeof(MyButton), new FrameworkPropertyMetadata(null));
public ImageSource ImageSourceHover
{
get { return (ImageSource)GetValue(ImageSourceHoverProperty); }
set { SetValue(ImageSourceHoverProperty, value); }
}
public static readonly DependencyProperty ImageSourcePressedProperty = DependencyProperty.Register("ImageSourcePressed", typeof(ImageSource),
typeof(MyButton), new FrameworkPropertyMetadata(null));
public ImageSource ImageSourcePressed
{
get { return (ImageSource)GetValue(ImageSourcePressedProperty); }
set { SetValue(ImageSourcePressedProperty, value); }
}
}
Then define the default Style like so:
<Style x:Key="{x:Type local:MyButton}" TargetType="{x:Type local:MyButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyButton}">
<Grid>
<Image x:Name="Background" Source="{TemplateBinding ImageSource}" />
<Image Source="/UOC;component/ICON_SLICER.gif" Width="20" Height="20" Margin="0,-10,0,0"/>
<TextBlock Foreground="White" FontSize="9" Text="{TemplateBinding Button.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,15,0,0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Source" TargetName="Background" Value="{TemplateBinding ImageSourceHover}"/>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Source" TargetName="Background" Value="{TemplateBinding ImageSourcePressed}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And you'd use it like so:
<local:MyButton ImageSource="/UOC;component/TOOLBAR_BUTTON_NORMAL.png"
ImageSourceHover="/UOC;component/TOOLBAR_BUTTON_OVER.png"
ImageSourcePressed="/UOC;component/TOOLBAR_BUTTON_CLICK.png" />
I did something similar to this for a custom control a while back using the TemplatePart attribute. This displays an icon and some text in a panel. If the icons is the error or fail icon, it turns the text red. There is a dependency property called "Type" which is really just the image file name without the extension. Here's the code, I bet you can adapt this for a custom Button where you can set the source and still have your customization to the template.
[TemplatePart(Name = "PART_Image", Type = typeof(Image))]
public class IconPanel : ContentControl
{
static IconPanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(IconPanel), new FrameworkPropertyMetadata(typeof(IconPanel)));
}
public string Type
{
get { return (string)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register("Type", typeof(string), typeof(IconPanel),
new UIPropertyMetadata("warning", TypeChangedCallback));
static void TypeChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
IconPanel panel = obj as IconPanel;
panel.UpdateImage();
}
void UpdateImage()
{
Image img = GetTemplateChild("PART_Image") as Image;
if (img == null) return;
string ImagePath = String.Format("pack://application:,,,/Resources/{0}.png", this.Type);
Uri uri = new Uri(ImagePath, UriKind.RelativeOrAbsolute);
BitmapImage bmp = new BitmapImage(uri);
img.Source = bmp;
if ( String.Compare(Type, "error", true) == 0 ||
String.Compare(Type, "fail", true) == 0 )
{
this.Foreground = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0x00));
}
}
public override void OnApplyTemplate()
{
UpdateImage();
base.OnApplyTemplate();
}
}
XAML:
<Style TargetType="{x:Type local:IconPanel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:IconPanel}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="7">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image
x:Name="PART_Image"
Margin="0,0,5,5"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Width="16"
Height="16" />
<ContentPresenter Grid.Column="1"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources