The previous post has shown how to create a ToolTip with hyperlink in WPF.
<DataGridTextColumn Binding="{Binding CapitalGainsTaxPercentage, UpdateSourceTrigger=LostFocus, StringFormat={StaticResource PercentageFormat}, ConverterCulture={x:Static SystemGlobalization:CultureInfo.DefaultThreadCurrentCulture}, TargetNullValue=''}" ElementStyle="{StaticResource ItalicTextAlignmentRightElementStyle}"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <hhc:TextBlockWithToolTipWithHyperlink Command="{Binding DataContext.MainWindowViewModel.OpenWebPageCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" HyperlinkUrl="https://scutumsoft.blogspot.com/2024/10/tax-for-unrealized-capital-gains.html" Text="{x:Static r:Resource.CapitalGainsTaxPercentage}" /> </DataTemplate> </DataGridTextColumn.HeaderTemplate>
This was good but not perfect. It would be nice if I could provide the new properties at the column level and forget about the HeaderTemplate. This needs a custom DataGridTextColumn.
public class DataGridTextColumnWithHelpLink : DataGridTextColumn { public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( nameof(Command), typeof(ICommand), typeof(DataGridTextColumnWithHelpLink)); public static readonly DependencyProperty HyperlinkUrlProperty = DependencyProperty.Register( nameof(HyperlinkUrl), typeof(string), typeof(DataGridTextColumnWithHelpLink)); public ICommand Command { get => (ICommand)GetValue(CommandProperty); set => SetValue(CommandProperty, value); } public string HyperlinkUrl { get => (string)GetValue(HyperlinkUrlProperty); set => SetValue(HyperlinkUrlProperty, value); } public DataGridTextColumnWithHelpLink() { HeaderTemplate = CreateHeaderTemplate(); } private DataTemplate CreateHeaderTemplate() { // In DataTemplate you have to create factory instead of creating the object directly var textBlockWithToolTipWithHyperlinkFactory = new FrameworkElementFactory( typeof(TextBlockWithToolTipWithHyperlink)); textBlockWithToolTipWithHyperlinkFactory.SetBinding(TextBlockWithToolTipWithHyperlink.CommandProperty, new Binding(nameof(Command)) { //RelativeSource = new RelativeSource(RelativeSourceMode.Self) Source = this}); textBlockWithToolTipWithHyperlinkFactory.SetBinding(TextBlockWithToolTipWithHyperlink.HyperlinkUrlProperty, new Binding(nameof(HyperlinkUrl)) { Source = this }); textBlockWithToolTipWithHyperlinkFactory.SetBinding(TextBlockWithToolTipWithHyperlink.TextProperty, new Binding(nameof(Header)) { Source = this }); // If StackPanel is used then the wrap setting is not working var wrapPanelFactory = new FrameworkElementFactory(typeof(WrapPanel)); wrapPanelFactory.AppendChild(textBlockWithToolTipWithHyperlinkFactory); var dataTemplate = new DataTemplate(typeof(DataGridTextColumnWithHelpLink)) { VisualTree = wrapPanelFactory }; return dataTemplate; } }
No xaml file is need just a cs file. The new class DataGridTextColumnWithHelpLink is a child of DataGridTextColumn. Two properties are defined, the third one, Text, is not needed because the Header property can be used instead.
The aim is to create the DataTemplate from code. To do that we need its components, a TextBlockWithToolTipWithHyperlink and a WrapPanel to hold it. Notice that we cannot create the objects directly but the factories of the objects because we are in a standalone DataTemplate. We have to bind the Header property of the DataGridTextColumnWithHelpLink to Text property of the TextBlockWithToolTipWithHyperlink. We have to manage the other two bindings also.
There was a problem with the Binding of the Command. We got a "UserControl is not found" error. The reason is that a standalone DataTemplate may be created before the main control is created. The DataContextProxy is a workaround for this issue.
public class DataContextProxy : Freezable { protected override Freezable CreateInstanceCore() => new DataContextProxy(); public object Data { get { return GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(DataContextProxy), new UIPropertyMetadata(null)); }
This class can contain an object in the Data property we will store the DataContext there. "Binding" of "Binding ." means binding the DataCotnext.
<UserControl.Resources> <hhd:DataContextProxy x:Key="proxy" Data="{Binding}" /> </UserControl.Resources>
Now it is easy to create a column with a ToolTip which contains a hyperlink. The binding works as expected.
<hhc:DataGridTextColumnWithHelpLink Binding="{Binding CapitalGainsTaxPercentage, UpdateSourceTrigger=LostFocus, StringFormat={StaticResource PercentageFormat}, ConverterCulture={x:Static SystemGlobalization:CultureInfo.DefaultThreadCurrentCulture}, TargetNullValue=''}" Command="{Binding Data.MainWindowViewModel.OpenWebPageCommand, Source={StaticResource DataContextProxy}}" ElementStyle="{StaticResource ItalicTextAlignmentRightElementStyle}" Header="{x:Static r:Resource.CapitalGainsTaxPercentage}" HyperlinkUrl="https://scutumsoft.blogspot.com/2024/10/tax-for-unrealized-capital-gains.html">
This required 5 hours to figure out, we will see if it was worth it.
Tags:
C#