Display only two lines for a long text in a column - wpf

I have column in DataGrid to display a text message. Unfortunately it is too long. So I custom the cell column template in a textblock use TextWrapping = "Wrap"
It displays multiple lines. I don't want it. I just want to display the first two lines, at the end adding an ellipsis(...)
Is there a way to do that?

To achieve that you need to define a custom Behavior, first make sure to add the System.Windows.Interactivity namespace ( which is part of Expression.Blend.Sdk, use NuGet to install it: Install-Package Expression.Blend.Sdk), here a basic implementation (credit goes to #Itzalive):
public class NumLinesBehaviour : Behavior<TextBlock>
{
public static readonly DependencyProperty MaxLinesProperty =
DependencyProperty.RegisterAttached(
"MaxLines",
typeof(int),
typeof(NumLinesBehaviour),
new PropertyMetadata(default(int), OnMaxLinesPropertyChangedCallback));
public static void SetMaxLines(DependencyObject element, int value)
{
element.SetValue(MaxLinesProperty, value);
}
public static int GetMaxLines(DependencyObject element)
{
return (int)element.GetValue(MaxLinesProperty);
}
private static void OnMaxLinesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBlock element) element.MaxHeight = GetLineHeight(element) * GetMaxLines(element);
}
public static readonly DependencyProperty MinLinesProperty =
DependencyProperty.RegisterAttached(
"MinLines",
typeof(int),
typeof(NumLinesBehaviour),
new PropertyMetadata(default(int), OnMinLinesPropertyChangedCallback));
public static void SetMinLines(DependencyObject element, int value)
{
element.SetValue(MinLinesProperty, value);
}
public static int GetMinLines(DependencyObject element)
{
return (int)element.GetValue(MinLinesProperty);
}
private static void OnMinLinesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBlock element) element.MinHeight = GetLineHeight(element) * GetMinLines(element);
}
private static double GetLineHeight(TextBlock textBlock)
{
double lineHeight = textBlock.LineHeight;
if (double.IsNaN(lineHeight))
lineHeight = Math.Ceiling(textBlock.FontSize * textBlock.FontFamily.LineSpacing);
return lineHeight;
}
}
Now let's say you have a DataGrid Bound to an ObservableCollection of TestClass with the "Name" property, a basic use of the NumLinesBehaviour Behavior is as follow:
<Window ...
xmlns:local="clr-namespace:YourNameSpace"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<DataTemplate x:Key="CellTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock
Width="200"
TextWrapping="Wrap"
local:NumLinesBehaviour.MaxLines="2"
TextTrimming="WordEllipsis"
LineStackingStrategy="BlockLineHeight"
Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding DgCollection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" CellTemplate="{StaticResource CellTemplate}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Make sure to set the TextTrimming of the TextBlock to "WordEllipsis".
Update
The output looks something like that:

Related

How to display a image on mouse over in silverlight data grid

I need to display a image on mouse over only in silverlight 5.
Can any one please help me.
Give me any idea how to achieve it...
<sdk:DataGridTemplateColumn x:Name="colDeleteContent" IsReadOnly="True" Header="Delete Content" Width="100" CanUserResize="False">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel x:Name="spDeleteContent" VerticalAlignment="Center" Margin="10,0,0,0" Width="20" Height="20" HorizontalAlignment="Center" Orientation="Vertical">
<Image x:Name="imgDeleteContent" Source="Assets/Images/close.png" Height="15" Width="15" Margin="0" MouseLeftButtonDown="imgDeleteContent_MouseLeftButtonDown" Cursor="Hand"/>
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
Neon
There are many ways, OnFocus set your images Visibility Visible and on FocusLeft set Collapsed basically of your main element.
But I see that it is on DataTemplate on your sample.
So There are some ways I imagine.
1)Create a new component instead of element in DataTemplate such as
namespace ProjectBus
{
public class StackPanelHasHiddenImage : Control
{
//You may don't need dependency property
//It supports bindability
#region dependency property
public static Image GetMyProperty(DependencyObject obj)
{
return (Image)obj.GetValue(ImageProperty);
}
public static void SetMyProperty(DependencyObject obj, Image value)
{
obj.SetValue(ImageProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached("Image", typeof(Image), typeof(StackPanelHasHiddenImage), new System.Windows.PropertyMetadata(null));
#endregion
public Image Image
{
get;
set;
}
protected override void OnGotFocus(RoutedEventArgs e)
{
Image.Visibility = Visibility.Visible;
base.OnGotFocus(e);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
Image.Visibility = Visibility.Collapsed;
base.OnLostFocus(e);
}
}
}
Then in your xaml use like
<DataTemplate>
<local:StackPanelHasHiddenImage Image="/ProjectBus;component/blabal.png"/>
</DataTemplate>
2) Use GotoStateAction behaviour
http://msdn.microsoft.com/en-us/library/ff723953%28v=expression.40%29.aspx but I see that its in a DataTemplate and using this may not be easier.
3) MainElement.FinChildByType < StackPanel >().FirstOrDefault() is not null then add your focus and unfocus handler to this element on your codebehind. But this is a method I mostly avoid to use.
Its a bit harder because its in a template so your named object in template can't be seen on your codebehind.
Hope helps

Silverlight how to bind my usercontrol in Data Grid column template

today I getting crazy while trying to do, what I think, is simple thing.
I want to be able to create my usercontrol, and use it in my column template in my datagrid
I have searched and tried several combinations, and nothing appear to work
Can anyone help me?
public class User
{
public string Name { get; set; }
public bool IsValid { get; set; }
}
partial class MyControl : UserControl
{
private string _value;
public string Value
{
get { return _value; }
set { _value = value;
txt.Text = value;
}
}
public MyControl()
{
InitializeComponent();
}
}
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="txt" Text="[undefined]"></TextBlock>
</Grid>
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var items = new List<User>();
items.Add(new User{Name = "user 1", IsValid = true});
items.Add(new User { Name = "user 2", IsValid = false });
myGrid.ItemsSource = items;
}
}
<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid x:Name="myGrid" AutoGenerateColumns="False" IsReadOnly="True">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Header="Name">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<SilverlightApplication1:MyControl Value="{Binding Name}"></SilverlightApplication1:MyControl>
<!--<TextBlock Text="{Binding Name}"></TextBlock>-->
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
Edited:
I also tried the following, but I get no results on my grid:
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="txt" Text="{Binding Value}"></TextBlock>
</Grid>
public partial class MyControl : UserControl
{
public DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof(string),
typeof(MyControl),
new PropertyMetadata(OnValueChanged));
public string Value
{
get
{
return (string)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
NotifyPropertyChanged("Value");
}
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyControl) d).Value = (String)e.NewValue; //ERROR: here I got always empty string
}
public MyControl()
{
InitializeComponent();
}
}
The reason why your first code didn't work is simple. To be able to bind the "Value" property on your "MyControl" (Value={Binding Name}), it has to be a Dependency Property. which you fixed in your second bit of code.
Here's what I did (and that worked well):
<UserControl x:Class="BusinessApplication8_SOF_Sandbox.Controls.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" Name="myControl">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Name="textBlock" Text="{Binding Value, ElementName=myControl}"/>
</Grid>
</UserControl>
For the rest, I used your code.
Another possibility, which should be OK in case you only want the data to flow in one direction ("One Way" from source to target), as it is the case when using the TextBlock control is to update the Text property in the "OnValueChanged". here's the code for the Value property:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(MyControl),
new PropertyMetadata("", OnValueChanged));
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (MyControl)d;
var oldValue = (string)e.OldValue;
var newValue = target.Value;
target.OnValueChanged(oldValue, newValue);
}
protected virtual void OnValueChanged(string oldValue, string newValue)
{
textBlock.Text = newValue;
}
and you can remove the binding in xaml:
<TextBlock Name="textBlock" />
this worked for me as well.
Hope this helps ;)
you need to implement the INotifyPropertyChanged interface in your User class so that bound user controls are aware if any of the bound properties change. See the following page with the details how to implement it : http://www.silverlightshow.net/tips/How-to-implement-INotifyPropertyChanged-interface.aspx
As you can see you need to implement the interface and in the setters raise the event OnPropertyChanged
Then it should work with your bindings.
Best,
Tim

How can I execute a command binding on MouseEnter of a StackPanel in WPF?

I'm using MVVM.
<ItemsControl ItemsSource="{Binding AllIcons}" Tag="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label HorizontalAlignment="Right">x</Label>
<Image Source="{Binding Source}" Height="100" Width="100" />
<Label HorizontalAlignment="Center" Content="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
That looks fine. If I put a button in the stack panel using this command:
<Button Command="{Binding Path=DataContext.InvasionCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding}"/>
I'm able to capture the command. However, I want to execute the command binding when the mouse enters the stack panel, not when I click a button.
Any idea?
My wrong, input bindings does not solve the problem. You may use attached properties for this:
public static class MouseEnterCommandBinding
{
public static readonly DependencyProperty MouseEnterCommandProperty = DependencyProperty.RegisterAttached(
"MouseEnterCommand",
typeof(ICommand),
typeof(MouseEnterCommandBinding),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetMouseEnterCommand(UIElement element, ICommand value)
{
element.SetValue(MouseEnterCommandProperty, value);
element.MouseEnter += (s,e) =>
{
var uiElement = s as UIElement;
var command = GetMouseEnterCommand(uiElement);
if (command != null && command.CanExecute(uiElement.CommandParameter))
command.Execute(uiElement.CommandParameter);
}
}
public static ICommand GetMouseEnterCommand(UIElement element)
{
return element.GetValue(MouseEnterCommandProperty) as ICommand;
}
}
First you need to declare a behavior for mouse enter. This basically translates the event into a command in your ViewModel.
public static class MouseEnterBehavior
{
public static readonly DependencyProperty MouseEnterProperty =
DependencyProperty.RegisterAttached("MouseEnter",
typeof(ICommand),
typeof(MouseEnterBehavior),
new PropertyMetadata(null, MouseEnterChanged));
public static ICommand GetMouseEnter(DependencyObject obj)
{
return (ICommand)obj.GetValue(MouseEnterProperty);
}
public static void SetMouseEnter(DependencyObject obj, ICommand value)
{
obj.SetValue(MouseEnterProperty, value);
}
private static void MouseEnterChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
UIElement uiElement = obj as UIElement;
if (uiElement != null)
uiElement.MouseEnter += new MouseEventHandler(uiElement_MouseEnter);
}
static void uiElement_MouseEnter(object sender, MouseEventArgs e)
{
UIElement uiElement = sender as UIElement;
if (uiElement != null)
{
ICommand command = GetMouseEnter(uiElement);
command.Execute(uiElement);
}
}
}
Then you just need to create that command in your view model and reference it in the view. The behaviors: namespace should just point to wherever you created that behavior. I use this pattern every time I need to translate an event into a command in a view model.
<Grid>
<StackPanel behaviors:MouseEnterBehavior.MouseEnter="{Binding MouseEnteredCommand}"
Height="150"
Width="150"
Background="Red">
</StackPanel>
</Grid>
You probably need to use InputBindings: http://msdn.microsoft.com/en-us/library/system.windows.input.inputbinding.aspx

Binding WPF DataGrid to DataTable using TemplateColumns

I have tried everything and got nowhere so I'm hoping someone can give me the aha moment.
I simply cannot get the binding to pull the data in the datagrid successfully.
I have a DataTable that contains multiple columns with of MyDataType
public class MyData
{
string nameData {get;set;}
bool showData {get;set;}
}
MyDataType has 2 properties (A string, a boolean)
I have created a test DataTable
DataTable GetDummyData()
{
DataTable dt = new DataTable("Foo");
dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData)));
dt.Rows.Add(new MyData("Row1C1", true));
dt.Rows.Add(new MyData("Row2C1", false));
dt.AcceptChanges();
return dt;
}
I have a WPF DataGrid which I want to show my DataTable.
But all I want to do is to change how each cell is rendered to show [TextBlock][Button] per cell with values bound to the MyData object and this is where I'm having a tonne of trouble.
My XAML looks like this
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyDataTemplate" DataType="MyData">
<StackPanel Orientation="Horizontal" >
<Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="{Binding Path=nameData}"></Button>
<TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="{Binding Path=nameData}"></TextBlock>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<dg:DataGrid Grid.Row="1" ItemsSource="{Binding}" AutoGenerateColumns="True"
x:Name="dataGrid1" SelectionMode="Single" CanUserAddRows="False"
CanUserSortColumns="true" CanUserDeleteRows="False" AlternatingRowBackground="AliceBlue"
AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn" />
</Grid>
Now all I do once loaded is to attempt to bind the DataTable to the WPF DataGrid
dt = GetDummyData();
dataGrid1.ItemsSource = dt.DefaultView;
The TextBlock and Button show up, but they don't bind, which leaves them blank.
Could anyone let me know if they have any idea how to fix this.
This should be simple, thats what Microsoft leads us to believe.
I have set the Column.CellTemplate during the AutoGenerating event and still get no binding.
Please help!!!
Edit: Updated to reflect the input of Aran Mulholland (see comment)
Apparently the DataGrid is passing the entire DataRowView to each cell. That's why the binding doesn't work. Your DataTemplate expects the DataContext to be of type MyData, but instead it is of type DataRowView. My proposed (somewhat hack-ish) workaround to get the DataContext you want is to create a custom DataGridTemplateColumn that will extract the necessary item from the DataRowView. The code is below:
<Window x:Class="DataGridTemplateColumnSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyDataTemplate" DataType="DataRowView">
<StackPanel Orientation="Horizontal">
<Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="{Binding Path=nameData}"></Button>
<TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="{Binding Path=nameData}"></TextBlock>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<dg:DataGrid Grid.Row="1" AutoGenerateColumns="True" x:Name="dataGrid1" SelectionMode="Single"
CanUserAddRows="False" CanUserSortColumns="true" CanUserDeleteRows="False"
AlternatingRowBackground="AliceBlue" AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn"
ItemsSource="{Binding}" VirtualizingStackPanel.VirtualizationMode="Standard" />
</Grid>
</Window>
using System.Data;
using System.Windows;
using Microsoft.Windows.Controls;
namespace DataGridTemplateColumnSample
{
public partial class Window1
{
public Window1()
{
InitializeComponent();
DataContext = GetDummyData().DefaultView;
}
private static DataTable GetDummyData()
{
var dt = new DataTable("Foo");
dt.Columns.Add(new DataColumn("OneColumn", typeof(MyData)));
dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData)));
dt.Rows.Add(new MyData("Row1C1", true), new MyData("Row1C2", true));
dt.Rows.Add(new MyData("Row2C1", false), new MyData("Row2C2", true));
dt.AcceptChanges();
return dt;
}
private void dataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var column = new DataRowColumn(e.PropertyName);
column.Header = e.Column.Header;
column.CellTemplate = (DataTemplate)Resources["MyDataTemplate"];
e.Column = column;
}
}
public class DataRowColumn : DataGridTemplateColumn
{
public DataRowColumn(string column) { ColumnName = column; }
public string ColumnName { get; private set; }
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
var row = (DataRowView) dataItem;
var item = row[ColumnName];
cell.DataContext = item;
var element = base.GenerateElement(cell, item);
return element;
}
}
public class MyData
{
public MyData(string name, bool data) { nameData = name; showData = data; }
public string nameData { get; set; }
public bool showData { get; set; }
}
}
Note: This approach only appears to work with container virtualization off or in Standard mode. If the VirtualizationMode is set to Recycling the template is not applied.
After finding this thread and having trouble with the code shown here, I ran across this thread on MSDN, and it works much better! No virtualization problems at all so far as I've seen.
http://social.msdn.microsoft.com/Forums/en/wpf/thread/8b2e94b7-3c44-4642-8acc-851de5285062
Code:
private void dataGrid1_AutoGeneratingColumn(object sender, Microsoft.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyType == typeof(MyData))
{
MyDataGridTemplateColumn col = new MyDataGridTemplateColumn();
col.ColumnName = e.PropertyName; // so it knows from which column to get MyData
col.CellTemplate = (DataTemplate)FindResource("MyDataTemplate");
e.Column = col;
e.Column.Header = e.PropertyName;
}
}
public class MyDataGridTemplateColumn : DataGridTemplateColumn
{
public string ColumnName
{
get;
set;
}
protected override System.Windows.FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
// The DataGridTemplateColumn uses ContentPresenter with your DataTemplate.
ContentPresenter cp = (ContentPresenter)base.GenerateElement(cell, dataItem);
// Reset the Binding to the specific column. The default binding is to the DataRowView.
BindingOperations.SetBinding(cp, ContentPresenter.ContentProperty, new Binding(this.ColumnName));
return cp;
}
}

Element Binding Silverlight 3

I trying to use Element Binding in Silverlight 3 to SelectedItem of ComboBox in ToolTipService.ToolTip.
This code works:
<ComboBox x:Name="cboSource" DisplayMemberPath="Name" ToolTipService.ToolTip="{Binding ElementName=cboSource, Path=SelectedItem.Name}" Width="180" />
but this code doesn't:
<ComboBox x:Name="cboSource" DisplayMemberPath="Name" Width="180" >
<ToolTipService.ToolTip>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ElementName=cboSource, Path=SelectedItem.Code}" Margin="0,0,5,0"/>
<TextBlock Text="-" Margin="0,0,5,0"/>
<TextBlock Text="{Binding ElementName=cboSource, Path=SelectedItem.Name}"/>
</StackPanel>
</ToolTipService.ToolTip>
</ComboBox>
Name and Code are properties of item in cboSource.ItemsSource.
In first code, the Name is correctly displayed in combo's tooltip but in second code tooltip is " - ".
Any ideas ?
Ahh...fun with tooltips.
The ToolTipService is actually "rooted" at the base of the tree (if you have Mole, you can double check to verify this) - hence, it does not get it's DataContext propagated down from parent elements.
I've done hacky things to fix this behavior in the past, but they all boil down to "Code up an attached property that accepts a DataContext and forwards it along to the attached element".
Best of luck - this thing has stung me a couple of times. :)
Ooh, found a link for you: http://www.codeproject.com/Articles/36078/Silverlight-2-0-How-to-use-a-DataBinding-with-the-ToolTipService.aspx
EDIT: Try this out:
<ComboBox x:Name="cboSource" DisplayMemberPath="Name" Width="180">
<local:DataBindingTooltip.TooltipDataContext>
<Binding ElementName="cboSource"/>
</local:DataBindingTooltip.TooltipDataContext>
<local:DataBindingTooltip.Tooltip>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=SelectedItem.Code}" Margin="0,0,5,0"/>
<TextBlock Text="-" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Path=SelectedItem.Name}"/>
</StackPanel>
</local:DataBindingTooltip.Tooltip>
</ComboBox>
With the following class:
public class DataBindingTooltip
{
public static readonly DependencyProperty TooltipDataContextProperty =
DependencyProperty.RegisterAttached(
"TooltipDataContext",
typeof (object),
typeof (DataBindingTooltip),
null);
public static readonly DependencyProperty TooltipProperty =
DependencyProperty.RegisterAttached(
"Tooltip",
typeof(object),
typeof(DataBindingTooltip),
new PropertyMetadata(TooltipChanged));
public static void SetTooltip(DependencyObject d, object value)
{
d.SetValue(TooltipProperty, value);
}
public static object GetTooltip(DependencyObject d)
{
return d.GetValue(TooltipProperty);
}
public static void SetTooltipDataContext(DependencyObject d, object value)
{
d.SetValue(TooltipDataContextProperty, value);
}
public static object GetTooltipDataContext(DependencyObject d)
{
return d.GetValue(TooltipDataContextProperty);
}
private static void TooltipChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
if (sender is FrameworkElement)
{
var element = sender as FrameworkElement;
element.Loaded += ElementLoaded;
}
}
static void ElementLoaded(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement)
{
var element = sender as FrameworkElement;
element.Loaded -= ElementLoaded;
var tooltip = element.GetValue(TooltipProperty) as DependencyObject;
if (tooltip != null)
{
if (GetTooltipDataContext(element) != null)
{
tooltip.SetValue(FrameworkElement.DataContextProperty,
element.GetValue(TooltipDataContextProperty));
}
else
{
tooltip.SetValue(FrameworkElement.DataContextProperty,
element.GetValue(FrameworkElement.DataContextProperty));
}
}
ToolTipService.SetToolTip(element, tooltip);
}
}
}
A very simple way could be to define an additional property in the source object something
whenever a user hovers the mouse over the control, the concatenated string will be shown as a nice simple tooltip.
like this:
using System...
....
public Class Employee
{
public string Forenames {get;set;}
public string Surname {get;set;}
public string Address {get;set;}
private string tooltip;
public string Tooltip
{
get{return tooltip;}
set
{
value=Forenames + " " + Surname + "," Address ;
}
}
//... other methods to follow
}
XAML MyPage.cs code has following
public partial Class MyPage : Page
{
Public List<Employee> Employees{get;set;}
public MyPage()
{
InitiazeComponents();
Employees = new List<Employee>(); // initialise
Employees=GetEmployees();
}
public List<Employee> GetEmployees(){
..
Write code that ..returns
..
}
.. other code to follow..
}
Now in MyPage.xaml
...
<ComboBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="8,4,0,0" Name="cboCostCentreInvestor" ItemsSource="{Binding Employees}" ToolTipService.ToolTip="{Binding ElementName=cboCostCentreInvestor,Path=SelectedItem.Tooltip}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Style="{StaticResource stackPanelComboboxItemStyle}">
<TextBlock Text="{Binding Forenames}" Style="{StaticResource textBlockComboboxItem}" />
<TextBlock Text="{Binding Surname}" Style="{StaticResource textBlockComboboxItem}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Resources