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.
Related
I am attempting a save/load mechanism for re-use in a business application. I have the groundwork laid to read/write ObservableCollection<> to/from xml, using attributes to describe my class properties. That part is working. I can save an ObservableCollection to XML, then load the XML back into an ObservableCollection the next time I run the program.
Here's my problem. I have a ComboBox whose ItemsSource.DataContext = ObservableCollection<Flag>;
When I run the program, it accepts the binding just fine, but the ComboBox itself does not populate itself until later. I want to set the SelectedItem to be the first item in the ObservableCollection<Flag> that I have loaded from XML. Nothing happens though, because as the program is executing it's startup methods, the Items.Count remains 0. I'm guessing the ComboBox doesn't populate itself until it gets focus. How do I work around this? Can I force the ComboBox to populate itself? I've tried cb_ARDAR_ARFlag.Items.Refresh();
XAML:
<ComboBox Name="cb_ARDAR_ARFlag"
ItemsSource="{Binding}"
SelectionChanged="cb_ARDAR_ARFlag_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Flag_Desc}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Relevant Code:
public MainWindow()
{
InitializeComponent();
setDataBinding();
loadSavedData();
}
private void setDataBinding()
{
//Returns ObservableCollection<Flag>
cb_ARDAR_ARFlag.DataContext = Flag.getOCAvailableFlags();
}
private void loadSavedData()
{
//When it gets here the ItemCount is 0 so nothing happens.
//Refresh didn't help
cb_ARDAR_ARFlag.Items.Refresh();
Flag f = Enforcement_Save.loadOCARFlag().First();
cb_ARDAR_ARFlag.SelectedItem = f;
}
At this point I'm still not sure the code at the end will successfully identify the correct 'flag' item to be selected, or if I'll end up using Linq. Which, by the way, leads me to another question. Can you Linq to ComboBox.Items somehow?
I have recreated your issue, and your are correct, the items count is = 0 in the loadSavedData method. The combobox doesn't seem to be populated until after the constructor has fully executed.
In the meantime I found you can use the ItemsSource property to load the combobox at the time you want it loaded:
cb_ARDAR_ARFlag.ItemsSource = Flag.getOCAvailableFlags();
I want to call a simple Command which adds values from my GUI into the database.
My Command:
private ICommand addSpeechCommand;
public ICommand AddSpeechCommand
{
get
{
if (addSpeechCommand == null)
{
addSpeechCommand = new RelayCommand(param => AddSpeech());
}
return addSpeechCommand;
}
}
public void AddSpeech()
{
// TODO add
OrganizerBL.InsertSpeech(new Speech(maxSpeechId, currentSpeech.Title, currentSpeech.Summary, currentSpeech.Start, currentSpeech.End, currentSpeech.TrackId, currentSpeech.SpeakerId, currentSpeech.ConferenceId, currentSpeech.Room));
this.LoadSpeeches();
}
-- this commented out row shows how i dealt with it when a row of my datagrid was selected. but i want it to work without a currentSpeech
My XAML:
<Label x:Name ="lblTitle" Content="Title"/>
<TextBox x:Name="txtTitle" Text="{Binding CurrentSpeech.Title, Mode=TwoWay}" Margin="2,144,0,0" Height="20" VerticalAlignment="Top"/>
and other textboxes...
I really don't know how to access the values of the textboxes from the command to call my insertSpeech method...
Sorry for my english :)
UPDATE:
I'm getting a nullreference exception because my currentSpeech is null.
Is there a way to solve this without the currentSpeech?
The reason you get the NullReferenceException is probably because it's instanced in the property itself. When you cerate a binding, it's created to the property as it is at that stage. And you bind to the property when it's NULL. IT's actually created inside the property, but the Binding will never know that.
First of all, I would remove all logic from properties.
I would also implement the INotifyPropertyChanged to the class, and call the PropertyChanged in the property's "set". This means that the UI will know of any changes to the porperty.
Then I would create a depencency property for the property, if it's used in any Binding ot XAML.
Last, I would instance the command in the class's constructor.
Logic don't (in my book) belong to properties.
How to do
1. Bind TextBox.Text to View model property
2. Use View model property in Command Handler.
In your case, You have binded TextBox.Text to CurrentSpeech.Title, but using this.Title.
In you command, change this.Title to currentSpeech.Title
How can I figure out what line of xaml contains the troublesome binding When my debug output is full of lines like the following:
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='UW.Entities.ProgramModel.UWProgram' BindingExpression:Path=; DataItem='RuntimeType' (HashCode=24995901); target element is 'DataGridCollectionViewSource' (HashCode=60976864); target property is 'Source' (type 'Object')
I don't know how to interpret this in a way that can let me find the responsible line of xaml. I can't even figure out what xaml file the error is coming from. Is there some way to get more information when these errors occur?
'UW.Entities.ProgramModel.UWProgram' is just a type - I don't know what the object being bound to is. I also have lots of DataGridCollectionViewSources in various bits of xaml, all who's property 'Source' is bound to something which may or may not have that type (again - no easy way to tell).
If you do not know which binding fails
I would use the Snoop utility for this purposes. In short - at the top-left corner above the visual tree, you'll find a drop-down list which allows filtering visuals, just select Visuals with binding Error. See online documentation for more details.
If you know which binding fails
Sometime you know which binding fails but was not able to find a source fo the problem since binding is pretty tricky, for instance TemplateBindings, bindings which refer to a DataContext of another control, etc.. I found helpful putting a TextBlock which Text property is bound to the same binding source in this way you can see what exactly bound since TextBlock will display a type name of a bound object.
For instance you have following failed binding:
<ItemsControl ItemsSource="{Binding Parent.DataContext.ActiveItem.DataContext}" />
<!-- See what is bound, if failed - try previous level -->
<TextBlock Text="{Binding Parent.DataContext}" />
<TextBlock Text="{Binding Parent.Inner.Items}" />
<TextBlock Text="{Binding Parent.Inner}" />
Useful links:
Debugging Data Bindings in a WPF or Silverlight Application
Nice trick using special DebugConverter which allows break a debugger whilst doing a binding, see Debugging WPF DataBinding article
I have been happily using the wonderful snippet from 'Switch on the Code' to detect and report binding errors since it was first published in 2009...
http://www.switchonthecode.com/tutorials/wpf-snippet-detecting-binding-errors
edit: still works excellently on VS2012 (Sept 2013)
Update 25 Jan 2016
The link appears broken, so I'll paste in the relevant snippets...
using System.Diagnostics;
using System.Text;
using System.Windows;
namespace SOTC_BindingErrorTracer
{
public class BindingErrorTraceListener : DefaultTraceListener
{ //http://www.switchonthecode.com/tutorials/wpf-snippet-detecting-binding-errors
private static BindingErrorTraceListener _Listener;
public static void SetTrace()
{ SetTrace(SourceLevels.Error, TraceOptions.None); }
public static void SetTrace(SourceLevels level, TraceOptions options)
{
if (_Listener == null)
{
_Listener = new BindingErrorTraceListener();
PresentationTraceSources.DataBindingSource.Listeners.Add(_Listener);
}
_Listener.TraceOutputOptions = options;
PresentationTraceSources.DataBindingSource.Switch.Level = level;
}
public static void CloseTrace()
{
if (_Listener == null)
{ return; }
_Listener.Flush();
_Listener.Close();
PresentationTraceSources.DataBindingSource.Listeners.Remove(_Listener);
_Listener = null;
}
private StringBuilder _Message = new StringBuilder();
private BindingErrorTraceListener()
{ }
public override void Write(string message)
{ _Message.Append(message); }
public override void WriteLine(string message)
{
_Message.Append(message);
var final = _Message.ToString();
_Message.Length = 0;
MessageBox.Show(final, "Binding Error", MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
}
And to set it up/initialize it...
namespace WpfListeningForTraceErrors
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
BindingErrorTraceListener.SetTrace();
InitializeComponent();
}
}
}
You can add this to every control that binds
PresentationTraceSources.TraceLevel="High"
And run the program in debug, the detailed binding information will appear in your Output window. It may help a bit. You can also create a pass though converter to catch an error (catches the problem some times but not always). There are no good tools for debugging XAML in general that I am aware of.
You can download a tool called Snoop that will allow you to debug bindings. It provides a view of your WPF applications visual tree higlighting any binding errors that it finds.
You can get some basic information about binding errors in the Output Window in Visual Studio. It will show the binding expression path error and the line on which the error occured.
In VisualStudio goto Tools->Extentions and Updates->(download Output Enhancer). When you build your solution you will get the exact kind of error message you posted in Red color if there is a binding error.
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();
}
}
UPDATE II
Problem was solved. Thank you.
For a simple Silverlight printing preview engine, my XAML looks like this (excerpt):
<Grid>
<TextBlock Text="{Binding IntroText}" />
<ItemsControl ItemsSource="{Binding DataItems}"
x:Name="DataItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
TextWrapping="Wrap"
Margin="0,2" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="{Binding OutroText}" />
</Grid>
I want to ensure that everything fits on a page, therefore i have a simple method:
public bool FitsOnPrintPage(Size pageDimensions)
{
Measure(new Size(pageDimensions.Width, Double.PositiveInfinity));
return
DesiredSize.Height <= pageDimensions.Height &&
DesiredSize.Width <= pageDimensions.Width;
}
Now we have a strange problem here which I can't explain:
The bound collection DataItems is a generic object List. When containing simple strings, the Measure(...) method works as expected and returns a properly calculated DesiredSize. So far, everything is working.
However, when having a simple object like this...
public class DataItem
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
...and changing the TextBlock Binding to <TextBlock Text="{Binding Path=Value1}"... the resulting view is identical, however the Measure(...) method doesn't return the expected values, the height of the Items is always zero. Also not working: keep Text Binding and override DataItems ToString() method. View working, Measure doesn't.
I was then trying to force a recalculation using methods like InvalidateMeasure() or UpdateLayout() on the DataTemplate or the whole page, without success.
Can you explain this?
UPDATE
Interesting: I've attached a simple custom ValueConverter to the TextBlock's Binding just for debugging reasons. When a string object is bound, I can see that Measure(...) is triggering the Binding - it's resolved first (i can see the debugger stepping into the ValueConverter) and measured afterwards. But when binding a custom class as described above, Measure(...) doesn't touch the Binding, i am stepping into the ValueConverters breakpoint "later". (Have to find out, when exactly)
Does this help you in any kind?
The answer is simple.. you working not in the 'silverlight way'
In Silverligth - it dosen't mather if string fits to the screen width or not, if string dosen't fit, just set TextBlock.Wrap to Wrap...
You have problem with this becose of 'old way of thinking'...
But if you want it so much try this:
var ContainerGrid = new Grid(); // create grid at runtime
// !!! it's important for controlToMesure.Parent property to be NULL, if it's not
// !!! then temporary remove controlToMesure from parent container...
ContainerGrid.Children.Add(controlToMesure); // add control that you want to mesure
ContainerGrid.Measure(new Size(pageWidth, pageHeight));
ContainerGrid.Arrange(new Rect(0, 0, pageWidth, pageHeight));
ContainerGrid.UpdateLayout();
var size = ((FrameworkElement)ContainerGrid.Children[0]).DesiredSize;
Here is the code from http://silverpdf.codeplex.com/
Maybee it would help you, but you have to modify it, to make it usable.
private System.Windows.Size CalculeteSize()
{
var s = new System.Windows.Controls.StackPanel()
{
VerticalAlignment = System.Windows.VerticalAlignment.Center,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
};
var fs = FontPool.GetFontStream(Typeface.FontFamily.Source);
s.Children.Add(new System.Windows.Controls.TextBlock
{
Text = Text,
FontSource = new FontSource(fs),
FontSize = EmSize,
FontFamily = Typeface.FontFamily,
FontStretch = Typeface.FontStretch,
FontStyle = Typeface.FontStyle,
FontWeight = Typeface.FontWeight,
});
s.Measure(new System.Windows.Size(double.MaxValue, double.MaxValue));
var aw = s.DesiredSize.Width;
var ah = s.DesiredSize.Height;
var size = new System.Windows.Size(aw, ah);
return size;
}
Solved
the problem was that the page controls were created and calculated first, and added to the displaying control after generation, because i wanted to avoid frequent UI updates. Something similar was even suggested by Ai_boy, who was trying to solve the problem by using an independent Grid Control - unfortunately this turned out as a misleading approach. Only after the generated page control was added to the visual tree, it automatically resolves the Bindings resulting in a proper size measuring.
Hope this helps anyone.