DataGrid column cell underline binding issue - c#

I am having issue binding text decoration to datagrid column DataTemplate. Here is the xaml setup.
<UserControl x:Class="myClass"...>
<DataGrid
Name="_dataGrid"
FontFamily="{Binding Font_Family}"
FontWeight="{Binding Font_Weight}">
<Controls:DataGrid.Columns>
<Controls:DataGridTemplateColumn>
<Controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Data1}"
Foreground="Red"
//This binding does not work
TextDecorations="{Binding Decor_Underline"/>
</DataTemplate>
</Controls:DataGridTemplateColumn.CellTemplate>
</Controls:DataGridTemplateColumn>
</Controls:DataGrid.Columns>
</DataGrid>
</UserControl>
The error i get goes something like this.
BindingExpression with XPath cannot bind to non-XML object.; XPath='Model.Decor_Underline' BindingExpression:Path=Decor_Underline; DataItem='DataGrid'
If i manually set the text decoration it works fine. TextDecoration = "Underline"
The bindings for Font_Family and Font_Weight is working fine.
I have tried doing the following; But since DataGrid does not have TextDecorations property it did not help.
TextDecorations="{Binding Decor_Underline ,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}
Here is the property in the model. The event is raised as expected without no effect on the control - its as if no one is listening. The scenario is i press a button and the grid columns textbox gets underlined.
public TextDecorationCollection Decor_Underline {
get { return decor_Underline; }
set {
if(someFlg)
decor_Underline = TextDecorations.Underline;
RaisePropertyChanged("Decor_Underline");
}
}
TextDecorationCollection decor_Underline;
Any Ideas?

I wonder why you want to create your own DataGridTemplateColumn when it contains just a TextBlock ? I have little experience with DataGridTemplateColumn, but why not use a DataGridTextColumn ? Here is a solution that works with DataGridTextColumn and maybe you can do it similarly for DataGridTemplateColumn ?
<Window.Resources>
<Style x:Key="TextDecorationsStyle" TargetType="TextBlock">
...
<Setter Property="TextDecorations"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}},
Path =Item.Decor_Underline}" />
</Style>
</Window.Resources>
...
<DataGrid ...>
...
<DataGrid.Columns>
...
<DataGridTextColumn Binding="{Binding Path=Data1}"
ElementStyle="{StaticResource TextDecorationsStyle}"/>
</DataGrid.Columns>
For the complete code and in detail explanation how it works see my article on CodeProject: Guide to WPF DataGrid Formatting Using Bindings . It describes how most DataGrid formatting can be done with binding, including TextDecorations.

Related

ToolTipService.ToolTip not working on DataGridCell in UWP XAML

i'm trying to display a tooltip over a cell in a Windows Toolkit DataGrid in UWP XAML.
I'm able to show a static value by doing this:
<wct:DataGridTextColumn Width="200" Binding="{Binding ChargeInformationDto.Description, Mode=OneWay}" Header="Navn" Tag="Description">
<wct:DataGridTextColumn.CellStyle>
<Style TargetType="wct:DataGridCell">
<Setter Property="ToolTipService.ToolTip" Value="Tooltip text to show" />
</Style>
</wct:DataGridTextColumn.CellStyle>
</wct:DataGridTextColumn>
But with Bindings it does not show:
<wct:DataGridTextColumn Width="200" Binding="{Binding ChargeInformationDto.Description, Mode=OneWay}" Header="Navn" Tag="Description">
<wct:DataGridTextColumn.CellStyle>
<Style TargetType="wct:DataGridCell">
<Setter Property="ToolTipService.ToolTip" Value="{Binding ChargeInformationDto.Description, Mode=OneWay}" />
</Style>
</wct:DataGridTextColumn.CellStyle>
</wct:DataGridTextColumn>
Anyone managed to make this work?
In UWP XAML, you cannot use binding in Style. For specific instructions, please refer to this document:
Windows Presentation Foundation (WPF) and Microsoft Silverlight supported the ability to use a Binding expression to supply the Value for a Setter in a Style. The Windows Runtime doesn't support a Binding usage for Setter.Value (the Binding won't evaluate and the Setter has no effect, you won't get errors, but you won't get the desired result either).
As you can see in the source code of DataGridTextColumn, DataGridTextColumn will dynamically create a TextBlock and TextBox to display and edit content, so using TooltipService directly in the XAML to set Tooltip will not be attached to the newly created TextBlock, which is the reason of Tooltip cannot be displayed.
If you want to display Tooltip, then you can consider creating a DataGridTemplateColumn:
<controls:DataGridTemplateColumn Width="200"
Header="Navn" Tag="Description">
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ChargeInformationDto.Description, Mode=OneWay}" FontSize="20"
ToolTipService.ToolTip="{Binding ChargeInformationDto.Description, Mode=OneWay}"/>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>

WPF DataGridTextColumHeaderTemplate bind width to TextColumnWidth of "*"

I have a DataGrid with DataGridTextColum of width="*" (multiple of them, so that they're all divided equally) and I'd like to set the width of this DataGridTextColumn's DataGridTextColumn.Header to its parent width (DataGridTextColumn).
However the code doesn't work, reason is I'm not defining RelativeSource correct, so my question is, how do I define Binding to Width inside HeaderTemplate of DataGridTextColumn to bind to DataGridTextColumn.ActualWidth? Code below!
<DataGridTextColumn Width="*" Binding="{Binding Username}" CanUserReorder="False" CanUserSort="False" x:Name="DataGridTextColumn">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="Red" MouseLeftButtonDown="EventSetter_OnHandler" Width="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType=DataGridTextColumn},Path=ActualWidth, UpdateSourceTrigger=PropertyChanged}">
<Label>Username</Label>
</StackPanel>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
If I change the binding source to ElementName, everything works fine as expected, but i'd like to use RelativeSource to reduce amount of naming that I have to do.
Thanks!
DataGridColumn is not a FrameworkElement, so it won't appear in the VisualTree. It is merely a information-holder entity. What actually gets rendered is a DataGridColumnHeader for every Column. So, your HeaderTemplate contents would be present in this DataGridColumnHeader. And this DataGridColumnHeader uses its Column property to maintain its association with DataGridColumn. So,
Change your Width of StackPanel to Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridColumnHeader},Path=Column.ActualWidth, UpdateSourceTrigger=PropertyChanged}" .

How to get DataGridTemplateColumn Header Binding working?

A Window contains some elements plus a DataGrid. The DataContext of the Window is set to a ViewModel, and the DataGrid is bound to a Property of the ViewModel:
<DataGrid ItemsSource="{Binding FilteredMessages}" AutoGenerateColumns="False" >
Now I'd like to bind the Headers of the Columns to properties of the ViewModel, like e.g.:
<DataGrid.Columns>
<DataGridTemplateColumn Header="{Binding TimeColumnHeaderText}" Width="Auto" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding (logging:LogMessage.Time), StringFormat=\{0:yyyy-MM-dd HH:mm:ss.fff\}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
While the contents of the grid is shown correctly, the Header fails to bind.
I've found a description of a BindingProxy at http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ but failed to get it working. That solution requires a Resource in the DataGrid
<DataGrid.Resources>
<wpfUtilities:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
and a reference to that proxy in the Binding clause:
<DataGridTemplateColumn Header="{Binding TimeColumnHeaderText, Source={StaticResource proxy}}" Width="Auto" IsReadOnly="True">
I replaced {Binding} in the resources section with {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}} but the property is not found on the proxy:
BindingExpression path error: 'TimeColumnHeaderText' property not found on 'object' ''BindingProxy'
Also tried to move the proxy to the resources section of the window, didn't help either.
Remember, you bound {Binding} to the proxy's Data property:
<wpfUtilities:BindingProxy x:Key="proxy" Data="{Binding}" />
That line was correct the way you originally had it. It's the binding that's wrong. The proxy isn't itself a reference to the viewmodel. Its Data property is the reference to the viewmodel. You just neglected one tiny detail in the example: You need to include the source's Data property in the path to your viewmodel property:
<DataGridTemplateColumn
Header="{Binding Data.TimeColumnHeaderText, Source={StaticResource proxy}}"
Width="Auto"
IsReadOnly="True"
>

WPF dynamic DataGrid binding value from nested object from the collection to custom DataGridCell template

I've been spending more then half a day to get this task sorted and for a tree I cannot see the forest.
The aim is to display data (DataGrid) as several grids in sequence with dynamic count of columns, where each cell (not just column) can or cannot be editable with two way binding.
I would like to avoid using code behind approach and I believe xaml can offer me what I need. Other thing is mvvm injection.
Lets make it simple and do binding for one table first.
My first tough was to create DataTable but this on cannot be used for cell editable level. Then I created collection of collections of objects (for one table -> many rows -> many columns).
public class DataGridCell : BaseViewModel
{
public string Value
....
public bool IsEditable
....
}
Then I have another VM which represents one table (grid) which contains VM upon
public class DataGridItem : BaseViewModel
{
public string TableName
{
....
}
public ObservableCollection<ObservableCollection<DataGridCell>> Data
{
....
}
}
Then my xaml looks like this
<DataGrid ItemsSource="{Binding Path=Data}" AutoGenerateColumns="True">
<DataGrid.Resources>
<DataTemplate x:Key="ReadonlyCellTemplate">
<TextBlock Text="{Binding Value}" />
</DataTemplate>
<DataTemplate x:Key="EditableCellTemplate">
<TextBox Text="{Binding Value}" />
</DataTemplate>
</DataGrid.Resources>
<DataGridTemplateColumn CellTemplate="{StaticResource ReadonlyCellTemplate}">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ContentPresenter x:Name="Presenter" ContentTemplate="{StaticResource ReadonlyCellTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsEditable, PresentationTraceSources.TraceLevel=High}" Value="True">
<Setter TargetName="Presenter" Property="ContentTemplate" Value="{StaticResource EditableCellTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid>
The idea is to dynamically choose which cell will be filled in by data input controls and which not.
Problem is binding. I cannot figure out how to bind concrete cell element in the collection.
Thanks for any possible advice
I hope it's not too late but this is how I solved my issues with binding cells to dynamic data:
Problems binding to a the content of a WPF DataGridCell in XAML
(With code added as requested)

DataGridTextColumn header binding issue within a TabItem/TabControl

I seem to be having problems trying to bind the header column of a DataGridTextColumn. This code works fine when I have no TabControl/TabItem wrapping it but when I put it in the TabControl it can no longer find the DayHeader. I'd imagine it's a problem with the FindAncestor/AncestoryType but I'm not sure what to do to fix it or if that really is the issue. Any help would be appreciated.
<!--Not Working
<TabControl Margin="0,25,0,0" Background="{x:Null}">
<TabItem >
<Grid >
<DataGrid></DataGrid>
</Grid>
</TabItem>
<TabItem Header="Test Header">-->
<!--Working-->
<Grid>
<DataGrid ItemsSource="{Binding RunningViewSource.View}" Margin="0,27,0,-5" SelectionMode="Single">
<DataGrid.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizingStackPanel.VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</DataGrid.ItemsPanel>
<DataGrid.Columns>
<DataGridTextColumn Header="Contingencies" Binding="{Binding Contingencies}"
IsReadOnly="True" Width="400" />
<DataGridTextColumn Binding="{Binding Days[4]}" CellStyle="{StaticResource NumberCell}">
<DataGridTextColumn.Header>
<TextBlock Text="{Binding DataContext.DayHeader, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}">
</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
<!--Not Working
</TabItem>
</TabControl>-->
This is the message I get in the output window if I add the non working parts.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=DataContext.DayHeader; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
Edit: I can only reproduce this error when I have a TabControl with more than one TabItem. I've updated my code. Sorry for any confusion but adding the other TabItem is what makes the DayHeader unreachable.
Edit2: I know a lot of people use Snoop to help them with binding issues. Well when I inspect the column headers with Snoop, the text magically appears when I have them highlighted. I have no idea why this works so hopefully someone with more knowledge about Snoop will be able to help. Here is code behind for accessing the DayHeader and maybe that will help.
//Code Behind
private string dayHeader;
public string DayHeader
{
get { return dayHeader; }
set
{
dayHeader = value;
NotifyOfPropertyChange(() => DayHeader);
}
}
Thanks for any help.
DataGridTextColumn.Header is not in the visual tree,so it's not inherting DataContext.You can use Freezable class as it's shown in this article.

Categories