Initially, I have the following code:
<TextBox Text="{Binding LengthUnit, Mode=OneWay}" IsReadOnly="True" Background="{x:Static SystemColors.ControlBrush}" />
I know I can define a style like this:
<Style TargetType="{x:Type TextBox}" x:Key="readOnlyTextBox">
<Setter Property="Background" Value="{x:Static SystemColors.ControlBrush}"></Setter>
<Setter Property="IsReadOnly" Value="True"></Setter>
</Style>
So that I can write:
<TextBox Text="{Binding LengthUnit, Mode=OneWay}" Style="{StaticResource readOnlyTextBox}" />
Because this textbox is readonly, the binding mode cannot be twoway. So, is it possible to make the OneWay binding as the default for my TextBox with this style?
EDIT: I need to change the binding mode to OneWay, because my property is get-only, not because I marked the TextBox readonly. However, I still want to change the default binding mode of the textbox to OneWay if possible.
this is the code I have following your suggestion, but it doesn't work. Did I miss anything?
public class ReadOnlyTextBox : TextBox
{
static ReadOnlyTextBox()
{
TextBox.TextProperty.OverrideMetadata(typeof(ReadOnlyTextBox),
new FrameworkPropertyMetadata() { BindsTwoWayByDefault = false, Journal = true, DefaultUpdateSourceTrigger = UpdateSourceTrigger.Explicit });
}
public ReadOnlyTextBox()
{
base.Background = SystemColors.ControlBrush;
base.IsReadOnly = true;
}
}
Because this textbox is readonly, the binding mode cannot be twoway.
Why not? IsReadOnly will prevent the user from modifying the Text and thereby modifying the property. Just make sure not to modify the Text property in code.
You can prevent the bound property from updating if you subclass TextBox. If you do so, you can override the TextBox.Text Dependency Property metadata.
public class TextBoxEx : TextBox
{
public TextBoxEx() : base() { }
static TextBoxEx()
{
TextBox.TextProperty.OverrideMetadata(typeof(TextBoxEx),
new FrameworkPropertyMetadata() { BindsTwoWayByDefault = false, Journal = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.Explicit });
}
}
For some reasion changing BindsTwoWayByDefault to false doesn't work for me, but you can set DefaultUpdateSourceTrigger to Explicit which means that the bound property won't be updated unless done so by code, effectively making the binding OneWay.
Styles are a way to apply the same set of customizations to one or more properties for UI objects e.g. Background, IsReadOnly etc which are typically dependency properties.
Mode is a property of the Binding object, which is not a UI object.
You can set a style on any element
that derives from FrameworkElement or
FrameworkContentElement. -- Source (MSDN)
So this is not typically done via XAML/Styling.. my guess is you'd have to write code for it. Although XAML allows you to set nested properties Text.Mode="value", it is error prone (because it assumes that Text has been already set to a binding object). It will result in a binding exception if Text property returns an object that doesn't have a Mode property on it - e.g. if Text="a plain string".
If you absolutely must have this, then you'd need to create your binding programatically. You could use a naming convention for example to see if the backing property has a setter and add a OneWay binding if it doesn't.
I know this question is really old, but I recently encountered this problem myself, so maybe I can help somebody else as well.
I wanted to create a TextBox that have a OneWayBinding on its Text property.
I discovered that this is not working as shown in the question because WPF combines the existing metadata and the overriding metadata together by basically ORing the flags together.
Since BindsTwoWayByDefault is one of those flags, as long as one of the Metadata objects has BindsTwoWayByDefault=true is stays true.
The only way around that is to change the Metadata after the WPF merging process takes places in OverrideMetadata.
However the Metadata object is marked as Sealed in the method.
As any good developer would I stopped here and reconsidered...
Naaa, I used reflection to "unseal" the metadata object and set the BindsTwoWayByDefault back to false.
If anybody knows a better way to do that please let me know.
Here my code:
public partial class SelectableTextBlock : TextBox
{
static SelectableTextBlock()
{
var defaultMetadata = (FrameworkPropertyMetadata)TextProperty.GetMetadata(typeof(TextBox));
var newMetadata = new FrameworkPropertyMetadata(
defaultMetadata.DefaultValue,
FrameworkPropertyMetadataOptions.Journal,
defaultMetadata.PropertyChangedCallback,
defaultMetadata.CoerceValueCallback,
defaultMetadata.IsAnimationProhibited,
defaultMetadata.DefaultUpdateSourceTrigger);
TextProperty.OverrideMetadata(typeof(SelectableTextBlock), newMetadata);
//Workaround for a bug in WPF were the Metadata is merged wrongly and BindsTwoWayByDefault is always true
var sealedProperty = typeof(PropertyMetadata).GetProperty("Sealed", BindingFlags.Instance | BindingFlags.NonPublic);
sealedProperty.SetValue(newMetadata, false);
newMetadata.BindsTwoWayByDefault = false;
sealedProperty.SetValue(newMetadata, true);
}
public SelectableTextBlock()
{
InitializeComponent();
}
}
Related
I'm very new to WPF and while studying (particularly creating a user control), I stumbled upon this thing called "DependencyProperty".
I understand how it works in code but why and when do we need it when I can just create a property and expose it for public use.
Example:
XAML:
<UserControl.....>
<StackPanel Orientation="Vertical">
<TextBlock x:Name="label" Text="Hello"/>
<TextBlock x:Name="text" Text="World!" />
</StackPanel>
</UserControl>
CS file:
public partial SampleUserCtrl : UserControl
{
public string LabelText { get { return this.label.Text; } set { this.label.Text = value; } }
public string TextBoxText { get { return this.text.Text; } set { this.text.Text = value; } }
}
DependecyProperty in WPF has different uses.
Advantages compared to normal .NET property
Reduced memory footprint It's a huge dissipation to store a field for
each property when you think that over 90% of the properties of a UI
control typically stay at its initial values. Dependency properties
solve these problems by only store modified properties in the
instance. The default values are stored once within the dependency
property.
Value inheritance When you access a dependency property the value is
resolved by using a value resolution strategy. If no local value is
set, the dependency property navigates up the logical tree until it
finds a value. When you set the FontSize on the root element it
applies to all textblocks below except you override the value.
Change notification Dependency properties have a built-in change
notification mechanism. By registering a callback in the property
metadata you get notified, when the value of the property has been
changed. This is also used by the databinding.
As you dig deeper to WPF you will also stumble upon DataBinding and object-oriented design patterns like MVVM or MVPVM. Both patterns rely on DataBinding which is achieved through using of Dependency Properties. You cannot perform data binding if it is not a dependency property.
Basically data binding through dependency properties allow the user to update the view when updating a value through code.
Ex: In Windows Forms in order to update a label text you assign its value on the code behind like this:
lbl1.Text = "foo";
In data binding where as you bind in the XAML (View)
<label Text = "{Binding foo}"></label>
and update the values in your code behind:
foo = "foo";
I am not an expert myself so sorry if I might sound confusing.
Dependency property has many benefits over normal property.
A dependency property value can be set by referencing a resource
It can reference a value through data binding
It can be animated. When an animation is
applied and is running, the animated value operates at a higher
precedence than any value (such as a local value) that the property
otherwise has.
You can learn more about it from msdn.
The main advantage of DP property you can find anything in DP like your Control info and you current DataContext info.
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(MyUserControl), new PropertyMetadata(null, (s, e) => OnChangedValue(s, e)));
private static void OnChangedValue(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
We can achieve the binding by simply CLR property, so why do we need to use DP?
When do you need DPs over CLRPs?
When you need binding
when you need property value change callback (Default Implementation)
When you need property value validation
When you need animation
When you need property value inheritance
When you need to attach a property value to another element (Attached Property, but still)
When you need styling
Some of these can be implemented in CLR properties. But, with DPs, its piece of cake.
Typically these are declared in UserControls and derived controls.
You can bind to a CLR property, but you can't bind with a CLR property; you'll need a dependency property to do any binding.
Edit (in response to comment)
Let's say you need a TextBox, but you want to customize it to have different behaviour in "EditMode" and "ReadMode". You'll need to either create a derived class or a UserControl; in either case you'll add a DependencyPropery.
public class TextBoxWithModes : TextBox
{
public bool EditMode
{
get { return (bool) GetValue(EditModeProperty); }
set { SetValue(EditModeProperty, value); }
}
public static readonly DependencyProperty EditModeProperty = DependencyProperty.Register(
"EditMode", typeof (bool), typeof (TextBoxWithModes));
}
With this in place, you can declare it in XAML:
<Namespace:TextBoxWithModes Text="enter text here"
Width="200"
HorizontalAlignment="Center"
EditMode="{Binding IsChecked, ElementName=editModeCheckBox}" />
I want a 1-time databind between a checkbox (IsChecked propety) in a childWindow and a nested member variable in it's DataContext object, such that only the initial IsChecked property is set by the member variable. The member variable (binding source) is not INotifyPropertyChanged or marked as a DependencyProperty- but I think that is ok b/c I only want it to be evaluated once- when it gets its initial value.
Binding (in testChildWindow.xaml):
<CheckBox Content="Show Username?" Name="cbShowUser" IsChecked="{Binding Path=User.showUser}"/>
Setting DataContext (in parent window code-behind):
testChildWindow dlgBox = new testChildWindow();
dlgBox.DataContext = (this.DataContext as IAssignDlgViewModel).AssignVM("defaultChildWindow");
dlgBox.Show();
Data Context/Member variable:
public class testChildWindowViewModel : IDlgViewUpdate
{
public User
...
}
public class User
{
public bool showUser;
public User()
{
showUser = true;
}
...
}
If I make the Vm's binding source property (showUser) a dependency property at the (non-nested) testChildWindowViewModel, then the binding works. But all other combinations seem to fail.
Why must it be a dependency (or INotifyPropertyChanged?) property for a 1-time binding?
Why can't I get it to work at a nested level?
Thanks!!!
Ah, looking at the Output window during the binding answered the question for me. The problem was that User was not a property. Changed it to an auto property and the binding works just right now.
all day long I am sitting and trying to find out why binding to AvalonEdits Document property isn't working. AvalonEdit is an advanced WPF text editor - part of the SharpDevelop project.(it's going to be used in SharpDevelop v4 Mirador).
So when I set up a simple project - one TextEditor (that's the AvalonEdits real name in the library) and made a simple class that has one property - Document and it returns a dummy object with some static text the binding is working perfectly.
However in real life solution I'm binding a collection of SomeEditor objects to TabControl.
TabControl has DataTemplate for SomeEditor and there's the TextEditor object.
<TabControl Grid.Column="1" x:Name="tabControlFiles" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<TabControl.Resources>
<DataTemplate DataType="{x:Type m:SomeEditor}">
<a:TextEditor
Document="{Binding Path=Document, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource NoopConverter}, IsAsync=True}"
x:Name="avalonEdit"></a:TextEditor>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemContainerStyle>
<Style BasedOn="{StaticResource TabItemStyle}" TargetType="{x:Type TabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"></Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
This doesn't work. What I've investigated so far:
DataContext of TextEditor is set to the proper instance of SomeEditor
TextEditors Document property is set to some other instance than SomeEditor.Document property
when I set breakpoint to no-op converter that is attached to that binding it shows me the correct value for Document (the converter is used!)
I also dug through the VisualTree to obtain reference to TextEditor and called GetBindingExpression(TextEditor.DocumentProperty) and this did return nothing
WPF produces the following information:
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Document; DataItem='SomeEditor' (HashCode=26280264); target element is 'TextEditor' (Name='avalonEdit'); target property is 'Document' (type 'TextDocument')
SomeEditor instance that is bound to already has a created and cached copy of Document before the binding occurs. The getter is never called.
Anyone can tell me what might be wrong? Why BindingExpression isn't set ? Why property getter is never called?
//edit: new tests and new results
I've read some more and set the binding in code behind. When I do that it works.
How come setting this in XAML doesn't work and doing the same thing in code does?
//edit2: The code also fails when called immediately after adding the object to the observable collection that is used as higher level DataSource.(that's not long after the xaml binding should fire). That makes me think this is timing issue. Anyone can tell something about it ?
//edit3: The binding code:
private List<T> GetObjectOfTypeInVisualTree<T>(DependencyObject dpob) where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(dpob);
List<T> returnlist = new List<T>();
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(dpob, i);
T childAsT = child as T;
if (childAsT != null)
{
returnlist.Add(childAsT);
}
List<T> lst = GetObjectOfTypeInVisualTree<T>(child);
if (lst != null)
{
returnlist.AddRange(lst);
}
}
if (returnlist.Count > 0)
{
return returnlist;
}
return null;
}
private void RebindMenuItem_Click(object sender, RoutedEventArgs e)
{
foreach (XHTMLStudioPrototypeFileEditor ed in CurrentProject.OpenedFiles)
{
List<ContentPresenter> cps = GetObjectOfTypeInVisualTree<ContentPresenter>(tabControlFiles);
if (cps != null)
{
foreach (ContentPresenter cp in cps)
{
foreach (DataTemplate dt in tabControlFiles.Resources.Values)
{
try
{
object o = dt.FindName("avalonEdit", cp);
TextEditor ted = (TextEditor)o;
bool isDataBound = BindingOperations.IsDataBound(ted, TextEditor.DocumentProperty);
if (!isDataBound)
{
BindingOperations.SetBinding(ted, TextEditor.DocumentProperty, new Binding("Document"));
}
Console.WriteLine(isDataBound);
}
catch (Exception)
{
}
}
}
}
}
}
Here are six more things to try:
Search your carefully application for any place at all where you directly assign to the Document property of a TextEditor. It looks like some code, somewhere is doing an avalonEdit.Document = ... which would overwrite the binding. I would search your entire app for the match-case whole-word strings "Document" and "DocumentProperty" and give each occurence a moment's thought to see if it could be setting this property.
Set a breakpoint in TextEditor.OnDocumentChanged to see if the document is being properly bound and then changed back later. Check call stacks with "Just My Code" disabled and showing external code.
Try setting breakpoints in the NoopConverter.Convert, SomeEditor.get_Document, and TextEditor.OnDocumentChanged to figure out the precise sequence of operations. Also note when the Binding error message is shown.
Temporarily modify TextEditor's constructor to store a reference to every instance in a public static List field so you can determine which TextEditors have ever been created, then write code that looks through them displaying their GetHashCode() and their BindingOperations.GetBindingExpression(editor, DocumentProperty) results. Make sure you take out the public static field when you're done!
Take the "Path=" out of your XAML that constructs the Binding so it will better match the C# version. (I once had a problem where the XAML interpreted the path different than the Binding constructor because of the ITypeDescriptorContext passed to PropertyConverter.) The exact equivalent to the C# code you posted is Document="{Binding Document}".
Create a custom trace listener and set a breakpoint in it to get the call stack when the binding error is produced, search up the stack frames to find the objects involved and give them debugger object ids (right-click, Make Object ID), then investigate the actual values of properties to make sure they are as expected.
Enjoy!
Just an observation: I had the same problem and looked through the AvalonEdit source; it seems the problem is that the TextEditor constructor overwrites the Document property (instantiates a new TextDocument); if you comment this out, the bindings work; however, if you don't have a binding, you'd need to make further modifications. I'll try to discuss this with the authors and maybe suggest a patch.
We have an object that derives from DependencyObject, and implements some DependencyProperties.
Basically something like this:
class Context : DependencyObject {
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register ("Name", typeof (string), typeof (Context), new PropertyMetadata (""));
public string Name {
get {
return (string)this.GetValue (NameProperty);
}
set {
this.SetValue (NameProperty, value);
}
}
}
This works, the property is setup, can be bound, etc. The issue comes when I bind TO the propery from WPF, using a TwoWay bind. The TwoWay part never actually happens, WPF never calls the set of this property. I have set my binding up like this:
<TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
In this case, typing in the text box should immediately update the Name property, but it does not. If I change the Name property to be a regular POCO property, it works (though the other side of the TwoWay obviously doesn't unless I implement INotifyPropertyChanged).
What am I doing wrong here? This should be a really simple thing to do, but it's causing me no end of headaches.
This is expected behavior. The CLR property is merely a wrapper around the underlying DependencyProperty. WPF often optimizes by calling GetValue and SetValue directly. If you need custom logic to execute then use the metadata of the DependencyProperty.
After this issue cost me some time:
For those of you who have the same problem, but - like me - don't see the solution in the answer above:
To support inheritance of the DataContext, the custom class (Context in this case) has to be derived from FrameworkElement rather than DependencyObject. That's all.
Marc