RelayCommand called in "old" DataContext - wpf

I am using RelayCommands in my ViewModels to ged rid of the code behinds. The RelayCommands are working, except in this situation: the ViewModel in which the RelayCommands are defined is changed with the change of the datacontext. If a command is being fired after the datacontext change, it is fired in the "old" viewmodel. While debugging I can see that the properties are from the old datacontext. But in the view is everything fine because the correct data of the correct viewmodel is displayed.
So is it possible that the bounded commands are not able to react to a change of the datacontext?
Or might there something else being wrong?
Here a snip of the code:
<TabControl x:Name="TestView" DataContext="{Binding Path=SelectedParentElement}"
TabStripPlacement="Top" ItemsSource="{Binding Path=ChildElements, Mode=OneWay}"
SelectedValue="{Binding Path=SelectedChildElement, Mode=TwoWay}">
<TabControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Klassifikation" Name="Klassifikation">
<MenuItem Header="Kindebene" Name="KlassisfizierendNext" Command="{Binding KlassifizierendNextCommand}"/>
</MenuItem>
</ContextMenu>
</TabControl.ContextMenu>
.......
</TabControl>
The datacontext of the tabcontrol is correctly changing, but after changing the command is being called in the old datacontext.

Found a solution: http://social.msdn.microsoft.com/Forums/en/wpf/thread/2cbec263-df05-4000-9077-35861fc2fa8e
But it seems to be a bug that the datacontext auf contextmenus dont change right.

#Rick. The reason for the downvote:
It doesn't answer the OP's question. He's debugging the data context, you're doing everything in XAML.
It doesn't work for me. I assume that this is due to the same bug that the OP noticed.
To be fair, this isn't your fault: it's clearly a bug in the way that Microsoft is handling DataContext inheritance for ContextMenus, but your answer doesn't appear to have solved the OP's problem, nor mine.

Related

Binding DataContext in XAML With DataContext Set In Code

I have the following code:
ProcessMainWindow.xaml.cs
public ProcessMainWindow(SourceTableRowInfo rowContent)
{
InitializeComponent();
this.DataContext = rowContent;
}
ProcessMainWindow.xaml
<!--Insert Code---->
<TabItem x:Name="postProcessTab" Header="Post-Processes">
<local:PostProcessUserControl PostProcessItem="{Binding PostProcess, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</TabItem>
So RowContent has an element called PostProcess in it. I am trying to bind that element to a UserControl Dependency Property, but cannot get the binding to work. Based off what I was reading here (Using the DataContext) my understanding is that what I have should work, but I can't get it to work. So am I misunderstanding what it is saying? I have read a few other pages but still can't figure it out.
I have also tried:
<!--Insert Code---->
<TabItem x:Name="postProcessTab" Header="Post-Processes">
<local:PostProcessUserControl PostProcessItem="{Binding, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Path=PreProcess}" />
</TabItem>
If these are correct, I guess I have an error elsewhere in my code. I have yet to fully understand data binding in WPF to know if that is the case though so any help would be appreciated.
One thing to try is to put a trace on the Binding:
PostProcessItem="{Binding PostProcess, PresentationTraceSources.TraceLevel=High}.
Then look for what it tells you at runtime in the Output pane in VS. This can help you identify cases where your DataContext isn't what you think it is, or your Path is misspelled -- all the simple stuff that the compiler catches in C# but can't be detected at compile time in a late-binding/duck-typed miasma like XAML.
Don't leave those traces on bindings once you're done with them; they can really slow things down. Or at least set TraceLevel=None, to save trouble if think you'll be coming back to one later.

How to bind a child user control's data context in the parent

<TextBlock Text="{Binding ChildGroupName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, UpdateSourceTrigger=PropertyChanged,NotifyOnTargetUpdated=True,Mode=TwoWay}"
TargetUpdated="OnTextUpdated"/>
Here ChildGroupName is a child control datacontext property. I want to bind ChildGroupName values to parent window.
You cannot use FindAncestor to data bind to a descendant's data... the clue is in its name. If the child UserControl is defined in the same XAML as the parent, then you can provide it with a name and then use the Binding.ElementName Property to data bind to its properties:
<TextBlock Text="{Binding ChildPropertyName, ElementName=NameOfChildUserControl,
UpdateSourceTrigger=PropertyChanged, NotifyOnTargetUpdated=True, Mode=TwoWay}"
TargetUpdated="OnTextUpdated" />
Sheridan's answer did not work for me, because ReSharper was issuing a warning saying that "ChildPropertyName" is an unknown property.
Now, I did not actually try Sheridan's solution; it may be that it would have worked; it may be that WPF does smart tricks under the hood and manages to get things to work even with Sheridan's approach; however, for me, all warnings must always be enabled, and all code must be absolutely free from warnings, so I had to look for a solution that would not only work, but also work without eliciting a warning from ReSharper.
What worked for me was adding DataContext., as follows (without the extra clutter):
<TextBlock Text="{Binding DataContext.ChildPropertyName,
ElementName=NameOfChildUserControl}" />
In other words, when you use ElementName, the DataContext becomes the element itself, (which makes sense,) so in order to get to the actual viewmodel you need to first reference the DataContext of the element.

Why is my ComboBox SelectedItem null?

I have a WPF/MVVM (using MVVM-Light) app setup with a ComboBox that is inside a DataTemplate. The XAML of the ComboBox looks like this:
<ComboBox x:Name="cbTeachers"
Grid.Column="1"
Style="{StaticResource ComboBox}"
ItemsSource="{Binding Teachers}"
Grid.Row="3"
DisplayMemberPath="Name"
SelectedValuePath="Id"
IsSynchronizedWithCurrentItem="False"
SelectedItem="{Binding Path=SelectedTeacher}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding TeacherSelectedCommand}"
CommandParameter="{Binding SelectedItem, ElementName=cbTeachers}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
The Teachers property for the ItemsSource is a type called ObservableRangeCollection and is based on the code found here: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx, but it's very similar to a standard ObservableCollection. The SelectedTeacher property is set when another property is set and the code looks very similiar to this:
this.SelectedTeacher = (from t in this.Teachers where t.Id == this.DataItem.Teacher.Id select t).Single();
The problem I am running into, which makes zero sense to me, is SelectedTeacher is getting reset to null once I set it. I can step through the debugger and see SelectedTeacher has a value and when I put a breakpoint on the setter for the property it definitely has the value. But then that property gets hit again with a null value. I checked the call stack and it showed the only preceeding line as being External Code (which makes sense since I only set that property in one place and it only gets hit once, as expected). Expanding the External Code option in the call stack window shows the typical WPF call stack of maybe 40 methods so it's definitely internal to WPF and not something I am doing to make it reset. In fact, when I remove the SelectedItem="{Binding SelectedTeacher}" the setter for that property doesn't get called a second time (thus it retains its value), but of course the ComboBox doesn't show the selected item either. I tried implementing a SelectedIndex option in my viewmodel but that didn't work either. The ComboBox just won't select the item. I can change the selected item in the ComboBox just fine, but the initial setting won't take.
Any ideas? Based on everything I've searched it might be related to me using a DataTemplate, but I have to because that template is part of a parent ContentTemplateSelector implementation.
As a side note, I have multiple properties that bind to controls in this DataTemplate and this is the only one that doesn't work. The others work perfectly. I have also tried the ComboBox with and without the "IsSynchronizedWithCurrentItem" flag and it made no difference.
have you tried to remove to EventTrigger stuff and just to use
SelectedItem="{Binding Path=SelectedTeacher, Mode=TwoWay}"
with Mode=TwoWay?
its not clear to me what you want to achieve with your EventTrigger?

Are "{Binding Path=.}" and "{Binding}" really equal

In my WPF project, I have a ListBox that displays items from a List<string> collection. I wanted to make the text of these items editable, so I wrapped each of them in an ItemTemplate with a TextBox (might not be the best way, but I'm new to WPF). I was having trouble simply binding the TextBoxes' Text property to the value of each item. I finally stumbled upon an example using a single dot or period for its Path property ({Binding Path=.}):
<ListBox ItemsSource="{Binding ElementName=recipesListbox,Path=SelectedItem.Steps}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
However I don't understand why simply using {Binding} didn't work.
It raised a "Two-way binding requires Path or XPath" exception, as according to Microsoft:
[...] a period (.) path can be used to bind to the current source. For example, Text="{Binding}" is equivalent to Text="{Binding Path=.}"
Could someone shed light on this ambiguous behavior?
EDIT: Moreover, it seems {Binding Path=.} does not necessarily give two-way binding, as modifying the text and moving the focus does not update the underlying source (the same source has also properties displayed and successfully modified on a DataGrid control). I'm definitely missing something here.
The point of the exception presumably is that you cannot two-way bind a binding-source itself, so it tries to prevent you from creating a binding which does not behave the way you would want it to. By using {Binding Path=.} you just trick the error detection.
(Also it's not unheard of that documentation is erroneous or inaccurate, though i do like the MSDN documentation a lot in general as it usually does contain the crucial points one is interested in)
The documentation states that {Binding} is equivalent to {Binding Path=.}. However it is not equivalent to {Binding Path} as you have typed. If you include the Path property, you must assign it to something, be it Path=. or Path=OtherProperty.
These are not the same. If you bind this where ConsoleMessages is an ObservableCollection string with just {Binding} you get a "Two-way binding requires Path or XPath." exception where as {Binding Path=.} works. This is with WPF 4.0...
<ItemsControl x:Name="ConsoleOutput" ItemsSource="{Binding ConsoleMessages, Mode=OneWay}" MaxHeight="400">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=.}" BorderThickness="0" Margin="0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My 2p worth...
In short, the difference between the two is analogous with the difference between the traditional pass by value and pass by reference. (FYR - What's the difference between passing by reference vs. passing by value?)
However I don't understand why simply using {Binding} didn't work (it raised a "Two-way binding requires Path or XPath" exception)
Lets assume here for now that {Binding} can be used for two way binding. In general {Binding} creates a value based link with datacontext which does not allow updating the datacontext.
Whereas {Binding Path=.} creates reference based link with the memory area referenced by the 'Path' which allows updating the value through reference.(in this case 'dot' the current datacontext).
Hope this helps!

SL ItemsControl, command on ViewModel not firing from ItemsControl (CheckBox)

I'm using PRISM v2, CAL, SL4 and MVVM and have a delegate command on my ViewModel called CheckCommand. The ItemsControl contains a checkbox and I'm trying to get the items in ItemsControl/Checkbox to fire this command when it's checked - but it's not communication back to the viewmodel!
I think it's because each items 'datacontext' is the individual object the item is bound to, rather than the ViewModel?
- My suspicion is actually correct, cause if I move my DelegateCommand out of the viewmodel and into the class defining the items in itemscontrol I can see the commands/methods beeing fired!
View:
<ListBox x:Name="BasketListBox" ItemsSource="{Binding BasketCollection}" MinWidth="200">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox commands:Checked.Command="{Binding CheckCommand}" IsChecked="False" </CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
Can anyone point me in the right direction please?
Cheers, Mcad.
EDIT 1:
The commanding now works, see solution below. BUT, I now run into another problem:
"An exception occurred while creating a region with name 'basketRegion'. The exception was: System.InvalidOperationException: ItemsControl's ItemsSource property is not empty. This control is being associated with a region, but the control is already bound to something else. If you did not explicitly set the control's ItemSource property, this exception may be caused by a change in the value of the inherited RegionManager attached property"
Created seperate question for this problem to make it more clean:
PRISM-MVVM, ItemsControl problem with View injection
You want every CheckBox to fire the same command? You could:
<CheckBox commands:Checked.Command="{Binding DataContext.CheckCommand, ElementName=BasketListBox}"
Or you could have every child view model expose the command via their own property.
Thanx Kent. You put me on the right path to solve this, ended up doing this:
<ListBox x:Name="basketListBox" ItemsSource="{Binding basketcollection}" MinWidth="200">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox commands:Checked1.Command="{Binding DataContext.CheckCommand, ElementName=basketListBox}" Content="{Binding basketName}"> </CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>

Resources