I'm creating custom control - image button. The purpose is to make button without any background or border, only 3 images (normal state, pressed and mouseover). When using it - all 3 images should be bounded. I get a bit code from here but sources are peramanent. My code is sth like that:
Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:JoesControls">
<Style TargetType="{x:Type local:ImageButton}" x:Key="styledImageButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid
Margin="{TemplateBinding Control.Padding}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
>
<Image Name="Normal" Source="{TemplateBinding NormalSource}"/>
<Image Name="Pressed" Source="{TemplateBinding PressedSource}" Visibility="Hidden"/>
<Image Name="Over" Source="{TemplateBinding MouseOverSource}" Visibility="Hidden"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Pressed" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Over" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And ImageButton.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace JoesControls
{
public class ImageButton : Button
{
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
#region NormalSourceProperty
public static readonly DependencyProperty NormalSourceProperty =
DependencyProperty.Register("NormalSource", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public ImageSource NormalSource
{
get { return (ImageSource)GetValue(NormalSourceProperty); }
set
{
SetValue(NormalSourceProperty, value);
}
}
#endregion // NormalSource
#region PressedSourceProperty
public static readonly DependencyProperty PressedSourceProperty =
DependencyProperty.Register("PressedSource", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public ImageSource PressedSource
{
get
{
if (PressedSource == null || PressedSource.ToString() == String.Empty)
SetValue(PressedSourceProperty, NormalSource);
return (ImageSource)GetValue(PressedSourceProperty);
}
set
{
SetValue(PressedSourceProperty, value);
}
}
#endregion // PressedSource
#region MouseOverSourceProperty
public static readonly DependencyProperty MouseOverSourceProperty =
DependencyProperty.Register("MouseOverSource", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public ImageSource MouseOverSource
{
get
{
if (MouseOverSource == null || MouseOverSource.ToString() == String.Empty)
SetValue(MouseOverSourceProperty, NormalSource);
return (ImageSource)GetValue(MouseOverSourceProperty);
}
set
{
SetValue(MouseOverSourceProperty, value);
}
}
#endregion // OverSource
}
}
It compiles without error but clearly doesn't work. Source are not bound. I guess binding on source are set not properly, but I've been trying for some time, googling for it but I stucked. I appreciate any help.
Thanks
I forgot about the question despite the fact I have achived what I wanted. Today I've found it unsolved, so I'm happy to share the solution with You:
xaml.cs file
namespace JoesControls.ImageButton
{
//JoesControls - Controls for WPF
/// <summary>
/// Interaction logic for ImageButton.xaml
/// </summary>
public partial class ImageButton : Button, INotifyPropertyChanged
{
public ImageButton()
{
InitializeComponent();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.Ims = NormalSource;
}
#region NormalSourceProperty
public static readonly DependencyProperty NormalSourceProperty =
DependencyProperty.Register("NormalSource", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public ImageSource NormalSource
{
get { return (ImageSource)GetValue(NormalSourceProperty); }
set
{
SetValue(NormalSourceProperty, value);
}
}
#endregion // NormalSource
#region PressedSourceProperty
public static readonly DependencyProperty PressedSourceProperty =
DependencyProperty.Register("PressedSource", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public ImageSource PressedSource
{
get
{
return (ImageSource)GetValue(PressedSourceProperty);
}
set
{
SetValue(PressedSourceProperty, value);
}
}
#endregion // PressedSource
#region MouseOverSourceProperty
public static readonly DependencyProperty MouseOverSourceProperty =
DependencyProperty.Register("MouseOverSource", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public ImageSource MouseOverSource
{
get
{
return (ImageSource)GetValue(MouseOverSourceProperty);
}
set
{
if (value == null)
SetValue(MouseOverSourceProperty, NormalSourceProperty);
else
SetValue(MouseOverSourceProperty, value);
}
}
#endregion // OverSource
private void Button_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
changeImage(MouseOverSource);
}
private void Button_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
changeImage(NormalSource);
}
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
changeImage(PressedSource);
}
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
changeImage(MouseOverSource);
}
private void changeImage(ImageSource newSource)
{
//ButtonImage.Source = newSource;
Ims = newSource;
}
private ImageSource _ims;
public ImageSource Ims
{
get { return _ims; }
set
{
_ims = value;
OnPropertyChanged("Ims");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and the xaml file:
<Button x:Class="JoesControls.ImageButton.ImageButton"
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"
MouseEnter="Button_MouseEnter"
MouseLeave="Button_MouseLeave"
PreviewMouseDown="Button_PreviewMouseDown"
PreviewMouseUp="Button_PreviewMouseUp"
>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Image Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}, Path=Ims}"
Margin="{TemplateBinding Control.Padding}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
MinHeight="10"
MinWidth="10"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
Related
I tried this sample How to develop treeview with checkboxes in wpf? to create a TreeView with checkbox but in this example you cannot have a family in another family.
So my question is how to create a full treeview with check box not only limited in one level?
So this is what I did
IParent.cs
interface IParent<T>
{
IEnumerable<T> GetChildren();
}
DataModel.cs
using System;
using System.Collections.Generic;
using System.Windows;
public class Family : DependencyObject, IParent<object>
{
public string Name { get; set; }
public List<Person> Members { get; set; }
IEnumerable<object> IParent<object>.GetChildren()
{
return Members;
}
}
public class Person : DependencyObject
{
public string Name { get; set; }
}
ItemHelper.cs
using System.Linq;
using System.Windows;
public class ItemHelper : DependencyObject
{
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), typeof(ItemHelper),
new PropertyMetadata(false, new PropertyChangedCallback(OnIsCheckedPropertyChanged)));
private static void OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
IParent<object> sect = d as IParent<object>;
DependencyObject depObj = d as DependencyObject;
if (sect != null)
{
if (((bool?)e.NewValue).HasValue)
{
foreach (DependencyObject p in sect.GetChildren())
{
SetIsChecked(p, (bool?)e.NewValue);
}
}
}
if (depObj != null)
{
var parentObject = depObj.GetValue(ParentProperty) as IParent<object>;
var parentDO = depObj.GetValue(ParentProperty) as DependencyObject;
int ch = parentObject?.GetChildren()?.Where(
x => GetIsChecked(x as DependencyObject) == true).Count() ?? 0;
int un = parentObject?.GetChildren()?.Where(
x => GetIsChecked(x as DependencyObject) == false).Count() ?? 0;
if (un > 0 && ch > 0)
{
SetIsChecked(parentDO, null);
return;
}
if (ch > 0)
{
SetIsChecked(parentDO, true);
return;
}
SetIsChecked(parentDO, false);
}
}
public static void SetIsChecked(DependencyObject element, bool? IsChecked)
{
element?.SetValue(IsCheckedProperty, IsChecked);
}
public static bool? GetIsChecked(DependencyObject element)
{
return (bool?)element?.GetValue(IsCheckedProperty);
}
public static readonly DependencyProperty ParentProperty =
DependencyProperty.RegisterAttached("Parent", typeof(object), typeof(ItemHelper));
public static void SetParent(DependencyObject element, object Parent)
{
element?.SetValue(ParentProperty, Parent);
}
public static object GetParent(DependencyObject element)
{
return element?.GetValue(ParentProperty);
}
}
MainWindow.xaml
<Window x:Class="WpfApplication102.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication102"
Title="MainWindow" Height="220" Width="250">
<StackPanel>
<TreeView x:Name="treeView" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Families}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Family}" ItemsSource="{Binding Members}" >
<CheckBox Content="{Binding Name}" IsChecked="{Binding Path=(local:ItemHelper.IsChecked), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(local:ItemHelper.IsChecked)}" Value="False" >
<Setter Property="Foreground" Value="LightGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:Person}" >
<CheckBox Content="{Binding Name}" IsChecked="{Binding Path=(local:ItemHelper.IsChecked), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(local:ItemHelper.IsChecked)}" Value="False" >
<Setter Property="Foreground" Value="LightGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<Button Content="?" Click="Button_PrintCrew_Click" />
<TextBlock x:Name="textBoxCrew"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication102
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Family> Families { get; set; }
public MainWindow()
{
InitializeComponent();
this.Families = new ObservableCollection<Family>();
this.Families.Add(new Family() { Name = "Simpsons", Members = new List<Person>() { new Person() { Name = "Homer" }, new Person() { Name = "Bart" } } });
this.Families.Add(new Family() { Name = "Griffin", Members = new List<Person>() { new Person() { Name = "Peter" }, new Person() { Name = "Stewie" } } });
this.Families.Add(new Family() { Name = "Fry", Members = new List<Person>() { new Person() { Name = "Philip J." } } });
foreach (Family family in this.Families)
foreach (Person person in family.Members)
person.SetValue(ItemHelper.ParentProperty, family);
}
private void Button_PrintCrew_Click(object sender, RoutedEventArgs e)
{
string crew = "";
foreach (Family family in this.Families)
foreach (Person person in family.Members)
if (ItemHelper.GetIsChecked(person) == true)
crew += person.Name + ", ";
crew = crew.TrimEnd(new char[] { ',', ' ' });
this.textBoxCrew.Text = "Your crew: " + crew;
}
}
}
Like many other aspects of WPF development, this becomes much easier if you follow the MVVM pattern.
Create a common ViewModel base class to represent each node of the tree, with a collection of the same type to represent the node's children. This is where you would include the logic to handle ticking / unticking the parent / child items as appropriate. The TreeView's ItemSource is then bound to a simple collection of items to form the root level. The U.I. layout of the View is defined by a HierachicalDataTemplate for each distinct node type.
I covered this in a recent blog post, with sample code here.
Can anyone help me with this problem , because i read a blog how to do this on
Walkthrough: Two-way binding inside a XAML User Control
but i don't now how to do this with a bool value
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValueIsSelected(IsSelectedProperty, value); }
}
private void SetValueIsSelected(DependencyProperty property, object value,
[System.Runtime.CompilerServices.CallerMemberName] bool s = null)
{
SetValue(property, value);
if (PropertyChanged != null)
{
string sender = s.ToString();
PropertyChanged(this, new PropertyChangedEventArgs(sender));
}
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(CustomPolygon), new PropertyMetadata(0));
So this was wrong accordingly #Clemens
to understand it more here i some more information on my application
In my MainWindow i am using two ComboBoxes to fill my data with binding to a public ObservableCollection DataPlannen
My code behind MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private ObservableCollection<Plan> dataPlannen;
public ObservableCollection<Plan> DataPlannen
{
get { return dataPlannen; }
set
{
if (value != dataPlannen)
{
dataPlannen = value;
}
}
}
//non relevant code deleted
//get database data for ComboBoxes
private void btnGetPlanData_Click(object sender, RoutedEventArgs e)
{
try
{
this.Cursor = Cursors.Wait;
DataPlannen = new PlanCanvasModel().PlanHotspotsHardwares;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString();
}
finally
{
this.Cursor = Cursors.Arrow;
}
}
private void cmbHotspot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cmbHotspot.SelectedIndex != -1)
{
foreach (Hotspot hotspot in cmbHotspot.ItemsSource)
{
if (hotspot == (Hotspot)cmbHotspot.SelectedItem)
{
hotspot.IsSelected = true;
else
{
hotspot.IsSelected = false;
}
}
}
}
My MainWindow XAML:
<Grid Background="LightGray">
<DockPanel Name="TestCanvas" LastChildFill="True">
<Grid x:Name="Sidebar" DockPanel.Dock="Right" Width="200">
<StackPanel Margin="0,10,10,0">
<ScrollViewer Height="112" VerticalScrollBarVisibility="Auto">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Button x:Name="btnGetPlanData" Width="30" Height="30" HorizontalAlignment="Left" Margin="5,0" Click="btnGetPlanData_Click">
<Image Source="Images/database38.png" Stretch="Uniform"></Image>
<Button.ToolTip>Laad plannen in</Button.ToolTip>
</Button>
</StackPanel>
<Grid>
<ComboBox x:Name="cmbPlannen" Width="180"
ItemsSource="{Binding ElementName=myWindow,Path=DataPlannen}"
DisplayMemberPath="{Binding Plan_naam}"
SelectedValuePath="{Binding Plan_Id}"
SelectionChanged="cmbPlannen_SelectionChanged" IsEditable="True">
<ComboBox.ToolTip>
<ToolTip>Zoek op text of selecteer</ToolTip>
</ComboBox.ToolTip>
</ComboBox>
</Grid>
</StackPanel>
</ScrollViewer>
<Grid>
<ComboBox Margin="5" SnapsToDevicePixels="True" ItemsSource="{Binding ElementName=cmbPlannen,Path=SelectedItem.Hotspots,Mode=TwoWay}"
x:Name="cmbHotspot" SelectedIndex="0"
DisplayMemberPath="{Binding Hotspot_naam}"
SelectedValuePath="{Binding Hotspot_Id}"
SelectedItem="{Binding SelectedItem}"
SelectionChanged="cmbHotspot_SelectionChanged" IsEditable="True">
<ComboBox.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0"></Setter>
<Style.Triggers>
<Trigger Property="ComboBoxItem.IsSelected" Value="True">
<Setter Property="ComboBoxItem.Background" Value="LightGray" />
</Trigger>
<Trigger Property="ComboBoxItem.IsHighlighted" Value="True">
<Setter Property="ComboBoxItem.Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ToolTip>
<ToolTip>Zoek op text of selecteer</ToolTip>
</ComboBox.ToolTip>
</ComboBox>
</Grid>
</StackPanel>
</Grid>
<Grid x:Name="MainContainer" ClipToBounds="True"
>
<Viewbox>
<ItemsControl x:Name="drawingsheet" ItemsSource="{Binding ElementName=cmbPlannen, Path=SelectedItem.Hotspots}"
Width="{StaticResource canvasWidth}"
Height="{StaticResource canvasHeight}"
MouseLeftButtonUp="drawingsheet_MouseLeftButtonUp"
MouseRightButtonDown="drawingsheet_MouseRightButtonDown"
MouseWheel="drawingsheet_MouseWheel"
MouseLeftButtonDown="drawingsheet_MouseLeftButtonDown"
MouseMove="drawingsheet_MouseMove"
Background="White"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{Binding ElementName=cmbPlannen,Path=SelectedItem.Plan_image,Converter={StaticResource ResourceKey=bytesToBitmapImageConverter}}"
Width="{StaticResource canvasWidth}" Height="{StaticResource canvasHeight}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<customPolygon:CustomPolygon x:Name="currentPolygon" PointsPolygon="{Binding Hotspot_points}"
IsSelected="{Binding IsSelected,Mode=TwoWay}"
HasChildren="{Binding HasChildren}"></customPolygon:CustomPolygon>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleCanvas"></ScaleTransform>
<TranslateTransform x:Name="moveCanvas"></TranslateTransform>
</TransformGroup>
</ItemsControl.RenderTransform>
</ItemsControl>
</Viewbox>
</Grid>
</DockPanel>
</Grid>
My UserControl CustomPolygon.Xaml:
<UserControl x:Class="testCanvas.Controls.DrawingControls.CustomPolygon"
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:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Name="userControl"
>
<Polygon x:Name="polygon"
Points="{Binding ElementName=userControl,Path=PointsSource}"
StrokeThickness="0.5"
Stroke="Black"
Opacity="0.5"
MouseEnter="polygon_MouseEnter"
MouseLeave="polygon_MouseLeave"
MouseLeftButtonDown="polygon_MouseLeftButtonDown"/>
</UserControl>
My code behind from CustomPolygon
public partial class CustomPolygon : UserControl, INotifyPropertyChanged
{
public CustomPolygon()
{
InitializeComponent();
}
#region Properties
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
SetValue(IsSelectedProperty, value);
if (IsSelected)
{
polygon.Stroke = Brushes.Red;
polygon.StrokeThickness = 2;
}
else
{
polygon.Stroke = Brushes.Black;
polygon.StrokeThickness = 0.5;
}
}
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
//points for polygon
public PointCollection PointsPolygon
{
get { return (PointCollection)GetValue(PointsPolygonProperty); }
set { SetValue(PointsPolygonProperty, value); }
}
// Using a DependencyProperty as the backing store for PointsPolygon. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointsPolygonProperty =
DependencyProperty.Register("PointsPolygon", typeof(PointCollection), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool HasChildren
{
get { return (bool)GetValue(HasChildrenProperty); }
set { SetValue(HasChildrenProperty, value); }
}
// Using a DependencyProperty as the backing store for HasChildren. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasChildrenProperty =
DependencyProperty.Register("HasChildren", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
#endregion
private void polygon_MouseEnter(object sender, MouseEventArgs e)
{
if (IsSelected != true)
{
polygon.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(Properties.Settings.Default.HotspotHover));
polygon.StrokeThickness = 1;
}
}
private void polygon_MouseLeave(object sender, MouseEventArgs e)
{
if (IsSelected != true)
{
polygon.Stroke = Brushes.Black;
polygon.StrokeThickness = 0.5;
}
}
private void polygon_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
IsSelected = !IsSelected;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Problem is now if i change my selection in cmbHotspot the property is not changing in my usercontrol and visa versa
What they are telling in this blog is not correct for WPF. The article is not about WPF and what it shows is not even necessary in Windows Runtime. You should definitely ignore the parts about setting the DataContext in the constructor of a custom control.
Anyway, you must not call anything else than SetValue in the setter of the CLR wrapper of a dependency property. See the XAML Loading and Dependency Properties article on MSDN for details.
Besides that a dependency property does not need to raise a PropertyChanged event, so your SetValueIsSelected property is redundant anyway.
Finally, your property metadata was wrong, because 0 is not a valid default value for type bool.
Your declaration should look like this:
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(CustomPolygon));
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
Unless you don't want to set any non-standard property metadata, you don't need to specify the PropertyMetadata parameter of the Register method.
However, if you want that the property binds two-way by default, you have to set the appropriate flag by property metadata.
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(
false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
I'm making a touchscreen interface that uses a listbox.
I have a button above and below the listbox for page up/down.
I'm trying to get it to where when scrolled all the way up the pageup button gets disabled.
and when scrolled all the way down the pagedown button gets disabled too.
Here's the code in my Styles.xaml for the Listbox
<Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">
<DockPanel>
<RepeatButton x:Name="LineUpButton" DockPanel.Dock="Top"
HorizontalAlignment="Stretch"
Height="50"
Content="/\"
Command="{x:Static ScrollBar.PageUpCommand}"
CommandTarget="{Binding ElementName=scrollviewer}" />
<RepeatButton x:Name="LineDownButton" DockPanel.Dock="Bottom"
HorizontalAlignment="Stretch"
Height="50"
Content="\/"
Command="{x:Static ScrollBar.PageDownCommand}"
CommandTarget="{Binding ElementName=scrollviewer}" />
<Border BorderThickness="1" BorderBrush="Gray" Background="White">
<ScrollViewer x:Name="scrollviewer">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
</Style>
And here's where I instantiate the listbox
<ListBox SelectedItem="{Binding SelectedCan}" ItemsSource="{Binding Path=SelectedKioskCashCans}">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding image}" MaxWidth="75" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I searched all around yesterday with no luck.
I'm hoping to be able to do it all in xaml.
I'm using images for the buttons but took them out for readability above,
they really look like...
<RepeatButton x:Name="LineUpButton" DockPanel.Dock="Top" HorizontalAlignment="Stretch"
Height="50"
Command="{x:Static ScrollBar.PageUpCommand}"
CommandTarget="{Binding ElementName=scrollviewer}">
<RepeatButton.Template>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Grid>
<Image Name="Normal" Source="/Images/up.png"/>
<Image Name="Pressed" Source="/Images/up.png" Visibility="Hidden"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Pressed" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</RepeatButton.Template>
</RepeatButton>
Just use CanExecute method of the PageUpCommand for that. Return false if where are no pages left and the button will become disabled automatically.
EDIT:
I have created a simple attached behavior that can be used to fix this problem. Just set the following attached property on the ScrollViewer:
<ScrollViewer x:Name="scrollviewer"
z:ScrollBarCommandsCanExecuteFixBehavior.IsEnabled="True">
<ItemsPresenter/>
</ScrollViewer>
And here is the source code of the behavior:
public static class ScrollBarCommandsCanExecuteFixBehavior
{
#region Nested Types
public class CommandCanExecuteMonitor<T> where T : UIElement
{
protected T Target { get; private set; }
protected CommandCanExecuteMonitor(T target, RoutedCommand command)
{
Target = target;
var binding = new CommandBinding(command);
binding.CanExecute += OnCanExecute;
target.CommandBindings.Add(binding);
}
protected virtual void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
}
public class PageUpCanExecuteMonitor : CommandCanExecuteMonitor<ScrollViewer>
{
public PageUpCanExecuteMonitor(ScrollViewer scrollViewer)
: base(scrollViewer, ScrollBar.PageUpCommand)
{
}
protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (e.Handled)
{
return;
}
if (Equals(Target.VerticalOffset, 0.0))
{
e.CanExecute = false;
e.Handled = true;
}
}
}
public class PageDownCanExecuteMonitor : CommandCanExecuteMonitor<ScrollViewer>
{
public PageDownCanExecuteMonitor(ScrollViewer scrollViewer)
: base(scrollViewer, ScrollBar.PageDownCommand)
{
}
protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (e.Handled)
{
return;
}
if (Equals(Target.VerticalOffset, Target.ScrollableHeight))
{
e.CanExecute = false;
e.Handled = true;
}
}
}
#endregion
#region IsEnabled Attached Property
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof (bool), typeof (ScrollBarCommandsCanExecuteFixBehavior), new PropertyMetadata(false, OnIsEnabledChanged));
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool) e.NewValue)
{
var scrollViewer = d as ScrollViewer;
if (scrollViewer != null)
{
OnAttached(scrollViewer);
}
else
{
throw new NotSupportedException("This behavior only supports ScrollViewer instances.");
}
}
}
private static void OnAttached(ScrollViewer target)
{
SetPageUpCanExecuteMonitor(target, new PageUpCanExecuteMonitor(target));
SetPageDownCanExecuteMonitor(target, new PageDownCanExecuteMonitor(target));
}
#endregion
#region PageUpCanExecuteMonitor Attached Property
private static void SetPageUpCanExecuteMonitor(DependencyObject obj, PageUpCanExecuteMonitor value)
{
obj.SetValue(PageUpCanExecuteMonitorProperty, value);
}
private static readonly DependencyProperty PageUpCanExecuteMonitorProperty =
DependencyProperty.RegisterAttached("PageUpCanExecuteMonitor", typeof (PageUpCanExecuteMonitor), typeof (ScrollBarCommandsCanExecuteFixBehavior), new PropertyMetadata(null));
#endregion
#region PageDownCanExecuteMonitor Attached Property
private static void SetPageDownCanExecuteMonitor(DependencyObject obj, PageDownCanExecuteMonitor value)
{
obj.SetValue(PageDownCanExecuteMonitorProperty, value);
}
private static readonly DependencyProperty PageDownCanExecuteMonitorProperty =
DependencyProperty.RegisterAttached("PageDownCanExecuteMonitor", typeof (PageDownCanExecuteMonitor), typeof (ScrollBarCommandsCanExecuteFixBehavior), new PropertyMetadata(null));
#endregion
}
The basic idea is that we add a CommandBinding to the ScrollViewer for each of the commands and subscribe to the CanExecute event on those bindings. In the event handler we check the current position of the scroll and set the e.CanExecute property accordingly.
I am using a Watermark textbox as in Watermark TextBox in WPF
<Grid Grid.Row="0" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
<TextBlock Margin="5,2" Text="This prompt dissappears as you type..." Foreground="{StaticResource brushWatermarkForeground}"
Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
</Grid>
How can I apply this for a PasswordBox?
The general approach is the same: write a custom control style, and show the watermark whenever the password box is empty. The only problem here is that PasswordBox.Password is not a dependency property, and you can't use it in a trigger. Also PasswordBox is sealed, so you can't override this notification behavior, but you can use attached properties here.
The following code demonstrates how.
XAML:
<Window x:Class="WpfTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfTest="clr-namespace:WpfTest"
Title="Password Box Sample" Height="300" Width="300">
<Window.Resources>
<Style x:Key="{x:Type PasswordBox}"
TargetType="{x:Type PasswordBox}">
<Setter Property="WpfTest:PasswordBoxMonitor.IsMonitoring"
Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true">
<Grid>
<ScrollViewer x:Name="PART_ContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBlock Text="Please enter your password"
Margin="4, 2, 0, 0"
Foreground="Gray"
Visibility="Collapsed"
Name="txtPrompt" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="WpfTest:PasswordBoxMonitor.PasswordLength" Value="0">
<Setter Property="Visibility" TargetName="txtPrompt" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<PasswordBox VerticalAlignment="Top"/>
</Grid>
</Window>
C#:
using System.Windows;
using System.Windows.Controls;
namespace WpfTest {
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
}
}
public class PasswordBoxMonitor : DependencyObject {
public static bool GetIsMonitoring(DependencyObject obj) {
return (bool)obj.GetValue(IsMonitoringProperty);
}
public static void SetIsMonitoring(DependencyObject obj, bool value) {
obj.SetValue(IsMonitoringProperty, value);
}
public static readonly DependencyProperty IsMonitoringProperty =
DependencyProperty.RegisterAttached("IsMonitoring", typeof(bool), typeof(PasswordBoxMonitor), new UIPropertyMetadata(false, OnIsMonitoringChanged));
public static int GetPasswordLength(DependencyObject obj) {
return (int)obj.GetValue(PasswordLengthProperty);
}
public static void SetPasswordLength(DependencyObject obj, int value) {
obj.SetValue(PasswordLengthProperty, value);
}
public static readonly DependencyProperty PasswordLengthProperty =
DependencyProperty.RegisterAttached("PasswordLength", typeof(int), typeof(PasswordBoxMonitor), new UIPropertyMetadata(0));
private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var pb = d as PasswordBox;
if (pb == null) {
return;
}
if ((bool) e.NewValue) {
pb.PasswordChanged += PasswordChanged;
} else {
pb.PasswordChanged -= PasswordChanged;
}
}
static void PasswordChanged(object sender, RoutedEventArgs e) {
var pb = sender as PasswordBox;
if (pb == null) {
return;
}
SetPasswordLength(pb, pb.Password.Length);
}
}
}
Please notice PasswordBoxMonitor in XAML code.
you can show/hide the background by yourself instead of using triggers:
XAML:
<PasswordBox x:Name="passwordBox" PasswordChanged="passwordChanged"
Background="{StaticResource PasswordHint}" />
Code behind:
// helper to hide watermark hint in password field
private void passwordChanged(object sender, RoutedEventArgs e)
{
if (passwordBox.Password.Length == 0)
passwordBox.Background.Opacity = 1;
else
passwordBox.Background.Opacity = 0;
}
you can use my approach for a watermark behavior. all you have to do is copy and paste the TextBoxWatermarkBehavior and the change the Behavior<TextBox> to Behavior<PasswordBox>.
you can find a demo project here
#blindmeis's suggestion is good. For PasswordBox the class would be as follows.
public class PasswordBoxWatermarkBehavior : System.Windows.Interactivity.Behavior<PasswordBox>
{
private TextBlockAdorner adorner;
private WeakPropertyChangeNotifier notifier;
#region DependencyProperty's
public static readonly DependencyProperty LabelProperty =
DependencyProperty.RegisterAttached("Label", typeof(string), typeof(PasswordBoxWatermarkBehavior));
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty LabelStyleProperty =
DependencyProperty.RegisterAttached("LabelStyle", typeof(Style), typeof(PasswordBoxWatermarkBehavior));
public Style LabelStyle
{
get { return (Style)GetValue(LabelStyleProperty); }
set { SetValue(LabelStyleProperty, value); }
}
#endregion
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += this.AssociatedObjectLoaded;
this.AssociatedObject.PasswordChanged += AssociatedObjectPasswordChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.Loaded -= this.AssociatedObjectLoaded;
this.AssociatedObject.PasswordChanged -= this.AssociatedObjectPasswordChanged;
this.notifier = null;
}
private void AssociatedObjectPasswordChanged(object sender, RoutedEventArgs e)
{
this.UpdateAdorner();
}
private void AssociatedObjectLoaded(object sender, System.Windows.RoutedEventArgs e)
{
this.adorner = new TextBlockAdorner(this.AssociatedObject, this.Label, this.LabelStyle);
this.UpdateAdorner();
//AddValueChanged for IsFocused in a weak manner
this.notifier = new WeakPropertyChangeNotifier(this.AssociatedObject, UIElement.IsFocusedProperty);
this.notifier.ValueChanged += new EventHandler(this.UpdateAdorner);
}
private void UpdateAdorner(object sender, EventArgs e)
{
this.UpdateAdorner();
}
private void UpdateAdorner()
{
if (!String.IsNullOrEmpty(this.AssociatedObject.Password) || this.AssociatedObject.IsFocused)
{
// Hide the Watermark Label if the adorner layer is visible
this.AssociatedObject.TryRemoveAdorners<TextBlockAdorner>();
}
else
{
// Show the Watermark Label if the adorner layer is visible
this.AssociatedObject.TryAddAdorner<TextBlockAdorner>(adorner);
}
}
}
I am trying to set up a custom style for my newly made usercontrol, however i am getting the error : "Cannot convert the value in attribute 'Property' to object of type 'System.Windows.DependencyProperty'."
I thought i had set up Dependency properties but it seemed this was not the case, so i did some research and added:
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapSource), typeof(Image));
to make this:
-- MyButton.Xaml.Cs --
namespace Client.Usercontrols
{
public partial class MyButton : UserControl
{
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapSource), typeof(Image));
public MyButton()
{
InitializeComponent();
}
public event RoutedEventHandler Click;
void onButtonClick(object sender, RoutedEventArgs e)
{
if (this.Click != null)
this.Click(this, e);
}
BitmapSource _imageSource;
public BitmapSource ImageSource
{
get { return _imageSource; }
set
{
_imageSource = value;
tehImage.Source = _imageSource;
}
}
}
}
This unfortunately does not work. I also tried this:
public BitmapSource ImageSource
{
get { return (BitmapSource)GetValue(MyButton.ImageSourceProperty); }
set
{
SetValue(ImageSourceProperty, value);
}
}
But that did not work and the image was not shown and generated the same error as mentioned previously anyway.
Any ideas?
Regards Kohan.
-- MyButton.Xaml --
<UserControl x:Class="Client.Usercontrols.MyButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MinHeight="30" MinWidth="40"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Button Width="Auto" HorizontalAlignment="Center" Click="onButtonClick">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Transparent" >
<Grid>
<Image Name="tehImage" Source="{Binding ImageSource}" />
<TextBlock Name="tehText" Text="{Binding Text}" Style="{DynamicResource ButtonText}" />
</Grid>
</Border>
</Button>
</UserControl>
-- MYButton Style --
<Style TargetType="{x:Type my:MyButton}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type my:MyButton}">
<ContentPresenter />
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="ImageSource" Value="../Images/Disabled.png" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Biggest problem I see is that you're registering the property as owned by Image rather than by your UserControl. Change to:
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapSource), typeof(MyButton));
If that doesn't work, will need to see your XAML.
The standard form for a dependency property is (i've added in your information):
public BitmapSource ImageSource
{
get { return (BitmapSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
/* Using a DependencyProperty as the backing store for ImageSource.
This enables animation, styling, binding, etc... */
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource",
typeof(BitmapSource),
typeof(MyButton),
new UIPropertyMetadata(null)
);
it seems like your also trying to pass through the dependency property to the ImageSource of the object called "tehImage". You can set this up to automatically update using the PropertyChangedCallback... this means that whenever the property is updated, this will call the update automatically.
thus the property code becomes:
public BitmapSource ImageSource
{
get { return (BitmapSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
/* Using a DependencyProperty as the backing store for ImageSource.
This enables animation, styling, binding, etc... */
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource",
typeof(BitmapSource), typeof(MyButton),
new UIPropertyMetadata(null,
ImageSource_PropertyChanged
)
);
private static void ImageSource_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((MyButton)source).tehImage.ImageSource = (ImageSource)e.NewValue
}
Hopefully with the correctly registered dependency property, this will help you narrow down the issue (or even fix it)
Set the DataContext for your UserControl:
public MyButton()
{
InitializeComponent();
DataContext = this;
}
Alternatively, if you can't do that (since the DataContext is set to another object, for example), you can do this in your XAML:
<UserControl x:Class="Client.Usercontrols.MyButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MinHeight="30" MinWidth="40"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
x:Name="MyControl">
<Button Width="Auto" HorizontalAlignment="Center" Click="onButtonClick">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="Transparent" >
<Grid>
<Image Name="tehImage" Source="{Binding ElementName=MyControl, Path=ImageSource}" />
<TextBlock Name="tehText" Text="{Binding ElementName=MyControl, Path=Text}" Style="{DynamicResource ButtonText}" />
</Grid>
</Border>
</Button>
</UserControl>
The correct way of implementing a source for an Image in a user control in my opinion is not BitmapSouce. The easiest and best way (according to me again) is using Uri.
Change your dependency property to this (while also defining a change callback event):
ImageSourceProperty = DependencyProperty.Register(
"ImageSource", typeof (Uri), typeof (MyButton),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnImageSourceChanged)));
and the property to this:
public Uri ImageSource
{
get
{
return (Uri)GetValue(ImageSourceProperty);
}
set
{
SetValue(ImageSourceProperty, value);
}
}
Where your call back is like this:
private static void OnImageSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyButton hsb = (MyButton)sender;
Image image = hsb.tehImage;
image.Source = new BitmapImage((Uri) e.NewValue);
}