OnPropertyChangedCallback not being invoked when using an attached property - wpf

My OnPropertyChangedCallback is not being called for an attached property.
I expect this method to be called by default when running the app.
My code compiles.
I have no errors in the Output window.
Any suggestions?
Attached Property:
namespace FileModifier.Behaviors
{
public class AttachedProperties : DependencyObject
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(AttachedProperties), new PropertyMetadata(null, OnPropertyChangedCallback));
public static void SetText(DependencyObject d, string value)
{
d.SetValue(TextProperty, value);
}
public static string GetText(DependencyObject d)
{
return d.GetValue(TextProperty) as string;
}
private static void OnPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var richEditBox = d as RichTextBox;
var textRange = new TextRange(richEditBox.Document.ContentStart, richEditBox.Document.ContentEnd);
throw new NotImplementedException();
}
}
}
XAML:
<RichTextBox x:Name="FileContentControl" IsReadOnly="True"
behaviors:AttachedProperties.Text="{Binding ElementName=FileListView, Path=SelectedItem,
Converter={StaticResource FilePathToContentConverter}}"
VerticalAlignment="Center" Padding="10" />
.
.
.
<ListView x:Name="FileListView" ItemsSource="{Binding FilePaths}" Grid.Column="2" Grid.Row="0"
SelectedItem="{Binding SelectedFilePath}"
VerticalAlignment="Center"
Margin="5">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource TextTruncationConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Padding" Value="5" />
</Style>
</ListView.ItemContainerStyle>
</ListView>

You do not register attached property but normal DependencyProperty. You should use DependencyProperty.RegisterAttached instead. Also if your class contains only attached properties it may as well be static class. It does not have to inherit from DependencyObject
public static class AttachedProperties
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.RegisterAttached("Text", typeof(string), typeof(AttachedProperties), new PropertyMetadata(null, OnPropertyChangedCallback));
public static void SetText(DependencyObject d, string value)
{
d.SetValue(TextProperty, value);
}
public static string GetText(DependencyObject d)
{
return d.GetValue(TextProperty) as string;
}
private static void OnPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var richEditBox = d as RichTextBox;
var textRange = new TextRange(richEditBox.Document.ContentStart, richEditBox.Document.ContentEnd);
throw new NotImplementedException();
}
}

Related

How to defeat a bug with Grid.Row / Column in ItemsPanelTemplate?

Created a simple Attached property to simplify bindings from an element template.
Instead of this:
<ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
ItemsPanel="{StaticResource Grid.Panel}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="Point">
<Ellipse Fill="Coral"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Y}"/>
<Setter Property="Grid.Column" Value="{Binding X}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
You can go this way:
<ItemsControl Grid.Row="1"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
ItemsPanel="{StaticResource Grid.Panel}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="Point">
<Ellipse Fill="LightBlue"
pa:Grid.Row="{Binding Y}"
pa:Grid.Column="{Binding X}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the complete code the attached property:
public static partial class Grid
{
public static int GetRow(FrameworkElement element)
{
return (int)element.GetValue(RowProperty);
}
public static void SetRow(FrameworkElement element, int value)
{
element.SetValue(RowProperty, value);
}
// Using a DependencyProperty as the backing store for Row. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RowProperty =
DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid),
new FrameworkPropertyMetadata
(
0,
FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
RowChanged
));
private static void RowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement element))
throw new ArgumentException("Must be FrameworkElement", nameof(d));
FrameworkElement parent;
while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
element = parent;
if (parent is System.Windows.Controls.Grid grid)
element.SetValue(System.Windows.Controls.Grid.RowProperty, (int)e.NewValue);
}
private static void GridLoaded(object sender, RoutedEventArgs e)
=> ((System.Windows.Controls.Grid)sender).InvalidateMeasure();
public static int GetColumn(FrameworkElement element)
{
return (int)element.GetValue(ColumnProperty);
}
public static void SetColumn(FrameworkElement element, int value)
{
element.SetValue(ColumnProperty, value);
}
// Using a DependencyProperty as the backing store for Column. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnProperty =
DependencyProperty.RegisterAttached("Column", typeof(int), typeof(Grid),
new FrameworkPropertyMetadata
(
0,
FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
ColumnChanged
));
private static void ColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement element))
throw new ArgumentException("Must be FrameworkElement", nameof(d));
FrameworkElement parent;
while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
element = parent;
if (parent is System.Windows.Controls.Grid grid)
element.SetValue(System.Windows.Controls.Grid.ColumnProperty, (int)e.NewValue);
}
}
The property is nothing complicated.
With Canvas, the same works fine.
And with the Grid, problems arise when attaching a collection with elements or when adding the first element to the collection - elements are displayed in the Grid without regard to their position.
Although when viewing in the visual tree and in the properties browser, the attached properties of Grid.Row / Column are set correctly.
And at the slightest change in the window, the elements fall into place.
In my opinion, a frank bug.
But how to deal with it?
Full XAML Demo Code:
<Window x:Class="AttachedPropertiesWPF.BindParentWind"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AttachedPropertiesWPF"
mc:Ignorable="d"
Title="BindParentWind" Height="450" Width="800"
xmlns:pa="clr-namespace:AttachedProperties;assembly=AttachedProperties">
<Window.Resources>
<x:Array x:Key="Points.Grid" Type="Point">
<Point X="1" Y="0"/>
<Point X="0" Y="2"/>
<Point X="2" Y="1"/>
</x:Array>
<ItemsPanelTemplate x:Key="Grid.Panel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
ItemsPanel="{StaticResource Grid.Panel}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="Point">
<Ellipse Fill="Coral"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Y}"/>
<Setter Property="Grid.Column" Value="{Binding X}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
<ItemsControl Grid.Row="1"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
ItemsPanel="{StaticResource Grid.Panel}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="Point">
<Ellipse Fill="LightBlue"
pa:Grid.Row="{Binding Y}"
pa:Grid.Column="{Binding X}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
Outputs like this:
And it should be like this:
It seems to be kind of a timing issue that initially setting Grid.Row and Grid.Column on the ContentPresenter won't result in another layout cycle.
While it would obviously be a good idea to drop the whole helper class and set the Grid properties directly in an ItemContainerStyle, an ugly workaround would be to asynchronously set the Grid properties, like shown here:
public class ContentPresenterHelper
{
public static readonly DependencyProperty ColumnProperty =
DependencyProperty.RegisterAttached(
"Column", typeof(int), typeof(ContentPresenterHelper),
new PropertyMetadata(0, ColumnPropertyChanged));
public static readonly DependencyProperty RowProperty =
DependencyProperty.RegisterAttached(
"Row", typeof(int), typeof(ContentPresenterHelper),
new PropertyMetadata(0, RowPropertyChanged));
public static int GetRow(DependencyObject o)
{
return (int)o.GetValue(RowProperty);
}
public static void SetColumn(DependencyObject o, int value)
{
o.SetValue(ColumnProperty, value);
}
public static int GetColumn(DependencyObject o)
{
return (int)o.GetValue(ColumnProperty);
}
public static void SetRow(DependencyObject o, int value)
{
o.SetValue(RowProperty, value);
}
private static void ColumnPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
o.Dispatcher.InvokeAsync(() =>
FindContentPresenterParent(o)?.SetValue(
Grid.ColumnProperty, (int)e.NewValue));
}
private static void RowPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
o.Dispatcher.InvokeAsync(() =>
FindContentPresenterParent(o)?.SetValue(
Grid.RowProperty, (int)e.NewValue));
}
private static ContentPresenter FindContentPresenterParent(DependencyObject element)
{
if (element == null)
{
return null;
}
var parent = VisualTreeHelper.GetParent(element);
return (parent as ContentPresenter) ?? FindContentPresenterParent(parent);
}
}
Welcome to SO!
I'll be honest, there are many, many problems with this code, but I'll stick to what you've posted....
Clemens is correct, it looks like you're a bit confused as to how you should be positioning your elements on the grid. In your first ItemsControl you're doing it via the ItemContainerStyle, in the second one you're applying it directly to the Ellipse (albeit, confusingly, using your custom Grid helper DPs). What you do to the first control won't affect the second one, so of course the layout behavior you see will be different between them as well.
Item templates such as your ellipse don't get added to the parent panel container directly, they get encapsulated in a ContentPresenter. So your first control is doing it properly. Set the ItemContainerStyle in your second ItemsControl as well, remove the ap:Grid.Row and ap:Grid.Column setters from the Ellipse tag in your second control, and get rid of that Grid helper class altogether, you don't need it.
Working implementation of Attached Properties:
public static partial class Grid
{
public static int GetRow(FrameworkElement element)
{
return (int)element.GetValue(RowProperty);
}
public static void SetRow(FrameworkElement element, int value)
{
element.SetValue(RowProperty, value);
}
// Using a DependencyProperty as the backing store for Row. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RowProperty =
DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid),
new FrameworkPropertyMetadata
(
0,
FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure
| FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure
| FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
RowChanged
));
private static void RowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement element))
throw new ArgumentException("Must be FrameworkElement", nameof(d));
FrameworkElement parent;
while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
element = parent;
if (parent is System.Windows.Controls.Grid grid)
element.Dispatcher.BeginInvoke((Action<FrameworkElement, DependencyProperty, object>)SetValueAsync, element, System.Windows.Controls.Grid.RowProperty, (int)e.NewValue);
}
private static void SetValueAsync(FrameworkElement element, DependencyProperty property, object value)
=> element.SetValue(property, value);
public static int GetColumn(FrameworkElement element)
{
return (int)element.GetValue(ColumnProperty);
}
public static void SetColumn(FrameworkElement element, int value)
{
element.SetValue(ColumnProperty, value);
}
// Using a DependencyProperty as the backing store for Column. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnProperty =
DependencyProperty.RegisterAttached("Column", typeof(int), typeof(Grid),
new FrameworkPropertyMetadata
(
0,
FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure
| FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure
| FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
ColumnChanged
));
private static void ColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement element))
throw new ArgumentException("Must be FrameworkElement", nameof(d));
FrameworkElement parent;
while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
element = parent;
if (parent is System.Windows.Controls.Grid grid)
element.Dispatcher.BeginInvoke((Action<FrameworkElement, DependencyProperty, object>)SetValueAsync, element, System.Windows.Controls.Grid.ColumnProperty, (int)e.NewValue);
}
}
I have not figured out the reasons for the incomprehensible behavior of the basу properties Grid.Row/Column.
Later I will watch their source code.
If I understand the reason, I'll post it here.

how to make a dependency property of type bool a two way binding

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);

What the correct way to Binding ObservableCollection<Block> to ItemsControl that stored in FlowDocument's Paragraph?

I trying to binding ObservableCollection to ItemsControl that stored in FlowDocument's Paragraph but the blocks doesn't design the content as expected..
I have a custom control of wrapped control, as follow:
public class HelpCtrl : Control
{
static HelpCtrl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HelpCtrl), new FrameworkPropertyMetadata(typeof(HelpCtrl)));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(HelpCtrl), new UIPropertyMetadata(null, OnTextChanged));
static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MemoryStream stream = new MemoryStream(File.ReadAllBytes(e.NewValue.ToString()));
RichTextBox r = new RichTextBox();
r.Selection.Load(stream, DataFormats.Rtf);
HelpCtrl helpCtrl = d as HelpCtrl;
ObservableCollection<Block> l = new ObservableCollection<Block>();
while (r.Document.Blocks.Count > 0)
{
var block = r.Document.Blocks.FirstBlock;
r.Document.Blocks.Remove(block);
l.Add(block);
}
helpCtrl.Blocks = l;
}
public ObservableCollection<Block> Blocks
{
get { return (ObservableCollection<Block>)GetValue(BlocksProperty); }
set { SetValue(BlocksProperty, value); }
}
public static readonly DependencyProperty BlocksProperty =
DependencyProperty.Register("Blocks", typeof(ObservableCollection<Block>), typeof(HelpCtrl), new UIPropertyMetadata(null));
}
<Style TargetType="{x:Type local:HelpCtrl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:HelpCtrl}">
<Grid>
<Grid x:Name="gridRichTextBox" Background="Black" Width="350" Height="600">
<RichTextBox
IsReadOnly="True"
x:Name="rtbHelpText"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
>
<FlowDocument>
<Paragraph>
<ItemsControl ItemsSource="{Binding Blocks, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:HelpCtrl}}}"/>
</Paragraph>
</FlowDocument>
</RichTextBox>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I load from a rtf file the content and design it, but i see that the binding isn't good, the results:
"System.Windows.Documents.Paragraph".
How to fix it?

how to binding something to groupstyle's header

Using CollectionViewSource can easily grouping the ListBoxItem.
But each group Header, only have a "Name" data.
How to put more data to the Header?
like the picture.
i want to use a custom control in the header, and binding a viewmodel to it.
but i dont know how to get the viewmodel from the grouping parent.
and, the <ItemsPresenter/>, groupItem's part.if i want binding something to it,
what can i do?
and , my code:
<ListBox ItemsSource="{Binding Source={StaticResource XamlBookMarks}}"
ItemTemplate="{StaticResource TemplateBookMark}">
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource StyleBookMarkGroup}" />
</ListBox.GroupStyle>
</ListBox>
<Style x:Key="StyleWorkSiteGroup" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<StackPanel>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TemplateHeader}" />
<!--here, i want binding something to ItemsPresenter-->
<ItemsPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="TemplateHeader" DataType="{x:Type GroupItem}">
<DockPanel>
<DockPanel.Resources>
<converters:HeaderGetter x:Key="HeaderConverter" />
</DockPanel.Resources>
<ContentControl DockPanel.Dock="Top" Content="{Binding Name, Converter={StaticResource HeaderConverter}, ConverterParameter={StaticResource XamlHeaders}}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:GroupOneHeaderViewModel}">
<views:GroupOneHeaderView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:GroupTwoHeaderViewModel}">
<views:GroupTwoHeaderView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</DataTemplate>
I made a wrapper class , but feel very ugly . Is there any other way to do this ?
1,get the Transition class from
http://www.11011.net/wpf-transitions
2.and
public class GroupItemsSelector : FrameworkElement
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source",
typeof (CollectionViewSource),
typeof (GroupItemsSelector),
new PropertyMetadata(OnSourceChangedCallback));
public static readonly DependencyProperty IdProperty =
DependencyProperty.Register("Id",
typeof (string),
typeof (GroupItemsSelector),
new PropertyMetadata(OnSourceChangedCallback));
public static readonly DependencyProperty CurrentProperty =
DependencyProperty.Register("Current",
typeof (object),
typeof (GroupItemsSelector),
new PropertyMetadata(OnCurrentItemChanged));
public static readonly DependencyProperty WatchingProperty =
DependencyProperty.Register("Watching",
typeof (string),
typeof (GroupItemsSelector),
new PropertyMetadata(OnCurrentItemChanged));
public static readonly DependencyProperty WatchingValueProperty =
DependencyProperty.Register("WatchingValue",
typeof (object),
typeof (GroupItemsSelector));
private readonly Transition _transition = new Transition();
public GroupItemsSelector()
{
var dpd = DependencyPropertyDescriptor.FromProperty(Transition.StateProperty, typeof (Transition));
if (dpd == null) return;
dpd.AddValueChanged(_transition, delegate { WatchingValue = _transition.Source; });
}
public object WatchingValue
{
get { return GetValue(WatchingValueProperty); }
private set { SetValue(WatchingValueProperty, value); }
}
public string Watching
{
get { return (string) GetValue(WatchingProperty); }
set { SetValue(WatchingProperty, value); }
}
public CollectionViewSource Source
{
get { return (CollectionViewSource) GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public string Id
{
get { return (string) GetValue(IdProperty); }
set { SetValue(IdProperty, value); }
}
public object Current
{
get { return GetValue(CurrentProperty); }
private set { SetValue(CurrentProperty, value); }
}
private static void OnCurrentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = d as GroupItemsSelector;
if (selector == null) return;
selector.SetWatchingProperty();
}
private static void OnSourceChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = d as GroupItemsSelector;
if (selector == null) return;
selector.SetCurrent();
}
private void SetCurrent()
{
IDictionary dictionary = null;
bool hasValue = false;
try
{
if (Source == null || string.IsNullOrEmpty(Id)) return;
dictionary = Source.Source as IDictionary;
if (dictionary == null) return;
hasValue = dictionary.Contains(Id);
}
finally
{
Current = (hasValue) ? dictionary[Id] : null;
}
}
private void SetWatchingProperty()
{
_transition.DataContext = Current as INotifyPropertyChanged;
if (string.IsNullOrEmpty(Watching))
{
BindingOperations.ClearBinding(_transition, Transition.SourceProperty);
}
else
{
_transition.SetBinding(Transition.SourceProperty, new Binding(Watching));
}
}
}
3. and sample
<Style x:Key="StyleGroupItem" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<StackPanel>
<converters:GroupItemsSelector x:Name="selector" Id="{Binding Name}" Source="{StaticResource XamlHeaders}" Watching="IsExpanded"/>
<ContentControl Content="{Binding ElementName=selector, Path=Current}" >
</ContentControl>
<ItemsPresenter Visibility="{Binding ElementName=selector, Path=WatchingValue, Converter={x:Static converters:BoolVisiblilityConverter.Instance}}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Dependency property not working, trying to set through style setter

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);
}

Resources