WPF trigger on attached property not working - wpf

i'm trying to change an image which i put as button's content through an attached property, yet for some reason the image is not changing and i don't get any exceptions or anything solid in the output.
Here is my attached property class code:
public class ImageButton
{
#region Image dependency property
/// <summary>
/// An attached dependency property which provides an
/// <see cref="ImageSource" /> for arbitrary WPF elements.
/// </summary>
public static DependencyProperty ImageProperty;
/// <summary>
/// Gets the <see cref="ImageProperty"/> for a given
/// <see cref="DependencyObject"/>, which provides an
/// <see cref="ImageSource" /> for arbitrary WPF elements.
/// </summary>
public static ImageSource GetImage(DependencyObject obj)
{
return (ImageSource)obj.GetValue(ImageProperty);
}
/// <summary>
/// Sets the attached <see cref="ImageProperty"/> for a given
/// <see cref="DependencyObject"/>, which provides an
/// <see cref="ImageSource" /> for arbitrary WPF elements.
/// </summary>
public static void SetImage(DependencyObject obj, ImageSource value)
{
obj.SetValue(ImageProperty, value);
}
#endregion
static ImageButton()
{
//register attached dependency property
var metadataImage = new FrameworkPropertyMetadata((ImageSource)null);
ImageProperty = DependencyProperty.RegisterAttached("Image",
typeof(ImageSource),
typeof(ImageButton), metadataImage);
}
}
here the code for the style using the attached property(the image source is set to take the value):
<Style TargetType="{x:Type Button}" x:Key="MyImageButton">
<Setter Property="Background" Value="White"/>
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border
x:Name="Border"
Background="White">
<ContentPresenter>
<ContentPresenter.Content>
<Grid
x:Name="Grid"
Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Source="{Binding Path=(attachedProperties:ImageButton.Image),
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Button}}}"
Width="30"
Height="25"
Margin="5,0,2,0"
VerticalAlignment="Center"/>
<TextBlock
x:Name="TextBlock"
Grid.Column="1"
Text="{Binding Path=(attachedProperties:ImageButton.StringContent),
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Button}}}"
VerticalAlignment="Center"
TextAlignment="Center"
Margin="0,0,15,0"/>
</Grid>
</ContentPresenter.Content>
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and finally the XAML code using the style and the trigger there trying to change the image source:
<Button
Grid.Row="2"
Width="30"
attachedProperties:ImageButton.Image="..\Images\Down_Arrow_Enabled_Icon_25x25.png"
Command="{Binding PriorityDownCommand}">
<Button.Style>
<Style TargetType="{x:Type Button}"
BasedOn="{StaticResource MyImageButton}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="attachedProperties:ImageButton.Image" Value="..\Images\Down_Arrow_Disabled_Icon_25x25.png"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
thanks ahead for anyone who can find the problem!!!

Please take a look at this Dependency Property Value Precedence article.
Your problem is that you are trying to override a Local Property Value from withing a Style Trigger, this cannot be done since the Local Value has a higher precedence value.
You should set the default property in style setters:
<Button
Grid.Row="2"
Width="30"
Command="{Binding PriorityDownCommand}">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MyImageButton}">
<Setter Property="attachedProperties:ImageButton.Image" Value="..\Images\Down_Arrow_Enabled_Icon_25x25.png"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="attachedProperties:ImageButton.Image" Value="..\Images\Down_Arrow_Disabled_Icon_25x25.png"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Related

TabControl view doesn't update on template change

I need to change the view of a TabControl's content on-the-fly.
I am guessing the best way to accomplish this is to define the view as a DataTemplate, and then change said template using a trigger.
In my test app, the background color is tied to the same data trigger as the template. The background color updates immediately upon making the radio button selection.
Expected behavior: The Tab Item Content / DataTemplate also updates immediately.
Actual Behavior: Tab content view does not update until the tab selection is changed.
Here's my Minimal, Complete, and Verifiable example:
Window XAML
<Window x:Class="ChangeView.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Title="Window1" Height="350" Width="400">
<Window.Resources>
<DataTemplate x:Key="ContentTemplate1">
<Grid>
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding MyBlurb}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ContentTemplate2">
<Grid>
<Label HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Content="{Binding MyHeader}" Background="Black" Foreground="White" FontSize="72"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType1}" Value="False">
<Setter Property="Background" Value="Chartreuse"/>
</DataTrigger>
<DataTrigger Binding="{Binding ViewType1}" Value="True">
<Setter Property="Background" Value="Bisque"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Margin="10,38,0,0" Text="Content Template:"/>
<RadioButton x:Name="radio1" Margin="120,40,0,0" Grid.ColumnSpan="2" Content="1" GroupName="ViewSelect" IsChecked="{Binding Path=ViewType1}"/>
<RadioButton Margin="170,40,0,0" Grid.ColumnSpan="2" Content="2" GroupName="ViewSelect"/>
<TabControl Grid.Row="1" ItemsSource="{Binding TabGroup}">
<TabControl.Style>
<Style TargetType="{x:Type TabControl}">
<Setter Property="Margin" Value="10"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType1}" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource ContentTemplate1}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ViewType1}" Value="False">
<Setter Property="ContentTemplate" Value="{DynamicResource ContentTemplate2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabControl.ItemTemplate>
<DataTemplate>
<Border x:Name="headerBorder">
<Label Content="{Binding MyHeader}" FontSize="20"/>
</Border>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
Code Behind
namespace ChangeView
{
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged
{
public ObservableCollection<TabData> TabGroup { get; set; } = new ObservableCollection<TabData>();
private bool _viewType1 = true;
public bool ViewType1
{
get { return _viewType1; }
set { _viewType1 = value; RaisePropertyChanged(nameof(ViewType1)); }
}
public Window1()
{
TabGroup.Add(new TabData("♻️", "Recycle"));
TabGroup.Add(new TabData("⚔", "Swords"));
TabGroup.Add(new TabData("⚗", "Chemistry"));
TabGroup.Add(new TabData("🌵", "Cactus"));
TabGroup.Add(new TabData("👺", "Tengu"));
TabGroup.Add(new TabData("🐙", "Octopus"));
DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public class TabData : INotifyPropertyChanged
{
private string _myHeader, _myBlurb;
public TabData(string header, string blurb)
{
MyHeader = header;
MyBlurb = blurb;
}
public string MyHeader
{
get { return _myHeader; }
set { _myHeader = value; RaisePropertyChanged(nameof(MyHeader)); }
}
public string MyBlurb
{
get { return _myBlurb; }
set { _myBlurb = value; RaisePropertyChanged(nameof(MyBlurb)); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
After changing the radio button state, change the selected tab. You will then see the correct content template.
It looks as if, in a TabControl, changing the content template alone does not cause the content to be rendered. If you render new content by switching the selected tab, the current content template will then be used.
So let's write one ContentTemplate, which creates a ContentControl and switches the ContentControl's ContentTemplate. I've tested, and the ContentControl will re-render its content when its ContentTemplate changes. The bindings get a little bit verbose.
<TabControl ItemsSource="{Binding TabGroup}" Grid.Row="1">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl
x:Name="ContentCtl"
Content="{Binding}"
/>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding DataContext.ViewType1, RelativeSource={RelativeSource AncestorType=TabControl}}"
Value="True">
<Setter
TargetName="ContentCtl"
Property="ContentTemplate"
Value="{DynamicResource ContentTemplate1}"
/>
</DataTrigger>
<DataTrigger
Binding="{Binding DataContext.ViewType1, RelativeSource={RelativeSource AncestorType=TabControl}}"
Value="False"
>
<Setter
TargetName="ContentCtl"
Property="ContentTemplate"
Value="{DynamicResource ContentTemplate2}"
/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemTemplate>
<DataTemplate>
<Border x:Name="headerBorder">
<Label Content="{Binding MyHeader}" FontSize="20"/>
</Border>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
You could also do something ugly in your code behind to make the TabControl render itself again on command. Or maybe you can replace the metadata on TabControl.ContentTemplate.

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" >

Bind Control's Property to Variable, Bind Variable to Another Control

Its hard to understand something from the Title, but its also hard for me to explain but I will try.
I have custom control to pick color.
The custom control has Property called SelectedColor:
/// <summary>
/// SelectedColor property backing ReadOnly DependencyProperty.
/// </summary>
private static readonly DependencyPropertyKey SelectedColorPropertyKey
= DependencyProperty.RegisterReadOnly("SelectedColor", typeof(Color), typeof(ImageColorPicker)
, new FrameworkPropertyMetadata(Colors.Transparent
, FrameworkPropertyMetadataOptions.AffectsRender));
/// <summary>
/// Gets or sets the color selected.
/// </summary>
/// <value>The color selected.</value>
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorPropertyKey.DependencyProperty); }
}
I want to bind that Property to a variable I have in my ViewModel which is called TabColor:
public event PropertyChangedEventHandler PropertyChanged; //Event to notify when Property changed.
/// <summary>
/// Notify that Property has Changed.
/// </summary>
/// <param name="propertyName">The name of the Property</param>
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private Brush _tabColor; //Color of the Tab.
//Return/Set the color of the Tab.
public Brush TabColor
{
get
{
return _tabColor;
}
set
{
_tabColor = value;
NotifyPropertyChanged("TabColor");
}
}
Now, I want to bind the TabColor to Properties on my Style:
<Style TargetType="{x:Type MetroStyle:MetroTabItem}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Header" Value="{Binding TabTitle}" />
<Setter Property="Foreground" Value="{Binding TabColor}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MetroStyle:MetroTabItem}">
<Grid Height="26" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Margin="5,0" HorizontalAlignment="Left" VerticalAlignment="Center" ContentSource="Header">
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type MetroStyle:MetroTabItem}}}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
<StackPanel Grid.Column="1" Height="16" Margin="0,0,1,0" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
<Button Width="16" Click="ShowHide_Click" Content="🌕" Style="{StaticResource CustomizedMetroTabItemButton}" ToolTip="Hide" Visibility="{Binding WindowsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
<Button Width="16" Click="ShowHide_Click" Content="🌑" Style="{StaticResource CustomizedMetroTabItemButton}" ToolTip="Show" Visibility="{Binding WindowsVisible, Converter={StaticResource InverseBooleanToVisibilityConverter}}" />
<Button Width="16" Click="Clear_Click" Content="" Style="{StaticResource CustomizedMetroTabItemButton}" ToolTip="Clear" />
<ToggleButton Width="16" x:Name="Edit" Content="" Style="{StaticResource CustomizedMetroTabItemToggleButton}" ToolTip="Edit" />
<Popup IsOpen="{Binding IsChecked, Mode=TwoWay, ElementName=Edit}" StaysOpen="False" PlacementTarget="{Binding ElementName=Edit}" Placement="Left" VerticalOffset="{Binding ActualHeight, ElementName=Edit}" HorizontalOffset="{Binding Width, ElementName=Edit}" PopupAnimation="Slide">
<StackPanel>
<local:ImageColorPicker x:Name="ColorPicker" Source="Images/ColorWheel.png" HorizontalAlignment="Center" Width="100" Height="100"/>
</StackPanel>
</Popup>
</StackPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Background" Value="#EFEFF2" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FFFFFF" />
<Setter Property="Foreground" Value="{Binding TabColor}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{Binding TabColor}" />
<Setter Property="Foreground" Value="#FFFFFF" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The reason why I use the variable as a "connector" is because I cant bind Properties out of the Tree - right? (I mean I cant do this: <Setter Property="Foreground" Value="{Binding ElementName=ColorPicker, Path=SelectedColor, Converter={StaticResource ColorToBrushConverter}}" />
I also have an Image that should simplify what I am asking:
If you have another solution as long as I will achieve what I need - I will accept.
Thanks!
EDIT:
I have a converter which converts from Color to SolidColorBrush but still, I dont know how to do the binding I want:
[ValueConversion(typeof(Color), typeof(Brush))]
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color color = (Color)value;
return new SolidColorBrush(color);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
EDIT2:
Sample code with everything above
EDIT3:
A new picture (maybe it will be more understandable)
I believe all you need to do is set up a binding on Selected Color, something like:
<local:ImageColorPicker x:Name="ColorPicker" Source="Images/ColorWheel.png" HorizontalAlignment="Center" Width="100" Height="100" SelectedColor="{Binding Path=TabColor, Converter={StaticResource ColorToBrushConverter}}/>
You can always set a binding on a dependency property (but nothing else). Hopefully that clears up "Binding properties out of the tree". If not, please clarify what you don't understand and I would be happy to try and address it.
Also, the dependency property isn't set up how I am used to seeing it, so if you are still running into trouble, you might look into setting it up with the dpprop code snippet, or follow the example on MSDN.
Comments
Bindings have multiple modes, and if you specifically want just one you just set the mode variable. In your case, you would change the binding to:
SelectedColor="{Binding Path=TabColor, Converter={StaticResource ColorToBrushConverter, Mode=OneWay}}
That way, any changes to the "TabColor" property won't propagate to the ColorPicker, but changes from the ColorPicker will set the "TabColor" property. For reference the other modes are: TwoWay (the default), OneTime (get the default just on initialization), and OneWayToSource(like OneWay, but it only changes the source, and never picks up updates).
When I mentioned the "unusual" dependency property, I was referring to the style of the "SelectedColor" DP. Of course TabColor will not be a DP :).
Finally, I believe that you can bind the Value property as described. Have you tried it? The only restriction on binding is that it must be done on a DP.
Update
I could not get the ColorPicker to update the view model. Everything I've read indicates that it should be able to, but I wasn't able to figure it out. If that is an important requirement, I would suggest starting from scratch and get just that working, then keep going.
That being said, I did get the color to update. Try changing your XAML to:
<TabItem Header="Test" Foreground="{Binding ElementName=ColorPicker, Path=SelectedColor, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<Grid>
<local:ImageColorPicker x:Name="ColorPicker" Source="ColorWheel.png" HorizontalAlignment="Center"/>
</Grid>
</TabItem>
For some reason, I had to rebuild a couple times to make this work. Again, I'm sorry I couldn't get the original request working.
Update 2
Apparently all you need to do is add "BindsTwoWayByDefault" to the metadata options. This will cause the "ConvertBack" function to be called since you are updating the ViewModel.
You could probably get away with having "OneWay" bindings on all the controls EXCEPT the ImageColorPicker so that they don't accidentally set the color, but in general TwoWay should be fine.

WPF Dependency Properties Settings binding doesnt update

Ive already been through about 30 different posts and changed how I handle this and it gets closer but still wont work.
I have a Custom Button control in a library, that I am using in Main application. The button displays, handles mouse overs and such, but the dependency property for the text, (or Icon but Ill get to that later) wont update. When I set up the Dependency property with a default value that the only value it displays, it wont display anyhting I set in the designer, or through code.
public static readonly DependencyProperty FileTextProperty;
//Constructor
static FileButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FileButton), new FrameworkPropertyMetadata(typeof(FileButton)));
// Initialize dependency properties
FileTextProperty = DependencyProperty.Register("FileText", typeof(string), typeof(FileButton), new UIPropertyMetadata ("Default File Text"));
}
/// <summary>
/// The Filename text displayed by the button.
/// </summary>
[Description("The text displayed by the button."), Category("Common Properties")]
public string FileText
{
get { return (string)GetValue(FileTextProperty); }
set { SetValue(FileTextProperty, value); }
}
here is the XAML (Somewhat abbreviated)
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="GradientStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FileButton}">
<Grid x:Name="main" MinHeight="38" MaxHeight="38">
<Grid Margin="4,0" Name="DisplayMain">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock x:Name="fileText" HorizontalAlignment="Stretch" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:FileButton, AncestorLevel=1}, Path=FileText}" TextWrapping="NoWrap" VerticalAlignment="Top" Grid.Column="0" Margin="4,0,0,0" TextTrimming="WordEllipsis"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ControlTemplate.Resources>
<Grid x:Name="LayoutRoot" Margin="0">
<local:FileButton Style="{DynamicResource GradientStyle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I run it the only data displayed on the button is the defaulted text. Even though I know I am setting the Dependency Property. I even when as far as using the binding Path=FileText.Length in the XAML to make sure its reaching it and it is, and it displayed 0 on the button, so it seems like Im not setting the data, even when I manually create the button, and insert it into a container, it still doesnt display what I set it to. (Yes I stepped into it in the debugger to make sure)
Any ideas, its been most of the day trying to figure this out.
PLEASE HELP THANKS!
You don't need to create a custom button for this. You can put any content you want inside a Button:
<Grid x:Name="LayoutRoot">
<Button VerticalAlignment="Top" Margin="132,140,168,0" Height="54.96">
<StackPanel>
<Image Source="{Binding Icon}"></Image>
<TextBlock Text="{Binding FileName}" HorizontalAlignment="Left"/>
</StackPanel>
</Button>
</Grid>
Where FileName is a property in a viewmodel you create, which you set to be the DataContext of your Window (or grid or button).
With this MVVM setup, when you change FileName in your code, it will automatically display it in your button.
You can still style the button any way you want.
I'm not sure why you're using RelativeSource binding instead of TemplateBinding
With all else being the same, I would do this
<TextBlock Text="{TemplateBinding FileText}" />

Make DatePicker easier to read when disabled

The text is grayed out when the DatePicker is disabled and I want the content to be easier to read.
What I did on some TextBoxes was:
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
It did make the text easier to read.
I do manage to change the Foreground colour on the DataPicker but it does not do the trick. The text was still grayed out.
Seems like there is another property I need to set to make the content of the disabled DatePicker easier to read.
So, how do I make the content of my disabled DatePicker easier to read?
Can you extend DatePicker by adding bool DependencyProperty called Editable.
I found a working example at the following link, note that I run this code in .NET 4.
Here is the DatePicker Control:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
namespace DatePickerStyle
{
public class ExtendedDatePicker : DatePicker
{
public static readonly DependencyProperty EditableProperty = DependencyProperty.Register("Editable", typeof(bool),
typeof(ExtendedDatePicker), new PropertyMetadata(true));
public bool Editable
{
get { return (bool)GetValue(EditableProperty); }
set { SetValue(EditableProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textBox = GetTemplateChild("PART_TextBox") as DatePickerTextBox;
var binding = new Binding { Source = this, Path = new PropertyPath(ExtendedDatePicker.EditableProperty) };
textBox.SetBinding(UIElement.FocusableProperty, binding);
}
}
}
Here is the XAML:
<Window x:Class="DatePickerStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DatePickerStyle="clr-namespace:DatePickerStyle"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<DatePicker IsEnabled="True" Grid.Row="0" SelectedDate="2002/12/31"/>
<DatePicker IsEnabled="False" Grid.Row="1" SelectedDate="2002/12/31"/>
<DatePickerStyle:ExtendedDatePicker Editable="True" Grid.Row="2" SelectedDate="2002/12/31"/>
<DatePickerStyle:ExtendedDatePicker Editable="False" Grid.Row="3" SelectedDate="2002/12/31"/>
</Grid>
</Window>
My I suggest this simpler, universal approach?
<ControlTemplate x:Key="MyDisabledDatePicker">
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock
Text="{Binding Path=SelectedDate, StringFormat={}{0:d}, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center" HorizontalAlignment="Left" Padding="10,0,0,0"/>
</Border>
</ControlTemplate>
<Style TargetType="{x:Type DatePicker}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="false">
<Setter Property="Template" Value="{StaticResource MyDisabledDatePicker}" />
</DataTrigger>
</Style.Triggers>
</Style>
Presto!
the above code w ExtendedDatePicker works, first I thought it didn't but that was because the dropdown could still change the text and the Editable="False" doesn't work on the dropdown
so don't forget to add the following to the ExtendedDatePicker
Editable="False" AllowDrop="False" IsDropDownOpen="False" IsHitTestVisible="False" IsManipulationEnabled="False"

Resources