Cannot get Two Way Template Binding (WPF) to work - c#

I need help trying to understand why this is not working. According to MSDN, TemplateBinding is what should be used when binding the property of a control in a template to a property of the control implementing the template.
Except that Template Binding is not two-way. For two-way you need to use binding and then specify the relative source as TemplatedParent.
So I have the following XAML:
template
<ItemContainerTemplate x:Key="colHeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" VerticalAlignment="Center"/>
<ToggleButton Style="{StaticResource ToggleButtonStyle}" IsChecked="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Path=(props:VisibilityHelper.IsGroupCollapsed)}"/>
</StackPanel>
</ItemContainerTemplate>
which is used here
<dxg:GridColumn x:Name="Total" Header="Total" FieldName="field1" Width="Auto" HorizontalHeaderContentAlignment="Center" props:VisibilityHelper.IsGroupCollapsed="False" HeaderTemplate="{StaticResource colHeaderTemplate}">
<dxg:GridColumn.EditSettings>
<dx:TextEditSettings HorizontalContentAlignment="Center"/>
</dxg:GridColumn.EditSettings>
</dxg:GridColumn>
The toggle button in the template must set a dependency property on the grid column. This works fine when the template is binding to a parent ie. the controls are nested,
I just can't figure out what I am doing wrong.
MSDN ref - http://msdn.microsoft.com/en-us/library/ms742882.aspx
One of the many SO posts about this - In WPF, why doesn't TemplateBinding work where Binding does?
Thank you

Right so I have found the solution. Firstly DataTemplate does work. As #Quercus, it is all in the binding to the correct control.
In my case not the GridColumn but the GridColumnHeader. So this
IsChecked="{Binding RelativeSource={RelativeSource AncestorType=dxg:GridColumnHeader}, Path=DataContext.(props:VisibilityHelper.IsGroupCollapsed)}"
works perfectly...when bound to the correct parent.
Also as #Quercus stated, the template is actually nested and that is why this works. I used a tool called Snoop which actually shows you the visual tree of the application and then the datacontext of the selected element. Using this I solved this issue as well as 2 others I was having.
I really hope this helps someone somewhere before everyone goes to MAUI or WinUI 3.

Related

UWP ButtonColumn MvvmLight / Telerik / WindowsTemplate Studio

I´m trying to add a ButtonColumn to a TelerikDataGrid which was generated by the Windows Template Studio, without CodeBehind.
In a perfect world it would work like this, I think.
<tg:DataGridTemplateColumn x:Uid="Table_Open" >
<tg:DataGridTemplateColumn.CellContentTemplate >
<DataTemplate>
<Button x:Uid="Button_Open" Command="{x:Bind ViewModel.OpenCustomerCommand}"></Button>
</DataTemplate>
</tg:DataGridTemplateColumn.CellContentTemplate>
</tg:DataGridTemplateColumn>
This doesn't work, now I tried many opportunities but never reach the ViewModel.
I know in WPF it would work using
RelativeSource={RelativeSource AncestorType={x:Type UserControl},
But I don't get it reproduced in my UWP case.
I'm afraid you can't use x:bind to bind OpenCustomerCommand in DataTemplate, In general, if we want to bind viewmodel's OpenCustomerCommand we need set current Page DataContext as ViewModel then use binding markup extension to bind like the following.
<Button HorizontalAlignment="Right"
Margin="0,0,30,0"
Content="Favorite"
Command="{Binding ElementName=RootGrid,Path=DataContext.OpenCustomerCommand }"
/>
And this is the similar case that your could refer.

Radio button in Gridcolumn won't trigger binding

I have two devexpress gridcolumns with one radio button in each.
In code behind when I am setting a value to the properties binding to the radio button, the binding takes. But when I am changing selected radio button in the view the selected won't trigger the property.
what am I missing?
<dxg:GridColumn Binding="{Binding IsOrder}"
Header="Order"
Visible="{Binding IsVisible}"
Width="60">
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<RadioButton IsChecked="{Binding Path=Value, Mode=TwoWay}"
GroupName="{Binding RowData.Row.Number}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
IsEnabled="{Binding
Path=View.DataContext.StatusNotHandled}"/>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
<dxg:GridColumn Binding="{Binding IsNotOrder}"
Header="Not order"
Visible="{Binding IsVisible}"
Width="60">
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<RadioButton IsChecked="{Binding Path=Value, Mode=TwoWay}"
GroupName="{Binding RowData.Row.Number}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
IsEnabled="{Binding
Path=View.DataContext.StatusNotHandled}"/>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
The cause of your problem is most likely the fact that you are trying to use a non-DX control as an edit of a GridColumn which is discouraged explicitly by DevExpress. Try replacing the RadioButton with a DX-CheckEdit for example.
Key-take-aways from my experience:
do NOT use non-DX controls as InplaceEdits of a GridControl.
for DX-edits to work correctly when assigned in a CellTemplate
their name has to be set to 'PART_Editor'. Omitting that will lead to problems with navigation etc.
Use the the GridColumn.FieldName-property instead of binding. This should make the InplaceEdit inherit the binding from its Column automatically.
Change your binding for IsChecked to RowData.Row.IsOrder, it will probably solve your selection problem. You can read a little bit more information on using RowData.Row over Value or Data here.
On another note, as #Sancho Panza said, you're always better to stick with BaseEdit descendant when dealing with CellTemplate.
This is one of the advantages of using a BaseEdit descendant in CellTemplate :
For the DevExpress Data Editors (the BaseEdit class's descendants), you can set the editor's Name property to PART_Editor. In this case, the GridControl automatically adjusts its appearance and synchronizes the editor with the source field specified by the ColumnBase.FieldName or ColumnBase.Binding properties.
You can use any binding with an editor named PART_Editor. In this case, the GridControl's logic of getting/setting the editor's value is disabled.
The final decision of using a BaseEdit descendant or a WPF RadioButton is yours, but I also recommend sticking with BaseEdit.

MVVM binding in templates issue (specifically Expander control's HeaderTemplate)

Alright so I have a data template called GeneralVocabItemTemplateInput. In it is an expander control.
The data template is linked to a view model and so various items can be bound. In fact I can bind the header text for the expander as follows:
<DataTemplate x:Key="GeneralVocabItemTemplateInput">
<Grid Margin="2">
<Expander Header="{Binding ID}">
And that works fine. The header text displays the ID value. The point being that the expander's data context is the view model I want.
All good so far. Now rather than the ID as text I wanted a template instead so I could have more customisation. I created a data template that I could assign to the HeaderTemplate property of expander. So I created one in my resource dictionary that looks like this:
<DataTemplate x:Key="TemplateTest">
<StackPanel>
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Margin="2" Text="{Binding ID}"/>
</Border>
</StackPanel>
</DataTemplate>
Very simple, a set border as well as the same bound value from before.
I use that in the expander as follows:
<DataTemplate x:Key="GeneralVocabItemTemplateInput">
<Grid Margin="2">
<Expander HeaderTemplate="{StaticResource TemplateTest}">
Here's where I get the problem. The black border appears, but there's nothing inside it.
Clearly the template works and can be found since the border appears, but the binding doesn't. I've played around with it for a while and haven't come up with a solution.
One thing I did try was to change where TemplateTest was stored (the data template). It was in a separate resource dictionary file, so I moved it into the same file as my original template (GeneralVocabItemTemplateInput) to see if that made a difference. No difference.
I hope someone can shed some light on this.
It works at my side with the following change:
<Expander HeaderTemplate="{StaticResource TemplateTest}" Header="{Binding}">
Explanation: DataContext in the HeaderTemplate is set to the Header itself (which seems to be reasonable). When there is no Header set, the DataContext is therefore null.

WPF Binding to parent DataContext

We have a WPF application with a standard MVVM pattern, leveraging Cinch (and therefore MefedMVVM) for View -> ViewModel resolution. This works well, and I can bind the relevant controls to properties on the ViewModel.
Within a particular View, we have an Infragistics XamGrid. This grid is bound to an ObservableCollection on the ViewModel, and displays the appropriate rows. However, I then have a specific column on this grid which I am trying to bind a TextBox text value to a property on the parent DataContext, rather than the ObservableCollection. This binding is failing.
We've gone through several options here including:
Using AncestorType to track up the tree and bind to the DataContext of the parent UserControl like so (from the great answer to this question, as well as this one)...
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
Specifying the ElementName and trying to target the top level control directly. Have a look here if you'd like to read about using ElementName.
Using a 'proxy' FrameorkElement defined in the resources for the UserControl to try and 'pass in' the context as required. We define the element as below, then reference as a static resource...
<FrameworkElement x:Key="ProxyContext" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}"></FrameworkElement>
In this case the binding finds the FrameworkElement, but can not access anything beyond that (when specifying a Path).
Having read around, it looks quite likely that this is caused by the Infragistics XamGrid building columns outside of the tree. However, even if this is the case, at least options 2 or 3 should work.
Our last thoughts are that it is related to the V - VM binding, but even using Snoop we've yet to find what the exact issue is. I'm by no means an expert with WPF binding so any pointers would be appreciated.
EDIT: I have found some templating examples from Infragistics here that I will try.
EDIT 2: As pointed out by #Dtex, templates are the way to go. Here is the relevant snippet for use with a XamGrid:
<ig:GroupColumn Key="CurrentDate">
<ig:GroupColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DataContext.CurrentDateTest, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</DataTemplate>
</ig:GroupColumn.HeaderTemplate>
<ig:GroupColumn.Columns>
I've left the XML open... you'd simply add the columns you wanted, then close off the relevant tags.
I dont know about XamGrid but that's what i'll do with a standard wpf DataGrid:
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Since the TextBlock and the TextBox specified in the cell templates will be part of the visual tree, you can walk up and find whatever control you need.
Because of things like this, as a general rule of thumb, I try to avoid as much XAML "trickery" as possible and keep the XAML as dumb and simple as possible and do the rest in the ViewModel (or attached properties or IValueConverters etc. if really necessary).
If possible I would give the ViewModel of the current DataContext a reference (i.e. property) to the relevant parent ViewModel
public class ThisViewModel : ViewModelBase
{
TypeOfAncestorViewModel Parent { get; set; }
}
and bind against that directly instead.
<TextBox Text="{Binding Parent}" />

How to bind item values in an accordion header template

I'm trying to create a simple header template for an accordion object in silverlight 4.
I've added an image and a TextBlock to the header template of the AccordionItem. I want to hide or show the image dependant on the values entered on the page.
Because i want to bind these values directly to the actual accordion item, I've created a new type 'AccordionItemWithIcons' that simply inherits from AccordionItem but adds a couple of dependancy properties to handle this. I'm only showing a couple of those properties for brevity. :)
So, here's my accordion with my 'AccordionItemWithIcons' control. Note that the property 'CheckIsVisible' is of type 'Visibility'
<Grid x:Name="LayoutRoot">
<Controls:Accordion Height="100">
<my:AccordionItemWithIcons
x:Name="FirstItem"
Content="Content Text"
Header="Header Text"
CheckIsVisible="Collapsed"
EventSummary="Summary Text"
HeaderTemplate="{StaticResource AccordionItemHeaderTemplate1}"/>
</Controls:Accordion>
</Grid>
And here is the header template.
<DataTemplate x:Key="AccordionWithIcons_HeaderTemplate1" >
<Grid >
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<TextBlock Text="{Binding EventSummary}" />
<Image Visibility="{Binding CheckIsVisible}" Source="/Labyrinth;component/cross.png"/>
</StackPanel>
</Grid>
</DataTemplate>
Can anyone explain how I can bind the TextBlock's text and the Image's Visibility to the values set in the underlying AccordionItemWithIcons object? I've spent hours messing about with different DataContext's and sources and cannot seem to get this to work!
I don't know if helps to explain what I'm trying to achieve, but ultimately in the code behind i want to be able to say something like (shown below), to show or hide the icon in the header template.
FirstItem.CheckIsVisible = Visibility.Visible
For this, there exists a VisibilityToBooleanConverter
<BooleanToVisibilityConverter x:Key=”boolVisConverter”/>
[...]
Visibility="{Binding ElementName=anyCheckbox,
Path=IsChecked,
Converter={StaticResource boolVisConverter}}"

Categories