Binding an XceedData Column EditTemplate to different property - wpf

I have an Xceed DataGrid with an EditTemplate defined for the grid.
The grid shows a listing of items bound to a collection with about 6 columns and the EditTemplate is a TextBox control for entering a quantity. I want the IsReadOnly property to be bound to a different property so that the item has a serial number, IsReadOnly will be set to true so the user cannot enter a value. I want to bind to the SerialNum property within the same collection and pass it to a converter to return the true/false value. I've got the converter written; however, I'm having issues binding to the property to pass to the converter.
My DataGridCollectionViewSource is simple enough:
<xcdg:DataGridCollectionViewSource x:Key="transferItems" Source="{Binding TransferItems}" />
TransferItems is set in my ViewModel and all of the columns get properly bound.
For all of my generic display columns, they are being properly displayed via:
<xcdg:Column Title="Serial No." AllowSort="False" FieldName="SerialNum" />
My issue is in defining the xcgd:CellEditor template and I'm pretty sure my issue is around the RelativeSource. I've tried many different combinations trying to get the TransferItems.SerialNum property from my ViewModel, but no combination is working.
This is what I currently have:
<xcdg:Column Title="Xfer Qty Good" TextWrapping="Wrap" ReadOnly="False" Width="50" AllowGroup="False" AllowSort="False" FieldName="TransferQtyGood">
<xcdg:Column.CellEditor>
<xcdg:CellEditor>
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<TextBox x:Name="QtyGood" Margin="2,2,2,2" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{xcdg:CellEditorBinding}" IsReadOnly="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type xcdg:DataGridCollectionViewSource}}, Path=DataContext.TransferItems.SerialNum, Converter={StaticResource serialToEnabledConverter}}"
/>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
</xcdg:Column.CellEditor>
</xcdg:Column>
Which gives the runtime error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Xceed.Wpf.DataGrid.DataGridCollectionViewSource', AncestorLevel='1''. BindingExpression:Path=DataContext.TransferItems.SerialNum; DataItem=null; target element is 'TextBox' (Name='QtyGood'); target property is 'IsReadOnly' (type 'Boolean')
I understand what the error is telling me, but I'm just getting the proper RelativeSource path. I've read some of the helpful posts here on the enumerations for RelativeSource and still am missing something.

Just in case someone is having this issue, I was able to finally get the binding working in this manner. The key is defining the correct RelativeSource path:
<TextBox x:Name="QtyGood" Margin="2,2,2,2" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{xcdg:CellEditorBinding}" GotFocus="Qty_GotFocus" LostFocus="Qty_LostFocus"
IsReadOnly="{Binding RelativeSource={RelativeSource AncestorType={x:Type xcdg:DataRow}}, Path=DataContext.SerialNum, Converter={StaticResource serialToEnabledConverter}}"
/>

Related

WPF CommandParameter value is changing to null on execution [duplicate]

My WPF uses the MVVM approach. I'm trying to bind 2 controls within my list control
<ListBox ItemsSource="{Binding ParentDuplicate}" SelectedItem="{Binding SelectedParent, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ContentControl Content="{Binding}" />
<Button Content="Delete me now"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType=Window}, Path=DeleteCommand}"
CommandParameter="{Binding FilePath}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem I'm having, is the DeleteCommand is not binding (the Ouput window informs me as well)
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Data.Binding', AncestorLevel='1''. BindingExpression:Path=DeleteCommand; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
If I move this button to outside the ListBox, then the binding works and the event fires so I know the issue must be with the ListBox (I'm guessing the problem is the ItemsSource prevents the binding to anything but the ItemsSource bound property (in this case, ParentDuplicate))
So, within the Button control, there are 2 properties being bound, DeleteCommand and FilePath
Both of these properties live within my single ViewModel. FilePath is a child of ParentDuplicate and this binds as desired. The issue is only with the DeleteCommand. What am I doing wrong?
Edit
When I use the Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType=Window}, Path=DeleteCommand} it is looking in my MainWindow code behind, not at the ViewModel. How do I make it use the ViewModel?
I tried
Command="{Binding RelativeSource={RelativeSource AncestorType=xmlnsViewModel:MainWindowViewModel}, Path=DeleteCommand}"
but the above also results in the same binding errors
The ancestor search finds the control, not the DataContext, so you'll need to tell your binding where to find the DeleteCommand property. If your ViewModel is the DataContext of the MainWindow then you can just use:
<Button Content="Delete me now"
Command="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorLevel=1, AncestorType=Window},
Path=DataContext.DeleteCommand}"
CommandParameter="{Binding FilePath}" />

DataGrid Column Header binding inside Expander: Binding source not found when expander is collapsed

I have DataGrid which has a column that is to be shown/hidden depending on a property. I have had no luck in showing/hiding the column, so we ended up taking the column out and put it in a separate DataGrid and place the second DataGrid inside an Expander.
The header of the second DataGrid is bound the the DataContext of the Expander (or DataGrid - same effect, have also tried binding it to the containing usercontrol).
<Expander ExpandDirection="Right" Style="{DynamicResource ExpanderDirectionRight}" IsExpanded="{Binding ShowLaterDate}">
<DataGrid AutoGenerateColumns="False" AlternationCount="2" SelectionMode="Single" ItemsSource="{Binding Rows, UpdateSourceTrigger=PropertyChanged}" CanUserDeleteRows="True" CanUserAddRows="False" CanUserResizeColumns="False" SnapsToDevicePixels="True" Visibility="{Binding Rows.HasContent, Converter={StaticResource BooleanToVisibilityConverter}}" HorizontalAlignment="Left" SelectedIndex="{Binding SelectedIndex, ElementName=BudgetDataGrid}">
<DataGrid.Columns>
<DataGridTemplateColumn CellTemplate="{StaticResource LaterDataTemplate}" Width="{StaticResource WidthOfValueColumns}">
<DataGridTemplateColumn.Header>
<Grid Margin="{StaticResource MarginOfTextBox}">
<userControls:DateOrAgeEditor Date="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=DataContext.LaterDate, UpdateSourceTrigger=LostFocus}" DateOfBirth="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=DataContext.DateOfBirth}" TabIndex="11"/>
</Grid>
</DataGridTemplateColumn.Header>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Expander>
This is all good an well.
Problem:
The bound value is shown when the usercontrol is displayed. BUT ONLY if the Expander is expanded!
If the Expander is collapsed when the usercontrol is displayed this is printed to output:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Expander', AncestorLevel='1''. BindingExpression:Path=DataContext.DateOfBirth; DataItem=null; target element is 'DateOrAgeEditor' (Name='UserControl'); target property is 'DateOfBirth' (type 'DateTime')
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Expander', AncestorLevel='1''. BindingExpression:Path=DataContext.LaterDate; DataItem=null; target element is 'DateOrAgeEditor' (Name='UserControl'); target property is 'Date' (type 'DateTime')
When the Expander is then expanded the binding is not updated and continues to be 'broken'.
Question:
How do I get the binding to bind correctly even when the Expander is not initially expanded?
Just found a way of binding the Visibility of my column (http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/) this has however reintroduced the same problem as the expander. But easy to fix, just use the same kind of binding for the header-content as for the visibility.

WPF: How to bind to the name property

Can i bind to the name property?
This does not seem to work:
<TextBlock Name="FordPerfect" Text="{Binding Path=Name, Mode=OneWay}"/>
Am i doing something wrong?
Edit:
Adding ElementName=FordPerfect" solved the issue. What i don't understand is why only binding to Name required this while other properties don't.
Note: Moved the second (design) issue to another question (where i should have placed in the first time...)
Thanks
I would try this :
<TextBlock Name="FordPerfect"
Text="{Binding ElementName=FordPerfect, Path=Name, Converter={StaticResource conv}, Mode=OneWay}"/>
This way, your TextBlock will be the context of the binding.
If it does not work, watch the Output window, you should find a binding error !
you could have more easily done this:
<TextBlock Name="FordPerfect"
Text="{Binding Name, Converter={StaticResource conv}, Mode=OneWay, RelativeSource={RelativeSource Self}}"/>
As to why: that textbox' DataContext is not automatically the TextBox itself. So binding to Name tries to bind to whateverObjectInDataContext.Name. So either you set the DataContext beforehand like:
<TextBlock Name="FordPerfect" DataContext={Binding RelativeSource={RelativeSource Self}}
Text="{Binding Name, Converter={StaticResource conv}, Mode=OneWay}"/>
... or directly set a Source for the Binding
The issue you're having is a Binding, by default, uses the DataContext of the element it's used on as its source. However you want the binding source to be the TextBlock element itself.
WPF has a class called RelativeSource which, as its name implies, sets the source relative to the binding. One of the relations you can choose is Self which does exactly what you want: sets the source of the binding to the element it's used on.
Here's the code:
<TextBlock Name="FordPerfect" Text="{Binding Name, RelativeSource={RelativeSource Self}}" />
Since you're already setting the source with RelativeSource, you don't need to specify ElementName. You also don't need Mode=OneWay as a TextBlock.TextProperty already defaults to one-way since it's output-only.
Hope this helps!

WPF Binding to ElementName inside ItemsControl

I have a checkbox, and an ItemsControl populating several DataGrids the following way:
<Checkbox Content="Birthday Column Visible" x:Name="UI_BirthdayVisibleCB" />
<ItemsControl ItemsSource="{Binding Path=ParentsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Children}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Birthday" Width="120" Visibility="{Binding IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" >
...
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Rest of closing tags>
This creates binding output errors as it tries to find IsChecked on the DataGridTemplateColumn. If I try to search for a Relative Ancestor I receive the exception:
Binding.RelativeSource cannot be set while using Binding.ElementName.
I have a ViewModel, and stick to MVVM mostly, but in this case I'd really like to keep the column visibilities on the View layer. Note that BoolToVis just converts Boolean to Visibility.
Edit
Here is an example of what I'm trying to do:
<DataGridTemplateColumn Header="Birthday" Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" />
It compiles but doesn't run however, it throws the exception above.
You are using RelativeSource, which can't be mixed with ElementName, but you once you have the correct RelativeSource, you can drill down deeper using path.
e.g.
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=UI_BirthdayVisibleCB.IsChecked, Converter={StaticResource BoolToVis}}"
presumably you have some xaml like this:
<UserControl class="MyView" ... >...<CheckBox Name="UI_BirthdayVisibileCB"/> ...
The above binding should find this UserControl by type based on RelativeSource, then it will try to find a property named UI_BirthdayVisibleCB, which it won't find because WPF XAML implements this named element as a field.
The easy work around is to go into your codebehind and expose a property for it.
public object BirthdayVisibileCB_4_binding {
get { return UI_BirthdayVisibileDB; }
}
and bind to it instead:
Visibility="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MyView} },
Path=BirthdayVisibileCB_4_binding.IsChecked, Converter={StaticResource BoolToVis}}"
Yes, it kind of a pain to do this, but MVVM only matches WPF so far... its not a great fit, its only the best fit we have around.
If you want to try RelativeSource, you have to remove ElementName from the declaration:
However, only one of the three
properties, ElementName, Source, and
RelativeSource, should be set for each
binding, or a conflict might occur.
This property throws an exception if
there is a binding source conflict.
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.elementname.aspx
Your usage of ElementName seems correct, so I'll continue to look at the problem if you prefer that over RelativeSource.

WPF Treeview contextmenu IsChecked binding MVVM

I've got a TreeView to which I associate a ContextMenu. That contextmenu has an item whose IsChecked property I want to bind to my ViewModel. Since I am using a tree each treeitem is bound to a subproperty of my ViewModel.
In the VS2010 output window I am seeing this databinding error:
BindingExpression path error: 'IsAutoStart' property not found on 'object' ''HostMgmtViewModel' (HashCode=12565727)'. BindingExpression:Path=IsAutoStart; DataItem='HostMgmtViewModel'
This clearly shows it is trying to bind to my ViewModel and not to the treeitem's associated data. How do I bind to the correct object? Remember my contextmenu is associated with the whole TreeView not to the specific treeitem.
---------- Edit
As xandy pointed out below the resolution to my problem was to bind the IsChecked like this:
{Binding Path=PlacementTarget.SelectedItem.IsDisabledStart, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}
<TreeView Name="tview" Grid.Row="0" Tag="{Binding RelativeSource={RelativeSource Self}, Path=SelectedItem}">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Name="miC" Header="{Binding Path=Tag.Key}" DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"></MenuItem>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
This is the working code snippet I have. Courtesy of [this].1 All you need is to change the binding path in the tag. I am currently binding the Treeview to a dictionary, so it is the Key property of it. It should not have any problem in binding to any object collections. One interesting finding is context menu is not in part of element tree and this cause the problem. I could bind the text box with no problem:
<TextBlock Grid.Row="1" DataContext="{Binding ElementName=tview, Path=SelectedItem}">
<TextBlock.Text>
<Binding Path="Key" />
</TextBlock.Text>
</TextBlock>
But it is not functioning if for menuitem if I put the same thing.

Resources