WPF nested property binding not working if parent property is null - c#

I have the following situtation:
<DatePicker IsEnabled="{Binding ParentObject.EditAvailable}"
Now if I set ParentObjectto nullin my ViewModel which is also the DataContext the IsEnabled Property on the DatePicker will be set to true
The binding is working completely correct for all instances if the ParentObject is properly set.
But I don't really get that behavior in WPF.
A workaround would be using:
<DatePicker IsEnabled="{Binding ParentObject.EditAvailable, FallbackValue=False}"
So now if the ParentObject is set to null via my ViewModel, the IsEnabled property on the DatePicker returns false.
Is there anything I can do without setting the FallbackValue on each of my controls in my project?
It's already huge and I need to find a solution to have it somehow as default behavior that if the ParentObject is null, that there is a default value set default(bool), default(string) etc etc.
Any help is much appreciated.

You can set IsEnabled on the panel or grid that contains the controls, and it will affect all controls it contains.
<StackPanel IsEnabled="{Binding ParentObject.EditAvailable, FallbackValue=False}">
<DatePicker />
<DatePicker />
<DatePicker />
</StackPanel>

Is there anything I can do without setting the FallbackValue on each of my controls in my project?
No, not apart from replacing each binding with a custom one that sets the FallbackValue by default. See this answer for an example.
You will still have to replace {Binding with {local:YourBinding everywhere.

If binding Path is always the same, then you can use default style for DatePicker:
<Style TargetType="DatePicker">
<Setter Property="IsEnabled" Value="{Binding ParentObject.EditAvailable, FallbackValue=False}"/>
</Style>

Related

WPF ContentPresenter Content null when Visibility=Collapsed

I'm making custom control with edit/view state.
I've made 2 dependencyProperties with default styles:
<Setter Property="EditContent">
<Setter.Value>
<TextBox Text="{Binding ElementName=parent, Path=LocalValue}" />
</Setter.Value>
</Setter>
<Setter Property="ViewContent">
<Setter.Value>
<TextBox IsEnabled="False" Text="{Binding ElementName=parent, Path=LocalValue}" />
</Setter.Value>
</Setter>
and then, displaying these Contents depending on IsReadOnly value like this:
<Border Background="Transparent"
MouseLeftButtonDown="UIElement_OnMouseLeftButtonDown"
Visibility="{Binding ElementName=parent,
Path=IsReadOnly,
Converter={StaticResource BooleanToCollapsingVisibilityConverter},
ConverterParameter=true}">
<ContentPresenter Content="{Binding ElementName=parent, Path=ViewContent}" />
</Border>
Problem is, that when my control loads with IsReadOnly = true, Content Property of my ContentPresenter for EditContent is null.
When I'm changing IsReadOnly to false Content of EditContent loads, but my binding does not work (like it's not evaluated).
How to re-evaluate bindings in WPF, or force ContentPresenter to load it's content on created (even if it's invisible)?
P.S. If I navigate to this ContentPresenter in Snoop (or WPF Inspector) when It's invisible - it's empty. When I navigate to it when it's visible - bindings starting to work
Please, have a look at output windows while debugging. you will see errormessage describing the binding problem. wpf rule nr.1: always check output window.
The reason is that your edit / view content has different NameScope, therefore ElementName does not work. However, in your Control you can set NameScope manually, by using something like:
var currentScope = NameScope.GetNameScope(this);
NameScope.SetNameScope((UIElement)this.EditContent, currentScope)
in your case you are using styles and styles has its own namescope, so it won't work. Imagine, that you used the style on multiple pages. What element should be used?
Sometimes you can use Source={x:Reference elementName}, but you cannot use it in direct children of the source the element, because the element does not exist yet, when the {x:Reference } is being resolved
never set content-like properties inside styles. if you applied your style to more than one element, that the same TextBox from ViewContent would be added to visual tree multiple times and that throws an exception. You should use DataTemplates instead of direct content in styles

Dependency Property Datacontext

I have a usercontrol, and there is a Datacontext set for it. This usercontrol contains also a Dependency-Property. Now, i want simply bind to this property.
I think the problem has something to do with the wrong datacontext.
The dependency-Property in my usercontrol (called TimePicker) looks like this:
public TimeSpan Time
{
get { return (TimeSpan)GetValue(TimeProperty); }
set
{
SetValue(TimeProperty, value);
OnPropertyChanged();
}
}
public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof (TimeSpan), typeof (TimePicker));
I try to use it like this:
<upDownControlDevelopement:TimePicker Grid.Row="1" Time="{Binding Path=TimeValue}" />
When i do this i get the following binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'TimeValue' property not found on 'object' ''TimePicker' (Name='TimePickerControl')'. BindingExpression:Path=TimeValue; DataItem='TimePicker' (Name='TimePickerControl'); target element is 'TimePicker' (Name='TimePickerControl'); target property is 'Time' (type 'TimeSpan')
Any help would be highly appreciated
Greetings Michael
PS: you can download the code at here
Although this has now been solved there seems to be some, in my opinion, inappropriate use of the DataContext.
When developing a custom reusable control, you should not set DataContext at all. What the DataContext will be, that is for the user of the control to decide, not for the developer. Consider the following common pattern of code:
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
</Grid>
Notice that here, you are using the Grid control. The developer of the control (in this case, the WPF team), didn't touch the DataContext at all - that is up to you. What does it mean for you as a control developer? Your DependencyProperty definition is fine, but you shouldn't touch the DataContext. How will you then bind something inside your control to the DependencyProperty value? A good way is using a template (namespaces omitted):
<MyTimePicker>
<MyTimePicker.Template>
<ControlTemplate TargetType="MyTimePicker">
<!-- Stuff in your control -->
<TextBlock Text="{TemplateBinding Time}" />
<TextBox Text="{Binding Time, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
<MyTimePicker.Template>
</MyTimePicker>
Note that TemplateBinding is always one-way only, so if you need any editing at all, you need to use normal binding (as you can see on the TextBox in the example).
This only means that the TextBlock/Box inside your control will get its Time value from your custom control itself, ignoring any DataContext you might have set.
Then, when you use the control, you do it like this (added to my first example):
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
<MyTimePicker Time="{Binding TimeValue}" />
</Grid>
What just happened here is that the MyTimePicker does not have DataContext set anywhere at all - it gets it from the parent control (the Grid). So the value goes like this: Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text.
And above all, avoid doing this in the constructor of your custom control:
public MyTimePicker()
{
InitializeComponent();
DataContext = this;
}
This will override any DataContext set in XAML, which will make binding a huge pain (because you'll have to always set Source manually). The previous example would not work, and this wouldn't work either:
<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />
You would think this is OK, but the DataContext will be resolved in the InitializeComponent() call, so the value will be immediately overwritten. So the binding to TimeValue will look for it in the control instead (which will, of course, fail).
Just don't touch the DataContext when developing a control and you'll be fine.
You don't need to override the data context of user control. You can use RelativeSource to point your binding source property i.e. TimeValue to any other source you like. E.g. If you have the source property in your window's class. You could simply point your binding target to the source in window's data context as follows:
{Binding Path=DataContext.TimeValue, RelativeSource={ RelativeSource AncestorType={x:Type Window}}}
Your error states that 'TimeValue' property not found on 'object' 'TimePicker', which means that the WPF Framework is looking at the 'TimePicker' object to resolve the 'TimeValue' property value. You must have somehow set the DataContext of the Window or UserControl that contains the 'TimePicker' object to an instance of the 'TimePicker' object.
Instead, it should be set to an instance of the class that declares the 'TimeValue' property. If you're using a view model, then you should set it to an instance of that:
DataContext = new YourViewModel();
If the 'TimeValue' property is declared in the Window or UserControl then you can set the DataContext to itself (although generally not recommended):
DataContext = this;
Please note that when data binding to the 'Time' property from inside your TimePicker control, you should use a RelativeSource Binding:
<TextBlock Text="{Binding Time, RelativeSource={RelativeSource
AncestorType={x:Type YourLocalPrefix:TimePicker}}}" ... />
Normally we are not setting datacontext directly.If u want to set datacontext create an instance of your usercontrol and set datacontext individually to each one.

How to create binding inside custom control automatically?

I have my custom toolbar control with DependencyProperty IsBusy
Here is how I use it:
<Controls:myToolbar
Grid.ColumnSpan="5" Mode="DataEntry"
Status="{Binding State, Converter={StaticResource ViewEditingStateToToolbarStateConverter}}"
IsBusy="{Binding IsBusy}"/>
By convention all my VM's inherit from base VM and have IsBusy property.
So, I KNOW that this property will always be available on VM.
Now I have another 4 properties like this. Instead of adding them to XAML on all my views I want to know how to bind to this IsBusy automatically inside control's code so I don't have to bind in XAML?
EDIT
Actually, I found answer to my question: Silverlight: Programmatically binding control properties
Now, my question is:
Is it correct to apply this binding in constructor like this?
public myToolbar()
{
this.DefaultStyleKey = typeof(myToolbar);
var binding = new Binding("IsBusy") { Mode = BindingMode.TwoWay };
this.SetBinding(IsBusyProperty, binding);
}
Should I check if XAML binding (another binding) exist to this property and not bind? It works either way but I wonder if it's bad for performance, smells, etc?
What about doing this in onApplyTemplate. Is that better way?
if (GetBindingExpression(IsBusyProperty) == null)
{
var binding = new Binding("IsBusy") { Mode = BindingMode.TwoWay };
this.SetBinding(IsBusyProperty, binding);
}
It would be bad if you tried to use this control with a view model which doesn't have the IsBusy property, but even then you'll receive just a debug warning in the output window, nothing to worry about.
As to the place of the binding, the constructor is appropriate if the dependency property which you are binding to doesn't perform any actions inside its callback.
But if the property changed callback tries to call such functions as GetTemplateChild and retrieve inner controls - then you should move the binding to the OnApplyTemplate functions, because only there you can be assured that inner controls exist.
By the way, if your dependency proeprty doesn't have a property changed callback and is used only in the control template like {TemplateBinding IsBusy}, you can replace this line by {Binding IsBusy}. Something like this, either by using binding or data triggers:
<ControlTemplate TargetType="{x:Type Controls:myToolbar}">
<Grid>
<ContentControl x:Name="content" ... />
<ProgressBar x:name="progress" ... />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsBusy}" Value="True">
<Setter TargetName="progress" Property="Visibility" Value="Visible" />
</DataTrigger>
The idea is simple: TemplateBinding is applied to dependency properties of the control, whereas Binding is applied to properties of the DataContext object or the view model and they can coexist without problems.

How to set focus to textbox using MVVM?

How to focus a textbox from ViewModel wpf?
<TextBox Name="PropertySearch"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, Path=PropertySearch,
ValidatesOnDataErrors=True}"
Width="110"
Height="25"
Margin="10" />
You can do this by adding a property to your ViewModel (or use an existing property) that indicates when the SetFocus should happen but the View should be responsible for actually setting the focus since that is purely View related.
You can do this with a DataTrigger.
View:
<Grid Name="LayoutRoot" DataContext="{StaticResource MyViewModelInstance}">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding UserShouldEditValueNow}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=PropertySearch}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBox Name="PropertySearch" Text="{Binding UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Path=PropertySearch, ValidatesOnDataErrors=True}" Width="110" Height="25" Margin="10" />
</Grid>
ViewModel:
// When you think the view should set focus on a control
this.UserShouldEditValueNow = true;
The example above is simplified by just using a boolean ViewModel property "UserShouldEditValueNow". You can add a property like this to your ViewModel or use some other exising property that indicates this state.
Note: So why is it done this way in MVVM? One reason is, suppose the View author decided to replace the TextBox with a ComboBox, or even better, suppose your property was an integer value that had both a TextBox to view/edit the number and a Slider as another way to edit the same value, both controls bound to the same property... how would the ViewModel know which control to set focus on? (when it shouldn't even know what control, or controls, are bound to it in the first place) This way the View can select which control to focus by changing the ElementName binding target in the DataTrigger Setter.
Happy coding!
The question you should be asking yourself is "why does my ViewModel need to know which control has the focus?"
I'd argue for focus being a view-only property; it's an interaction property, and has nothing to do with the conceptual state. This is akin to the background color of a control: why would you represent it in the VM? If you need to manage the focus in a custom way, it's probably better to use a view-level object to do the job.
In your parent control, add the following property:
FocusManager.FocusedElement="{Binding ElementName=PropertySearch}"
While purists may argue for leaving this out of the VM, there are cases where it may make sense to do so from the VM.
My approach has been to make the view implement an interface, pass that interface to the ViewModel, and then let the VM call methods on the interface.
Example:
public interface IFocusContainer
{
void SetFocus(string target);
}
A couple things to keep in mind:
A VM might serve more than one instance of a view, so your VM might want to have a collection of references to IFocusContainer instances, not just one.
Code the VM defensively. You don't know whether there are 0, 1 or 20 views listening.
The "target" parameter of SetFocus() should probably be "loosely" coupled to the VM. You don't want the VM caring about the exact control names in the UI. Rather, the VM should indicate a name that is defined solely for focus management. In my case, I created some attached properties that would allow me to "tag" controls with "focus names".
To implement the interface, you can:
Implement it in the code-behind
Create some behaviors that know how to attach to the ViewModel that is present in the DataContext.
There's nothing wrong with implementing it on the Code Behind, but the behavior approach does allow a XAML only hookup if that's important to you.
In the implementation of the interface, you can use the visual tree to locate the control, or you could just code up a switch statement for a known set of focusable items.

WPF ContextMenu bind some property to another property of the same control

I have a ContextMenu and a ColumnHeaderStyle defined in Window.Resource section which I use-it to a DataGrid ColumnHeader. My code is something like this:
<ContextMenu x:Key="cm_columnHeaderMenu"/>
<Style x:Key="DefaultColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContextMenu" Value="{StaticResource cm_columnHeaderMenu}" />
</Style>
<DataGrid Grid.Column="2" Grid.Row="1" x:Name="dgridFiles" IsReadOnly="True"
ColumnHeaderStyle="{StaticResource DefaultColumnHeaderStyle}">
I want to know if I can (and if the answer it true, then HOW I can I do it) bind the ContextMenu Visibility property to same control ContextMenu Items.Count > 0 property.
Initially based on some other treeView control selections made there shoud be no items in the context menu, but i wish to add dinamically items in ContextMenu based on selection in treeView. This part is done, the context has those items. On some selections there are no-items, but still on the grid it appears an empty ContextMenu. So I believe the easiest part it would be to bind the Visibility to Items.Count property of the same control.
Sorry if my english is not good enough, I'll try to explain better if i didnt make clear 1st time.
you want to bind via RelativeSource, especially the Self mode.
I think by reading this or this you will be able to achieve your goal.
Then you'll need a binding converter to convert the integer values to the matching type and values of the Visibility property. You'll find a short tutorial here.
Regards
Using this you can bind to the property in the same control
Visibility="{Binding Path=Items.Count, RelativeSource={RelativeSource Self}}"
You also have to use a converter to achieve what you want.
Just in case you need this
Try a converter to convert the value of the item count to a boolean. So you'll end up with something like
<ContextMenu Visibility={Binding RelativeSource={RelativeSource Self},
Converter={StaticResource ItemsToVisibilityConverter}, Path=Items.Count}} />
If that doesn't work, try this with data triggers (you still need a converter anyway, and this shows a converter at work):
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/a8ad8c14-95aa-4ed4-b806-d0ae874a8d26/

Categories