Binding a 'IsReadOnly' Dependency Property - wpf

I have tried to create a Dependency Property 'IsReadOnly' to automatically set all TextBoxes in my form to ReadOnly following certain events.
The property is set up in the code behind to my window with the textboxes and looks like:
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly",
typeof(bool),
typeof(MainWindow),
new PropertyMetadata());
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
Xaml code for textboxes is similar to this:
<TextBox Text="{numBind:NumericFormatBinding Path=BudgetStatement.OpExpTotalByFunction}"
IsReadOnly="{Binding Path=IsReadOnly,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window},
Mode=TwoWay}"
Name="txtOpExpByProgram" />
but it doesn't work. I can still edit values in the textbox. I'm getting the following output error:
System.Windows.Data Error: 40 : BindingExpression path error: 'IsReadOnly' property not found on 'object' ''ListCollectionView' (HashCode=54963679)'. BindingExpression:Path=IsReadOnly; DataItem='ListCollectionView' (HashCode=54963679); target element is 'TextBox' (Name=''); target property is 'IsReadOnly' (type 'Boolean')
I don't know enough wpf to correctly understand this error, but it seems to have something to do with the ListCollectionView - but I haven't tried to attach the property to a ListCollectionView so I'm stuck.
Googling suggests it might be due to the DataContext and dependency properties needing special treatment (http://stackoverflow.com/questions/8497841/dependency-property-and-binding-error), or maybe the PropertyMetaData should be a Framework (or UI)PropertyMetaData.
Can anyone point me in the right direction to find out what isn't working?
tia
alex
ps: the numbind thing just sets the stringformat in all the text boxes

After reading the comment, Change the owner type from MainWindow to BudgetMainWindow.
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly",
typeof(bool),
typeof(BudgetMainWindow),
new PropertyMetadata());

Related

WPF MVVM binding error

I'm using the MVVM pattern. In my ViewModel I have a public ObservableCollection:
public ObservableCollection<SettingsTemplateHistoryItemViewModel> HistoryItemCollection;
public SettingsTemplateViewModel()
{
this.HistoryItemCollection = new ObservableCollection<SettingsTemplateHistoryItemViewModel>();
}
In my View I have an ItemsControl with its ItemsSource property bound to the ObservableCollection in the ViewModel.
<ItemsControl ItemsSource="{Binding HistoryItemCollection}"/>
I'm getting the following error:
BindingExpression path error: 'HistoryItemCollection' property not found on 'object' ''SettingsTemplateViewModel' (HashCode=48413709)'. BindingExpression:Path=HistoryItemCollection; DataItem='SettingsTemplateViewModel' (HashCode=48413709); target element is 'ItemsControl' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
I'm baffled. I am 100% certain the property exists in the View's DataContext (i.e. the ViewModel). I've copied and pasted the property name into the View, so the binding path must be correct. The View and the ViewModel are wired up via implicit/typed DataTemplates. What am I missing?
like the binding error says, your datacontext for your itemscontrol does not contain a public property called HistoryItemCollection. an easy way to check the datacontext at runtime is using Snoop
EDIT: you have to use a property instead of a field.
public ObservableCollection<SettingsTemplateHistoryItemViewModel> HistoryItemCollection {get;set;}

Why does my UI not update to reflect the data I have bound to it?

I've bound the visibility of some buttons to a bool, but when the bool changes, the button's visibility does not change. Why could this be?
The boolean is set up as such:
public static readonly DependencyProperty editModeToggle = DependencyProperty.Register("editMode", typeof(bool), typeof(Window));
public bool EditMode
{
get { return(bool)GetValue(editModeToggle); }
set { SetValue(editModeToggle, value); }
}
I'm pretty sure the binding isn't at fault.
[Note: I have set up a Converter that works and the binding works. The visibility just doesn't change when I change from true to false or vice versa]
Binding:
<Button Content="Test" Visibility="{Binding ElementName=mainWindow, Path=EditMode, Converter={StaticResource BooltoVisibilityConverter}/>
There are a few things you need to check when a binding does not work as expected:
Does the bool property notify change using
INotifyPropertyChanged?
Do you use a converter to convert from
bool to Visibility?
Do you see any binding errors in output
window?
Have you tried putting a breakpoint on binding in xaml
or setter in bool property?
As you have mentioned in your comment, you have not implemented INotifyPropertyChanged interface.
The need to implement the interface is whenever value of the property in ViewModel changes, there has be be a way for binding to know that. So, after implementing INotifyPropertyChanged, for each property, you raise the PropertyChanged event with the property name in it. That way, the binding knows that value of the property has changed.
There are lots of articles online. Here is one to get you started: INotifyPropertyChanged and WPF
Dependency Property registration is not correct -
public static readonly DependencyProperty editModeToggle =
DependencyProperty.Register("editMode", typeof(bool), typeof(Window));
It should be-
public static readonly DependencyProperty editModeToggle =
DependencyProperty.Register("EditMode", typeof(bool), typeof(Window));
Notice the spelling of property - 'E' should be capital since its case sensitive and your property name is EditMode not editMode.
In this case you don't need point 1 from decyclone list since you are using a dependency property. I think that you should add Mode=TwoWay in binding if you don't have it, that will solve it.

Trigger on attached property of DataGridTextColumn

I am trying to define a custom attached property on DataGridTextColumn and writing a DataTrigger against it in my xaml file. Here is how the attached property (FilterDisplayStyle) is defined in my class.
//Dependency Property whether Column Filter is combobox or textbox or editable combobox.
public static FrameworkPropertyMetadata inheritsMetaData =
new FrameworkPropertyMetadata(FilterDisplayTypeEnum.TextBoxOnly, FrameworkPropertyMetadataOptions.Inherits);
public static DependencyProperty FilterDisplayTypeProperty = DependencyProperty.RegisterAttached("FilterDisplayType",
typeof(FilterDisplayTypeEnum), typeof(DataGridColumn), inheritsMetaData);
public static FilterDisplayTypeEnum GetFilterDisplayType(DependencyObject target) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter Element"); }
return (FilterDisplayTypeEnum)target.GetValue(FilterDisplayTypeProperty);
}
public static void SetFilterDisplayType(DependencyObject target, FilterDisplayTypeEnum value) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter Element"); }
target.SetValue(FilterDisplayTypeProperty, value);
}
The above attached property's type is FilterDisplayTypeEnum which is defined as below.
public enum FilterDisplayTypeEnum {
TextBoxOnly,
NonEditableComboBox,
EditableComboBox
}
Here is how I set this property in DataGridTextColumn
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" f:DataGridColumnExtensions.FilterDisplayType="NonEditableComboBox" />
....
</DataGrid.Columns>
Now I am trying to retrieve this property in using the following
<TextBox Text="{Binding Mode=OneWay, Path=FilterDisplayType, RelativeSource={RelativeSource AncestorType={x:Type DataGridTextColumn}}}"/>
But I don't get any text on my TextBox above.
Surprisingly, I have another attached property (this time attached to DataGrid instead) that works perfectly fine. The issue is only with DataGridTextColumn. Also, using WPF Inspector, I see there is no direct visual representation of DataGridTextColumn in the Visual Tree , so I was skeptical whether I could use FindAncestor way of binding on ancestor which is DataGridTextColumn. Can anyone help me out in this scenario. To Summarize, I can't access a custom attached property defined on DataGridTextColumn using FindAncestor type of Binding. Are there any alternatives to this?
regards,
Nirvan
Edit:
As per #Clemens suggestions, I changed the definition of the Attached Property to something like this. But I still can't access the attached property in my xaml.
Attached Property Definition:
public static DependencyProperty FilterDisplayTypeProperty = DependencyProperty.RegisterAttached("FilterDisplayType",
typeof(FilterDisplayTypeEnum), typeof(DataGridColumnExtensions), inheritsMetaData);
public static FilterDisplayTypeEnum GetFilterDisplayType(DataGridBoundColumn target) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter target"); }
return (FilterDisplayTypeEnum)target.GetValue(FilterDisplayTypeProperty);
}
public static void SetFilterDisplayType(DataGridBoundColumn target, FilterDisplayTypeEnum value) {
if (target == null) { throw new ArgumentNullException("Invalid Parameter target"); }
target.SetValue(FilterDisplayTypeProperty, value);
}
I am still unable to access the property "FilterDisplayType" in my xaml code as given below
<TextBox Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridTextColumn}}, Path=FilterDisplayType}"/>
The owner type must be the type that declares the property, here DataGridColumnExtensions:
public static DependencyProperty FilterDisplayTypeProperty =
DependencyProperty.RegisterAttached("FilterDisplayType",
typeof(FilterDisplayTypeEnum),
typeof(DataGridColumnExtensions), // here
inheritsMetaData);
This seems to be a common misunderstanding with attached properties. See also here.
And also note that the syntax for binding to an attached property is (Class.Property), so you would need to bind like this:
<TextBox
Text="{Binding Path=(DataGridColumnExtensions.FilterDisplayType)}"/>
And just another note: i haven't quite understood why the property inherits. As far as i can see you intend to set it explicitly on DataGridTextColumn objects.

Custom control ContentProperty DataBinding

I'm running in a issue while trying to use dependency properties in objects which are parts of a collection, inside acustom control, collection identified with the "ContentProperty" attribute. Ok, that's quite unclear. Here is sample of my custom control :
Here is my custom control basic definition :
[ContentProperty("SmarSearchScopes ")]
public class SmartSearchCc : Control
{
List<SmartSearchScope> SmarSearchScopes {get;set;}
(more code here)
}
Here is the basic definition of a SmartSearchScope object :
public class SmartSearchScope : DependencyObject
{
public static readonly DependencyProperty ViewProperty =DependencyProperty.Register("View", typeof (ICollectionView), typeof (SmartSearchScope),new UIPropertyMetadata(null,OnViewChanged));
public static readonly DependencyProperty FilterColumnsProperty =DependencyProperty.Register("FilterColumns", typeof (IEnumerable<ColumnBase>), typeof (SmartSearchScope),new UIPropertyMetadata(null, OnFilterColumnsChanged));
public ICollectionView View
{
get { return (ICollectionView) GetValue(ViewProperty); }
set { SetValue(ViewProperty, value); }
}
public IEnumerable<ColumnBase> FilterColumns
{
get { return (IEnumerable<ColumnBase>) GetValue(FilterColumnsProperty); }
set { SetValue(FilterColumnsProperty, value); }
}
(more code here)
}
All that for what ? Being able to pass a collection of SmartSearchScope objects via XAML like so :
<SmartSearch:SmartSearchCc HorizontalAlignment="Stretch" Grid.Row="0" >
<SmartSearch:SmartSearchScope FilterColumns="{Binding ElementName=CcyPairsConfigBlotter, Path=Columns}" View ="{Binding ElementName=CcyPairsConfigBlotter, Path=ItemsSource}"/>
<SmartSearch:SmartSearchScope FilterColumns="{Binding ElementName=ClientConfigBlotter, Path=Columns}" View ="{Binding ElementName=ClientConfigBlotter, Path=ItemsSource}"/>
</SmartSearch:SmartSearchCc>
'ClientConfigBlotter' and 'CcyPairsConfigBlotter' are just two ItemsControls which expose a 'Columns' and an 'ItemSource' d-property.
The problem here is that althought my 2 SmartSearchScope objects gets instantiated, the databinding on the "View" and "FilterColumns" d-properties is not made and I never go througth the the associated callbacks.
In addition, here is the output error message I got when creating the custom control.
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Columns; DataItem=null; target element is 'SmartSearchScope' (HashCode=56862858); target property is 'FilterColumns' (type 'IEnumerable`1')
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ItemsSource; DataItem=null; target element is 'SmartSearchScope' (HashCode=56862858); target property is 'View' (type 'ICollectionView')
This is obvious that I'm missing something but I can't find what.
I must say that, in a previous version of that control, these 2 problematic d-properties where SmartSearchCc properties and that all worked just fine.
Thanks for your help :)
--bruno
I had a similar problem here: Bindings on child dependency object of usercontrol not working
The reason the binding doesn't work is because DependencyObjects don't have a DataContext property. In my case I changed them to inherit from FrameworkElement which solved the problem.
Although as someone else has mentioned, changing the parent control to an ItemsControl could simplify things.
Ok, problem solved, I swithc inheritance of my main custom control from control to ItemsControl and inheritance of my child object to FrameWork element and that's it. no need to further modifications.
Thank you all for your suggestions !

Binding a Resource to a View Model

How do you bind data from the view model into an object in the resources of the user control? Here is a very abstract example:
<UserControl ...
xmlns:local="clr-namespace:My.Local.Namespace"
Name="userControl">
<UserControl.Resources>
<local:GroupingProvider x:Key="groupingProvider" GroupValue="{Binding ???}" />
</UserControl.Resources>
<Grid>
<local:GroupingConsumer Name="groupingConsumer1" Provider={StaticResource groupingProvider"} />
<local:GroupingConsumer Name="groupingConsumer2" Provider={StaticResource groupingProvider"} />
</Grid>
</UserControl>
How do I bind GroupValue to a property in the view model behind this view. I've tried the following:
<local:GroupingProvider x:Key="groupingProvider" GroupValue="{Binding ElementName=userControl, Path=DataContext.Property}"/>
But this doesn't work.
Edit:
GroupProvider extends DependencyObject and GroupValue is the name of a DependencyProperty. I'm getting the following error:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=DataContext.Property; DataItem=null; target element is 'GroupingProvider' (HashCode=47478197); target property is 'GroupValue' (type 'TimeSpan')
This seems to suggest that it cannot find userControl.
More Edit:
Nobody has an answer to my question? Is there not a way to do this?
I know its a bit late, but i had the same problem. Ricks answer is right, you need to inherit from Freezable.
The following Code gave me the same error as you got
Not working resource:
public class PrintBarcodesDocumentHelper : DependencyObject
{
public IEnumerable<BarcodeResult> Barcodes
{
get { return (IEnumerable<BarcodeResult>)GetValue(BarcodesProperty); }
set { SetValue(BarcodesProperty, value); }
}
public static readonly DependencyProperty BarcodesProperty =
DependencyProperty.Register("Barcodes", typeof(IEnumerable<BarcodeResult>), typeof(PrintBarcodesDocumentHelper), new PropertyMetadata(null, HandleBarcodesChanged));
private static void HandleBarcodesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Do stuff
}
}
Xaml:
<UserControl.Resources>
<Barcodes:PrintBarcodesDocumentHelper x:Key="docHelper" Barcodes="{Binding BarcodeResults}"/>
</UserControl.Resources>
My viewmodel is bound to the DataContext of the UserControl.
Error:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=BarcodeResults; DataItem=null; target element is 'PrintBarcodesDocumentHelper' (HashCode=55335902); target property is 'Barcodes' (type 'IEnumerable`1')
Working resource class:
public class PrintBarcodesDocumentHelper : Freezable
{
// Same properties
protected override Freezable CreateInstanceCore()
{
return new PrintBarcodesDocumentHelper();
}
}
Unfortunately i dont know why it have to be a Freezable.
In order to enable binding, GroupingProvider needs to be derived from Freezable or FrameworkElement or FrameworkContentElement and GroupValue needs to be a DependencyProperty.

Resources