I am writing two dependency properties and I keep getting the "[Property] was already registered by 'FrameworkElement'" error in the design window of VS11. Here is a snippet of my code
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register("IsEditingNumbers", typeof(bool), typeof(FrameworkElement),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
the problem seems to be the 3rd parameter (owner parameter typeof(FrameworkElement)). If I set the 3rd parameter to the class the contains two dependency properties, the error goes away, but I cannot use the properties directly from xaml. I would have to add ownership for each dependency property before I use it.
Actually, It does render correctly, but only when I first open it. Immediately after the first render it will give me an exception. At runtime, it seems to work perfectly.
Am I doing something wrong and is there a way to get rid of this annoying error?
---- Edit -----
Here is my custom class (includes 2 of the Dependency Properties):
public partial class EditableTextBox : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register("IsEditingNumber", typeof(bool), typeof(FrameworkElement),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(FrameworkElement),
new FrameworkPropertyMetadata("0", FrameworkPropertyMetadataOptions.AffectsRender)
{
CoerceValueCallback = new CoerceValueCallback((sender,value) =>
{
return expressionRestaraint.Match((string)value).Value;
})
});
#endregion
public static Regex expressionRestaraint = new Regex("[-a-zA-z0-9+*.\\(\\)\\[\\]\\{\\}]*");
public string Text
{
get { (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
tbxValue.Text = (string)GetValue(TextProperty);
}
}
public bool IsEditingNumber
{
get
{
return (bool)GetValue(IsEditingNumberProperty);
}
set
{
bool old = (bool)GetValue(IsEditingNumberProperty);
if (old != value)
{
if (!value)
stopEditing();
else
startEditing();
SetValue(IsEditingNumberProperty, value);
}
}
} . . .
Use in Main Class:
<Window x:Class="VisualMathExpression.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:edit="clr-namespace:VisualMathExpression.EditableTextBox"
xmlns:all="clr-namespace:VisualMathExpression"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<edit:EditableTextBox HorizontalAlignment="Center" VerticalAlignment="Center"
Text="af" IsEditingNumber="True" /> . . .
--- Edit ---
Wrapper fixed (problem that cause xaml property not to change when ownership belonged to the declared class)
public partial class EditableTextBox : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register("IsEditingNumber", typeof(bool), typeof(EditableTextBox),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender)
{
PropertyChangedCallback = new PropertyChangedCallback((sender, arg) =>
{
EditableTextBox ed = sender as EditableTextBox;
if (!(bool)arg.NewValue)
ed.stopEditing();
else
ed.startEditing();
}),
});
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBox),
new FrameworkPropertyMetadata("0", FrameworkPropertyMetadataOptions.AffectsRender)
{
PropertyChangedCallback = new PropertyChangedCallback((sender,arg) =>
{
EditableTextBox ed = sender as EditableTextBox;
ed.tbxValue.Text = arg.NewValue as string;
}),
CoerceValueCallback = new CoerceValueCallback((sender,value) =>
{
return expressionRestaraint.Match((string)value).Value;
})
});
#endregion
public static Regex expressionRestaraint = new Regex("[-a-zA-z0-9+*.\\(\\)\\[\\]\\{\\}]*");
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsEditingNumber
{
get { return (bool)GetValue(IsEditingNumberProperty); }
set { SetValue(IsEditingNumberProperty, value); }
}
The third parameter ownerType of the DependencyProperty.Register method must be the class that declares the property.
If your class is MyClass the declaration would have to look like this:
public class MyClass : DependencyObject
{
public static readonly DependencyProperty IsEditingNumberProperty =
DependencyProperty.Register(
"IsEditingNumber", typeof(bool), typeof(MyClass), ...);
// CLR wrapper
public bool IsEditingNumber
{
get { return (bool)GetValue(IsEditingNumberProperty); }
set { SetValue(IsEditingNumberProperty, value); }
}
}
I have a user control which exposes a property which is a long. I'd like to instantiate this control and bind to the exposed property in a data template.
I'm seeing xaml errors in the resource file. The ambiguous "must have derivative of panel as the root element". And when I run this in a debugger, I see that the value of TeamIdx is -1 and is not being set.
<DataTemplate x:Key="TeamScheduleTemplate">
<Grid HorizontalAlignment="Left" Width="400" Height="600">
<Team:ScheduleControl TeamIdx="{Binding Idx}" />
</Grid>
</DataTemplate>
public sealed partial class ScheduleControl : UserControl
{
public static readonly DependencyProperty TeamIdxProperty =
DependencyProperty.Register(
"TeamIdx",
typeof(long),
typeof(ScheduleControl),
new PropertyMetadata((long)-1));
public long TeamIdx
{
get { return (long)GetValue(TeamIdxProperty); }
set { SetValue(TeamIdxProperty, value); }
}
public ScheduleControl()
{
this.InitializeComponent();
var team = TeamLookup.GetTeam(TeamIdx);
}
}
Edit: It turns out that the binding doesn't happen until after the control is constructed. In retrospect, this makes total sense. The solution I used is below:
public sealed partial class ScheduleControl : UserControl
{
public static readonly DependencyProperty TeamIdxProperty =
DependencyProperty.Register(
"TeamIdx",
typeof(long),
typeof(ScheduleControl),
new PropertyMetadata(
(long)-1,
OnTeamIdxChanged));
public long TeamIdx
{
get { return (long)GetValue(TeamIdxProperty); }
set { SetValue(TeamIdxProperty, value); }
}
private static void OnTeamIdxChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var target = (ScheduleControl)sender;
target.OnTeamIdxChanged((long)e.NewValue);
}
private void OnTeamIdxChanged(long id)
{
var model = FindModel(id);
this.DataContext = model;
}
public ScheduleControl()
{
this.InitializeComponent();
}
}
I'm using C# 4.0 and have created a DependencyObject MyView.
In MyView, I have two DependencyProperties, PropA and PropB, both are booleans.
I want a third DependencyProperty, PropC, also a bool, and simply put, should always give me (PropA || PropB).
What is the best way to accomplish this?
I was also thinking of making PropC a readonly DependencyProperty, but have read about issues with binding to readonly dp's (WPF ReadOnly Dependency Properties using MVVM)
You can use the Dependency Property changed callback for PropA and PropB to set the value for PropC (don't use the CLR property wrapper for the Dependency Properties as they are never guaranteed to be called).
If you have these three DP's
public static readonly DependencyProperty PropAProperty =
DependencyProperty.Register("PropA",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false, PropAPropertyChanged));
public static readonly DependencyProperty PropBProperty =
DependencyProperty.Register("PropB",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false, PropBPropertyChanged));
public static readonly DependencyProperty PropCProperty =
DependencyProperty.Register("PropC",
typeof(bool),
typeof(MyView),
new PropertyMetadata(false));
public bool PropA
{
get { return (bool)this.GetValue(PropAProperty); }
set { this.SetValue(PropAProperty, value); }
}
public bool PropB
{
get { return (bool)this.GetValue(PropBProperty); }
set { this.SetValue(PropBProperty, value); }
}
public bool PropC
{
get { return (bool)this.GetValue(PropCProperty); }
set { this.SetValue(PropCProperty, value); }
}
you can use the property changed callback like this
private static void PropAPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MyView myView = source as MyView;
myView.OnPropChanged();
}
private static void PropBPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MyView myView = source as MyView;
myView.OnPropChanged();
}
public void OnPropChanged()
{
PropC = PropA || PropB;
}
This way, you'll always update the value of PropC everytime PropA or PropB changes
Also, PropC doesn't need to be a DP, it can be a normal CLR property if you implement INotifyPropertyChanged. Then the implementation can look like this instead
public void OnPropChanged()
{
OnPropertyChanged("PropC");
}
public bool PropC
{
get
{
return PropA || PropB;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You could also bind PropC to PropA and PropB with a MultiBinding. Let me know if you want an example of this as well
The linked web page is for an unusual situation, a "push" binding. That is, a one-way-to-source binding was attempted on the read-only property, not on another property trying to bind to it. By contrast, if you want your property to be bindable to other properties using a one-way binding expression on the other property, then you can use a read-only dependency property without any issues.
Edit:
Here is an example:
<Grid>
<Grid.Resources>
<local:MyObject x:Key="myObject" PropertyA="True" PropertyB="False"/>
</Grid.Resources>
<StackPanel DataContext="{StaticResource myObject}">
<CheckBox IsChecked="{Binding PropertyA}" Content="PropertyA"/>
<CheckBox IsChecked="{Binding PropertyB}" Content="PropertyB"/>
<CheckBox IsChecked="{Binding PropertyC, Mode=OneWay}" IsEnabled="False" Content="PropertyC"/>
</StackPanel>
</Grid>
and the dependency properties, one of which is read-only:
public class MyObject : DependencyObject
{
public bool PropertyA
{
get { return (bool)GetValue(PropertyAProperty); }
set { SetValue(PropertyAProperty, value); }
}
public static readonly DependencyProperty PropertyAProperty =
DependencyProperty.Register("PropertyA", typeof(bool), typeof(MyObject), new UIPropertyMetadata(false, OnPropertyAOrBChanged));
public bool PropertyB
{
get { return (bool)GetValue(PropertyBProperty); }
set { SetValue(PropertyBProperty, value); }
}
public static readonly DependencyProperty PropertyBProperty =
DependencyProperty.Register("PropertyB", typeof(bool), typeof(MyObject), new UIPropertyMetadata(false, OnPropertyAOrBChanged));
public bool PropertyC
{
get { return (bool)GetValue(PropertyCProperty); }
set { SetValue(PropertyCPropertyKey, value); }
}
private static readonly DependencyPropertyKey PropertyCPropertyKey =
DependencyProperty.RegisterReadOnly("PropertyC", typeof(bool), typeof(MyObject), new UIPropertyMetadata());
public static readonly DependencyProperty PropertyCProperty = PropertyCPropertyKey.DependencyProperty;
private static void OnPropertyAOrBChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myObject = d as MyObject;
myObject.PropertyC = myObject.PropertyA || myObject.PropertyB;
}
}
XAML:
<navigation:Page ... Title="{Binding Name}">
C#
public TablePage()
{
this.DataContext = new Table()
{
Name = "Finding Table"
};
InitializeComponent();
}
Getting a ag_e_parser_bad_property_value error in InitializeComponent at the point where the title binding is happening. I've tried adding static text which works fine. If I use binding anywhere else eg:
<TextBlock Text="{Binding Name}"/>
This doesn't work either.
I'm guessing it's complaining because the DataContext object isn't set but if I put in a break point before the InitializeComponent I can confirm it is populated and the Name property is set.
Any ideas?
You can only use data binding on properties that are supported by DependencyProperty. If you take a look at the docs for TextBlock for example you will find that the Text property has a matching TextProperty public static field of type DependencyProperty.
If you look at the docs for Page you will find that there is no TitleProperty defined, the Title property is therefore not a dependency property.
Edit
There is no way to "override" this however you could create an attached property:-
public static class Helper
{
#region public attached string Title
public static string GetTitle(Page element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return element.GetValue(TitleProperty) as string;
}
public static void SetTitle(Page element, string value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(TitleProperty, value);
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.RegisterAttached(
"Title",
typeof(string),
typeof(Helper),
new PropertyMetadata(null, OnTitlePropertyChanged));
private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Page source = d as Page;
source.Title = e.NewValue as string;
}
#endregion public attached string Title
}
Now your page xaml might look a bit like:-
<navigation:Page ...
xmlns:local="clr-namespace:SilverlightApplication1"
local:Helper.Title="{Binding Name}">
Add the following to MyPage.xaml.cs:
public new string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title",
typeof(string),
typeof(Page),
new PropertyMetadata(""));
Once you add this property (dependency property) to your code behind, your code will work as normal.
I'm trying to bind to a Readonly property with OneWayToSource as mode, but it seems this cannot be done in XAML:
<controls:FlagThingy IsModified="{Binding FlagIsModified,
ElementName=container,
Mode=OneWayToSource}" />
I get:
The property 'FlagThingy.IsModified' cannot be set because it does not have an accessible set accessor.
IsModified is a readonly DependencyProperty on FlagThingy. I want to bind that value to the FlagIsModified property on the container.
To be clear:
FlagThingy.IsModified --> container.FlagIsModified
------ READONLY ----- ----- READWRITE --------
Is this possible using just XAML?
Update: Well, I fixed this case by setting the binding on the container and not on the FlagThingy. But I'd still like to know if this is possible.
Some research results for OneWayToSource...
Option # 1.
// Control definition
public partial class FlagThingy : UserControl
{
public static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());
}
<controls:FlagThingy x:Name="_flagThingy" />
// Binding Code
Binding binding = new Binding();
binding.Path = new PropertyPath("FlagIsModified");
binding.ElementName = "container";
binding.Mode = BindingMode.OneWayToSource;
_flagThingy.SetBinding(FlagThingy.IsModifiedProperty, binding);
Option # 2
// Control definition
public partial class FlagThingy : UserControl
{
public static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());
public bool IsModified
{
get { return (bool)GetValue(IsModifiedProperty); }
set { throw new Exception("An attempt ot modify Read-Only property"); }
}
}
<controls:FlagThingy IsModified="{Binding Path=FlagIsModified,
ElementName=container, Mode=OneWayToSource}" />
Option # 3 (True read-only dependency property)
System.ArgumentException: 'IsModified' property cannot be data-bound.
// Control definition
public partial class FlagThingy : UserControl
{
private static readonly DependencyPropertyKey IsModifiedKey =
DependencyProperty.RegisterReadOnly("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());
public static readonly DependencyProperty IsModifiedProperty =
IsModifiedKey.DependencyProperty;
}
<controls:FlagThingy x:Name="_flagThingy" />
// Binding Code
Same binding code...
Reflector gives the answer:
internal static BindingExpression CreateBindingExpression(DependencyObject d, DependencyProperty dp, Binding binding, BindingExpressionBase parent)
{
FrameworkPropertyMetadata fwMetaData = dp.GetMetadata(d.DependencyObjectType) as FrameworkPropertyMetadata;
if (((fwMetaData != null) && !fwMetaData.IsDataBindingAllowed) || dp.ReadOnly)
{
throw new ArgumentException(System.Windows.SR.Get(System.Windows.SRID.PropertyNotBindable, new object[] { dp.Name }), "dp");
}
....
This is a limitation of WPF and it is by design. It is reported on Connect here:
OneWayToSource binding from a readonly dependency property
I made a solution to dynamically be able to push read-only dependency properties to the source called PushBinding which I blogged about here. The example below does OneWayToSource Bindings from the read-only DP's ActualWidth and ActualHeight to the Width and Height properties of the DataContext
<TextBlock Name="myTextBlock">
<pb:PushBindingManager.PushBindings>
<pb:PushBinding TargetProperty="ActualHeight" Path="Height"/>
<pb:PushBinding TargetProperty="ActualWidth" Path="Width"/>
</pb:PushBindingManager.PushBindings>
</TextBlock>
PushBinding works by using two Dependency Properties, Listener and Mirror. Listener is bound OneWay to the TargetProperty and in the PropertyChangedCallback it updates the Mirror property which is bound OneWayToSource to whatever was specified in the Binding.
Demo Project can be Downloaded Here.
It contains source code and short sample usage.
Wrote this:
Usage:
<TextBox Text="{Binding Text}"
p:OneWayToSource.Bind="{p:Paths From={x:Static Validation.HasErrorProperty},
To=SomeDataContextProperty}" />
Code:
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
public static class OneWayToSource
{
public static readonly DependencyProperty BindProperty = DependencyProperty.RegisterAttached(
"Bind",
typeof(ProxyBinding),
typeof(OneWayToSource),
new PropertyMetadata(default(Paths), OnBindChanged));
public static void SetBind(this UIElement element, ProxyBinding value)
{
element.SetValue(BindProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static ProxyBinding GetBind(this UIElement element)
{
return (ProxyBinding)element.GetValue(BindProperty);
}
private static void OnBindChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ProxyBinding)e.OldValue)?.Dispose();
}
public class ProxyBinding : DependencyObject, IDisposable
{
private static readonly DependencyProperty SourceProxyProperty = DependencyProperty.Register(
"SourceProxy",
typeof(object),
typeof(ProxyBinding),
new PropertyMetadata(default(object), OnSourceProxyChanged));
private static readonly DependencyProperty TargetProxyProperty = DependencyProperty.Register(
"TargetProxy",
typeof(object),
typeof(ProxyBinding),
new PropertyMetadata(default(object)));
public ProxyBinding(DependencyObject source, DependencyProperty sourceProperty, string targetProperty)
{
var sourceBinding = new Binding
{
Path = new PropertyPath(sourceProperty),
Source = source,
Mode = BindingMode.OneWay,
};
BindingOperations.SetBinding(this, SourceProxyProperty, sourceBinding);
var targetBinding = new Binding()
{
Path = new PropertyPath($"{nameof(FrameworkElement.DataContext)}.{targetProperty}"),
Mode = BindingMode.OneWayToSource,
Source = source
};
BindingOperations.SetBinding(this, TargetProxyProperty, targetBinding);
}
public void Dispose()
{
BindingOperations.ClearAllBindings(this);
}
private static void OnSourceProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(TargetProxyProperty, e.NewValue);
}
}
}
[MarkupExtensionReturnType(typeof(OneWayToSource.ProxyBinding))]
public class Paths : MarkupExtension
{
public DependencyProperty From { get; set; }
public string To { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var targetObject = (UIElement)provideValueTarget.TargetObject;
return new OneWayToSource.ProxyBinding(targetObject, this.From, this.To);
}
}
Have not tested it in styles and templates yet, guess it needs special casing.
Here is another implementation for binding to Validation.HasError
public static class OneWayToSource
{
public static readonly DependencyProperty BindingsProperty = DependencyProperty.RegisterAttached(
"Bindings",
typeof(OneWayToSourceBindings),
typeof(OneWayToSource),
new PropertyMetadata(default(OneWayToSourceBindings), OnBinidngsChanged));
public static void SetBindings(this FrameworkElement element, OneWayToSourceBindings value)
{
element.SetValue(BindingsProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(FrameworkElement))]
public static OneWayToSourceBindings GetBindings(this FrameworkElement element)
{
return (OneWayToSourceBindings)element.GetValue(BindingsProperty);
}
private static void OnBinidngsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((OneWayToSourceBindings)e.OldValue)?.ClearValue(OneWayToSourceBindings.ElementProperty);
((OneWayToSourceBindings)e.NewValue)?.SetValue(OneWayToSourceBindings.ElementProperty, d);
}
}
public class OneWayToSourceBindings : FrameworkElement
{
private static readonly PropertyPath DataContextPath = new PropertyPath(nameof(DataContext));
private static readonly PropertyPath HasErrorPath = new PropertyPath($"({typeof(Validation).Name}.{Validation.HasErrorProperty.Name})");
public static readonly DependencyProperty HasErrorProperty = DependencyProperty.Register(
nameof(HasError),
typeof(bool),
typeof(OneWayToSourceBindings),
new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
internal static readonly DependencyProperty ElementProperty = DependencyProperty.Register(
"Element",
typeof(UIElement),
typeof(OneWayToSourceBindings),
new PropertyMetadata(default(UIElement), OnElementChanged));
private static readonly DependencyProperty HasErrorProxyProperty = DependencyProperty.RegisterAttached(
"HasErrorProxy",
typeof(bool),
typeof(OneWayToSourceBindings),
new PropertyMetadata(default(bool), OnHasErrorProxyChanged));
public bool HasError
{
get { return (bool)this.GetValue(HasErrorProperty); }
set { this.SetValue(HasErrorProperty, value); }
}
private static void OnHasErrorProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(HasErrorProperty, e.NewValue);
}
private static void OnElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
{
BindingOperations.ClearBinding(d, DataContextProperty);
BindingOperations.ClearBinding(d, HasErrorProxyProperty);
}
else
{
var dataContextBinding = new Binding
{
Path = DataContextPath,
Mode = BindingMode.OneWay,
Source = e.NewValue
};
BindingOperations.SetBinding(d, DataContextProperty, dataContextBinding);
var hasErrorBinding = new Binding
{
Path = HasErrorPath,
Mode = BindingMode.OneWay,
Source = e.NewValue
};
BindingOperations.SetBinding(d, HasErrorProxyProperty, hasErrorBinding);
}
}
}
Usage in xaml
<StackPanel>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}">
<local:OneWayToSource.Bindings>
<local:OneWayToSourceBindings HasError="{Binding HasError}" />
</local:OneWayToSource.Bindings>
</TextBox>
<CheckBox IsChecked="{Binding HasError, Mode=OneWay}" />
</StackPanel>
This implementation is specific to binding Validation.HasError
Here's another attached property solution based on SizeObserver detailed here Pushing read-only GUI properties back into ViewModel
public static class MouseObserver
{
public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached(
"Observe",
typeof(bool),
typeof(MouseObserver),
new FrameworkPropertyMetadata(OnObserveChanged));
public static readonly DependencyProperty ObservedMouseOverProperty = DependencyProperty.RegisterAttached(
"ObservedMouseOver",
typeof(bool),
typeof(MouseObserver));
public static bool GetObserve(FrameworkElement frameworkElement)
{
return (bool)frameworkElement.GetValue(ObserveProperty);
}
public static void SetObserve(FrameworkElement frameworkElement, bool observe)
{
frameworkElement.SetValue(ObserveProperty, observe);
}
public static bool GetObservedMouseOver(FrameworkElement frameworkElement)
{
return (bool)frameworkElement.GetValue(ObservedMouseOverProperty);
}
public static void SetObservedMouseOver(FrameworkElement frameworkElement, bool observedMouseOver)
{
frameworkElement.SetValue(ObservedMouseOverProperty, observedMouseOver);
}
private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = (FrameworkElement)dependencyObject;
if ((bool)e.NewValue)
{
frameworkElement.MouseEnter += OnFrameworkElementMouseOverChanged;
frameworkElement.MouseLeave += OnFrameworkElementMouseOverChanged;
UpdateObservedMouseOverForFrameworkElement(frameworkElement);
}
else
{
frameworkElement.MouseEnter -= OnFrameworkElementMouseOverChanged;
frameworkElement.MouseLeave -= OnFrameworkElementMouseOverChanged;
}
}
private static void OnFrameworkElementMouseOverChanged(object sender, MouseEventArgs e)
{
UpdateObservedMouseOverForFrameworkElement((FrameworkElement)sender);
}
private static void UpdateObservedMouseOverForFrameworkElement(FrameworkElement frameworkElement)
{
frameworkElement.SetCurrentValue(ObservedMouseOverProperty, frameworkElement.IsMouseOver);
}
}
Declare attached property in control
<ListView ItemsSource="{Binding SomeGridItems}"
ut:MouseObserver.Observe="True"
ut:MouseObserver.ObservedMouseOver="{Binding IsMouseOverGrid, Mode=OneWayToSource}">
WPF will not use the CLR property setter, but seems it does some odd validation based on it.
May be in your situation this can be ok:
public bool IsModified
{
get { return (bool)GetValue(IsModifiedProperty); }
set { throw new Exception("An attempt ot modify Read-Only property"); }
}
Hmmm... I'm not sure I agree with any of these solutions. How about specifying a coercion callback in your property registration that ignores external change? For instance, I needed to implement a read-only Position dependency property to get the position of a MediaElement control inside a user control. Here's how I did it:
public static readonly DependencyProperty PositionProperty = DependencyProperty.Register("Position", typeof(double), typeof(MediaViewer),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnPositionChanged, OnPositionCoerce));
private static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctrl = d as MediaViewer;
}
private static object OnPositionCoerce(DependencyObject d, object value)
{
var ctrl = d as MediaViewer;
var position = ctrl.MediaRenderer.Position.TotalSeconds;
if (ctrl.MediaRenderer.NaturalDuration.HasTimeSpan == false)
return 0d;
else
return Math.Min(position, ctrl.Duration);
}
public double Position
{
get { return (double)GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); }
}
In other words, simply ignore the change and return the value backed by a different member that does not have a public modifier. -- In the above example, MediaRenderer is actually the private MediaElement control.
The way I worked around this limitation was to expose only a Binding property in my class, keeping the DependencyProperty private altogether. I implemented a "PropertyBindingToSource" write-only property (this one not a DependencyProperty) which can be set to a binding value in the xaml. In the setter for this write-only property I call to BindingOperations.SetBinding to link the binding to the DependencyProperty.
For the OP's specific example, it would look like this:
The FlatThingy implementation:
public partial class FlatThingy : UserControl
{
public FlatThingy()
{
InitializeComponent();
}
public Binding IsModifiedBindingToSource
{
set
{
if (value?.Mode != BindingMode.OneWayToSource)
{
throw new InvalidOperationException("IsModifiedBindingToSource must be set to a OneWayToSource binding");
}
BindingOperations.SetBinding(this, IsModifiedProperty, value);
}
}
public bool IsModified
{
get { return (bool)GetValue(IsModifiedProperty); }
private set { SetValue(IsModifiedProperty, value); }
}
private static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(FlatThingy), new PropertyMetadata(false));
private void Button_Click(object sender, RoutedEventArgs e)
{
IsModified = !IsModified;
}
}
Notice that the static readonly DependencyProperty object is private. In the control I added a button whose click is handled by Button_Click.
The use of the FlatThingy control in my window.xaml:
<Window x:Class="ReadOnlyBinding.MainWindow"
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:ReadOnlyBinding"
mc:Ignorable="d"
DataContext="{x:Static local:ViewModel.Instance}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{Binding FlagIsModified}" Grid.Row="0" />
<local:FlatThingy IsModifiedBindingToSource="{Binding FlagIsModified, Mode=OneWayToSource}" Grid.Row="1" />
</Grid>
Note that I've also implemented a ViewModel for binding to that is not shown here. It exposes a DependencyProperty named "FlagIsModified" as you can glean from the source above.
It works great, allowing me to push information back into the ViewModel from the View in a loosely coupled manner, with the direction of that information flow explicitly defined.
You're doing the binding in the wrong direction right now. OneWayToSource will try and update FlagIsModified on container whenever IsModified changes on the control you are creating. You want the opposite, which is to have IsModified bind to container.FlagIsModified. For that you should use the binding mode OneWay
<controls:FlagThingy IsModified="{Binding FlagIsModified,
ElementName=container,
Mode=OneWay}" />
Full list of enumeration members: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx